@openclaw/msteams 2026.7.1-beta.6 → 2026.7.2-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/api.cjs +8 -0
  2. package/dist/{channel-Cf5LJ4aO.js → channel-BSyPdpGN.cjs} +641 -227
  3. package/dist/channel-config-api.cjs +3 -0
  4. package/dist/channel-plugin-api.cjs +3 -0
  5. package/dist/channel.runtime-DQTOzzet.cjs +1372 -0
  6. package/dist/{config-schema-BL4qQZiA.js → config-schema-BSNLJUta.cjs} +13 -3
  7. package/dist/{directory-contract-api.js → directory-contract-api.cjs} +8 -7
  8. package/dist/{doctor-contract-api.js → doctor-contract-api.cjs} +73 -57
  9. package/dist/index.cjs +21 -0
  10. package/dist/{probe-BnyZRZsD.js → messenger-DtS9gqkV.cjs} +369 -861
  11. package/dist/{oauth-BifWxWzq.js → oauth-C5E5powI.cjs} +21 -21
  12. package/dist/{polls-B_hs4aKm.js → polls-BhoI4VaN.cjs} +188 -38
  13. package/dist/{resolve-allowlist-Dnw4aNOu.js → resolve-allowlist-CyQ57MfH.cjs} +817 -140
  14. package/dist/rolldown-runtime-D6vf50IK.cjs +28 -0
  15. package/dist/runtime-api.cjs +207 -0
  16. package/dist/runtime-gZTAuFux.cjs +24 -0
  17. package/dist/secret-contract-api.cjs +31 -0
  18. package/dist/setup-entry.cjs +14 -0
  19. package/dist/setup-plugin-api.cjs +65 -0
  20. package/dist/{setup-surface-qW-ECHL-.js → setup-surface-BDTPN3dX.cjs} +123 -49
  21. package/dist/{src-DZFs5XtZ.js → src-CUMz2B6f.cjs} +672 -740
  22. package/dist/{sso-token-store-DrVbnCUF.js → sso-token-store-CktCz88k.cjs} +51 -10
  23. package/npm-shrinkwrap.json +3 -3
  24. package/openclaw.plugin.json +10 -31
  25. package/package.json +8 -7
  26. package/dist/api.js +0 -3
  27. package/dist/channel-config-api.js +0 -2
  28. package/dist/channel-plugin-api.js +0 -2
  29. package/dist/channel.runtime-Bwu6Tjhf.js +0 -664
  30. package/dist/index.js +0 -22
  31. package/dist/runtime-BS5AZrKK.js +0 -8
  32. package/dist/runtime-api.js +0 -20
  33. package/dist/secret-contract-api.js +0 -35
  34. package/dist/setup-entry.js +0 -15
  35. package/dist/setup-plugin-api.js +0 -64
@@ -1,35 +1,45 @@
1
- import { n as getOptionalMSTeamsRuntime, t as getMSTeamsRuntime } from "./runtime-BS5AZrKK.js";
2
- import { DEFAULT_ACCOUNT_ID, DEFAULT_WEBHOOK_MAX_BODY_BYTES, buildMediaPayload, createChannelMessageReplyPipeline, createChannelPairingController, dispatchReplyFromConfigWithSettledDispatcher as dispatchReplyFromConfigWithSettledDispatcher$1, isDangerousNameMatchingEnabled, keepHttpServerTaskAlive, logTypingFailure, mergeAllowlist, resolveChannelMediaMaxBytes, resolveDefaultGroupPolicy, summarizeMapping } from "./runtime-api.js";
3
- import { $ as isDownloadableAttachment, C as resolveMSTeamsCredentials, F as loadMSTeamsSdkWithAuth, G as ATTACHMENT_TAG_RE, J as applyAuthorizationHeaderForUrl, K as GRAPH_ROOT, L as ensureUserAgentHeader, N as createMSTeamsExpressAdapter, P as createMSTeamsTokenProvider, Q as inferPlaceholder, U as resolveMSTeamsSdkCloudOptions, V as tryNormalizeBotFrameworkServiceUrl, X as extractHtmlFromAttachment, Y as encodeGraphShareId, Z as extractInlineImageCandidates, at as resolveAttachmentFetchPolicy, c as resolveMSTeamsUserAllowlist, ct as safeFetchWithPolicy, et as isLikelyImageAttachment, it as readNestedString, lt as safeHostForUrl, nt as isUrlAllowed, ot as resolveMediaSsrfPolicy, p as fetchGraphJson, q as IMG_SRC_RE, rt as normalizeContentType, s as resolveMSTeamsChannelAllowlist, st as resolveRequestUrl, tt as isRecord$1, ut as tryBuildGraphSharesUrlForSharedLink } from "./resolve-allowlist-Dnw4aNOu.js";
4
- import { a as normalizeMSTeamsConversationId, c as translateMSTeamsDmConversationIdForGraph, f as resolveMSTeamsAllowlistMatch, i as extractMSTeamsQuoteInfo, l as wasMSTeamsBotMentioned, m as resolveMSTeamsRouteConfig, n as resolveMSTeamsRouteSessionKey, o as parseMSTeamsActivityTimestamp, p as resolveMSTeamsReplyPolicy, r as extractMSTeamsConversationMessageId, s as stripMSTeamsMentionTags } from "./channel-Cf5LJ4aO.js";
5
- import { a as formatUnknownError, i as formatMSTeamsSendErrorHint, r as classifyMSTeamsSendError } from "./setup-surface-qW-ECHL-.js";
6
- import { l as createMSTeamsPollStoreState, u as extractMSTeamsPollVote, v as createMSTeamsConversationStoreState } from "./polls-B_hs4aKm.js";
7
- import { i as createMSTeamsSsoTokenStoreFs } from "./sso-token-store-DrVbnCUF.js";
8
- import { _ as buildFileInfoCard, c as renderReplyPayloadsToMessages, d as withRevokedProxyFallback, f as resolveGraphChatId, g as removePendingUploadFs, h as getPendingUploadFs, l as sendMSTeamsMessages, m as removePendingUpload, p as getPendingUpload, s as buildConversationReference, u as sendMSTeamsActivityWithReference, v as parseFileConsentInvoke, y as uploadToConsentUrl } from "./probe-BnyZRZsD.js";
9
- import { formatAllowlistMatchMeta } from "openclaw/plugin-sdk/allow-from";
10
- import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, normalizeChannelProgressDraftLineIdentity, resolveChannelPreviewStreamMode, resolveChannelProgressDraftMaxLines, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
11
- import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
12
- import { parseMediaContentLength, saveResponseMedia } from "openclaw/plugin-sdk/media-runtime";
13
- import { buildChannelInboundEventContext, dispatchReplyFromConfigWithSettledDispatcher, formatInboundMediaUnavailableText, hasFinalInboundReplyDispatch, logInboundDrop, resolveInboundMentionDecision, resolveInboundReplyDispatchCounts, resolveInboundSessionEnvelopeContext } from "openclaw/plugin-sdk/channel-inbound";
14
- import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
15
- import { readProviderJsonArrayFieldResponse, readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
16
- import path from "node:path";
17
- import { asDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
18
- import { appendRegularFile } from "openclaw/plugin-sdk/security-runtime";
19
- import { resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
20
- import crypto from "node:crypto";
21
- import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
22
- import { channelIngressRoutes, resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
23
- import { filterSupplementalContextItems, resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
24
- import { DEFAULT_GROUP_HISTORY_LIMIT, createChannelHistoryWindow } from "openclaw/plugin-sdk/reply-history";
25
- import { createPersistentDedupeCache } from "openclaw/plugin-sdk/dedupe-runtime";
1
+ const require_rolldown_runtime = require("./rolldown-runtime-D6vf50IK.cjs");
2
+ const require_runtime = require("./runtime-gZTAuFux.cjs");
3
+ require("./runtime-api.cjs");
4
+ const require_resolve_allowlist = require("./resolve-allowlist-CyQ57MfH.cjs");
5
+ const require_channel = require("./channel-BSyPdpGN.cjs");
6
+ const require_setup_surface = require("./setup-surface-BDTPN3dX.cjs");
7
+ const require_polls = require("./polls-BhoI4VaN.cjs");
8
+ const require_sso_token_store = require("./sso-token-store-CktCz88k.cjs");
9
+ const require_messenger = require("./messenger-DtS9gqkV.cjs");
10
+ let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
11
+ let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
12
+ let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
13
+ let openclaw_plugin_sdk_media_runtime = require("openclaw/plugin-sdk/media-runtime");
14
+ let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
15
+ let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
16
+ let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-http");
17
+ let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-runtime");
18
+ let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text-utility-runtime");
19
+ let node_path = require("node:path");
20
+ node_path = require_rolldown_runtime.__toESM(node_path, 1);
21
+ let openclaw_plugin_sdk_security_runtime = require("openclaw/plugin-sdk/security-runtime");
22
+ let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
23
+ let node_crypto = require("node:crypto");
24
+ node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
25
+ let openclaw_plugin_sdk_channel_ingress_runtime = require("openclaw/plugin-sdk/channel-ingress-runtime");
26
+ let openclaw_plugin_sdk_context_visibility_runtime = require("openclaw/plugin-sdk/context-visibility-runtime");
27
+ let openclaw_plugin_sdk_reply_history = require("openclaw/plugin-sdk/reply-history");
28
+ let openclaw_plugin_sdk_dedupe_runtime = require("openclaw/plugin-sdk/dedupe-runtime");
29
+ let openclaw_plugin_sdk_collection_runtime = require("openclaw/plugin-sdk/collection-runtime");
30
+ let openclaw_plugin_sdk_channel_pairing = require("openclaw/plugin-sdk/channel-pairing");
31
+ let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
32
+ let openclaw_plugin_sdk_runtime_group_policy = require("openclaw/plugin-sdk/runtime-group-policy");
33
+ let openclaw_plugin_sdk_dangerous_name_runtime = require("openclaw/plugin-sdk/dangerous-name-runtime");
34
+ let openclaw_plugin_sdk_reply_payload = require("openclaw/plugin-sdk/reply-payload");
35
+ let openclaw_plugin_sdk_webhook_ingress = require("openclaw/plugin-sdk/webhook-ingress");
26
36
  //#region extensions/msteams/src/feedback-reflection-prompt.ts
27
37
  /** Max chars of the thumbed-down response to include in the reflection prompt. */
28
38
  const MAX_RESPONSE_CHARS = 500;
29
39
  function buildReflectionPrompt(params) {
30
40
  const parts = ["A user indicated your previous response wasn't helpful."];
31
41
  if (params.thumbedDownResponse) {
32
- const truncated = params.thumbedDownResponse.length > MAX_RESPONSE_CHARS ? `${truncateUtf16Safe(params.thumbedDownResponse, MAX_RESPONSE_CHARS)}...` : params.thumbedDownResponse;
42
+ const truncated = params.thumbedDownResponse.length > MAX_RESPONSE_CHARS ? `${(0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(params.thumbedDownResponse, MAX_RESPONSE_CHARS)}...` : params.thumbedDownResponse;
33
43
  parts.push(`\nYour response was:\n> ${truncated}`);
34
44
  }
35
45
  if (params.userComment) parts.push(`\nUser's comment: "${params.userComment}"`);
@@ -39,7 +49,7 @@ function buildReflectionPrompt(params) {
39
49
  function parseBooleanLike(value) {
40
50
  if (typeof value === "boolean") return value;
41
51
  if (typeof value === "string") {
42
- const normalized = normalizeOptionalLowercaseString(value);
52
+ const normalized = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(value);
43
53
  if (normalized === "true" || normalized === "yes") return true;
44
54
  if (normalized === "false" || normalized === "no") return false;
45
55
  }
@@ -79,10 +89,10 @@ const MAX_COOLDOWN_ENTRIES = 500;
79
89
  const LEARNINGS_NAMESPACE = "feedback-learnings";
80
90
  const MAX_LEARNING_ENTRIES = 1e4;
81
91
  function learningStoreKey(storePath, sessionKey) {
82
- return crypto.createHash("sha256").update(`${storePath}\0${sessionKey}`, "utf8").digest("hex");
92
+ return node_crypto.default.createHash("sha256").update(`${storePath}\0${sessionKey}`, "utf8").digest("hex");
83
93
  }
84
94
  function openLearningStore() {
85
- return getMSTeamsRuntime().state.openKeyedStore({
95
+ return require_runtime.getMSTeamsRuntime().state.openKeyedStore({
86
96
  namespace: LEARNINGS_NAMESPACE,
87
97
  maxEntries: MAX_LEARNING_ENTRIES
88
98
  });
@@ -134,7 +144,7 @@ function buildFeedbackEvent(params) {
134
144
  };
135
145
  }
136
146
  function buildReflectionContext(params) {
137
- const core = getMSTeamsRuntime();
147
+ const core = require_runtime.getMSTeamsRuntime();
138
148
  const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(params.cfg);
139
149
  const body = core.channel.reply.formatAgentEnvelope({
140
150
  channel: "Teams",
@@ -163,7 +173,7 @@ function buildReflectionContext(params) {
163
173
  }) };
164
174
  }
165
175
  function createReflectionCaptureDispatcher(params) {
166
- const core = getMSTeamsRuntime();
176
+ const core = require_runtime.getMSTeamsRuntime();
167
177
  let response = "";
168
178
  const { dispatcher, replyOptions } = core.channel.reply.createReplyDispatcherWithTyping({
169
179
  deliver: async (payload) => {
@@ -176,7 +186,7 @@ function createReflectionCaptureDispatcher(params) {
176
186
  },
177
187
  humanDelay: core.channel.reply.resolveHumanDelayConfig(params.cfg, params.agentId),
178
188
  onError: (err) => {
179
- params.log.debug?.("reflection reply error", { error: formatUnknownError(err) });
189
+ params.log.debug?.("reflection reply error", { error: require_setup_surface.formatUnknownError(err) });
180
190
  }
181
191
  });
182
192
  return {
@@ -186,11 +196,11 @@ function createReflectionCaptureDispatcher(params) {
186
196
  };
187
197
  }
188
198
  async function sendReflectionFollowUp(params) {
189
- const baseRef = buildConversationReference(params.conversationRef);
190
- await sendMSTeamsActivityWithReference(params.app, baseRef, {
199
+ const baseRef = require_messenger.buildConversationReference(params.conversationRef);
200
+ await require_messenger.sendMSTeamsActivityWithReference(params.app, baseRef, {
191
201
  type: "message",
192
202
  text: params.userMessage
193
- }, { serviceUrlBoundary: resolveMSTeamsSdkCloudOptions(params.cfg.channels?.msteams) });
203
+ }, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(params.cfg.channels?.msteams) });
194
204
  }
195
205
  /**
196
206
  * Run a background reflection after negative feedback.
@@ -207,7 +217,7 @@ async function runFeedbackReflection(params) {
207
217
  thumbedDownResponse: params.thumbedDownResponse,
208
218
  userComment: params.userComment
209
219
  });
210
- const storePath = getMSTeamsRuntime().channel.session.resolveStorePath(cfg.session?.store, { agentId: params.agentId });
220
+ const storePath = require_runtime.getMSTeamsRuntime().channel.session.resolveStorePath(cfg.session?.store, { agentId: params.agentId });
211
221
  const { ctxPayload } = buildReflectionContext({
212
222
  cfg,
213
223
  conversationId: params.conversationId,
@@ -220,7 +230,7 @@ async function runFeedbackReflection(params) {
220
230
  log
221
231
  });
222
232
  try {
223
- await dispatchReplyFromConfigWithSettledDispatcher$1({
233
+ await (0, openclaw_plugin_sdk_channel_inbound.dispatchReplyFromConfigWithSettledDispatcher)({
224
234
  ctxPayload,
225
235
  cfg,
226
236
  dispatcher: capture.dispatcher,
@@ -228,7 +238,7 @@ async function runFeedbackReflection(params) {
228
238
  replyOptions: capture.replyOptions
229
239
  });
230
240
  } catch (err) {
231
- log.error("reflection dispatch failed", { error: formatUnknownError(err) });
241
+ log.error("reflection dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
232
242
  return;
233
243
  }
234
244
  const reflectionResponse = capture.readResponse().trim();
@@ -254,9 +264,9 @@ async function runFeedbackReflection(params) {
254
264
  learning: parsedReflection.learning
255
265
  });
256
266
  } catch (err) {
257
- log.debug?.("failed to store reflection learning", { error: formatUnknownError(err) });
267
+ log.debug?.("failed to store reflection learning", { error: require_setup_surface.formatUnknownError(err) });
258
268
  }
259
- const conversationType = normalizeOptionalLowercaseString(params.conversationRef.conversation?.conversationType);
269
+ const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationRef.conversation?.conversationType);
260
270
  if (!(conversationType === "personal" && parsedReflection.followUp && Boolean(parsedReflection.userMessage))) {
261
271
  if (parsedReflection.followUp && conversationType !== "personal") log.debug?.("skipping reflection follow-up outside direct message", {
262
272
  sessionKey,
@@ -273,22 +283,22 @@ async function runFeedbackReflection(params) {
273
283
  });
274
284
  log.info("sent reflection follow-up", { sessionKey });
275
285
  } catch (err) {
276
- log.debug?.("failed to send reflection follow-up", { error: formatUnknownError(err) });
286
+ log.debug?.("failed to send reflection follow-up", { error: require_setup_surface.formatUnknownError(err) });
277
287
  }
278
288
  }
279
289
  //#endregion
280
290
  //#region extensions/msteams/src/adaptive-card-submit.ts
281
291
  function extractAdaptiveCardSubmittedData(value) {
282
- if (!isRecord(value)) return value;
283
- const action = isRecord(value.action) ? value.action : void 0;
284
- if (action && normalizeOptionalLowercaseString(action.type) === "action.submit" && "data" in action) return action.data;
292
+ if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value)) return value;
293
+ const action = (0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value.action) ? value.action : void 0;
294
+ if (action && (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(action.type) === "action.submit" && "data" in action) return action.data;
285
295
  return value;
286
296
  }
287
297
  function readMSTeamsImBackValue(value) {
288
- if (!isRecord(value)) return null;
289
- const msteams = isRecord(value.msteams) ? value.msteams : void 0;
290
- if (!msteams || normalizeOptionalLowercaseString(msteams.type) !== "imback") return null;
291
- return normalizeOptionalString(msteams.value) ?? null;
298
+ if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value)) return null;
299
+ const msteams = (0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value.msteams) ? value.msteams : void 0;
300
+ if (!msteams || (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(msteams.type) !== "imback") return null;
301
+ return (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(msteams.value) ?? null;
292
302
  }
293
303
  function serializeMSTeamsAdaptiveCardActionValue(value) {
294
304
  const submittedValue = extractAdaptiveCardSubmittedData(value);
@@ -321,28 +331,28 @@ const msteamsIngressIdentity = {
321
331
  resolveEntryId: ({ entryIndex, fieldKey }) => `msteams-entry-${entryIndex + 1}:${fieldKey === "sender-name" ? "name" : "id"}`
322
332
  };
323
333
  function normalizeIngressValue(value) {
324
- return normalizeOptionalLowercaseString(value) ?? null;
334
+ return (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(value) ?? null;
325
335
  }
326
336
  async function resolveMSTeamsSenderAccess(params) {
327
337
  const activity = params.activity;
328
338
  const msteamsCfg = params.cfg.channels?.msteams;
329
- const conversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "unknown");
330
- const convType = normalizeOptionalLowercaseString(activity.conversation?.conversationType);
339
+ const conversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "unknown");
340
+ const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(activity.conversation?.conversationType);
331
341
  const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
332
342
  const senderId = activity.from?.aadObjectId ?? activity.from?.id ?? "unknown";
333
343
  const senderName = activity.from?.name ?? activity.from?.id ?? senderId;
334
- const pairing = createChannelPairingController({
335
- core: getMSTeamsRuntime(),
344
+ const pairing = (0, openclaw_plugin_sdk_channel_pairing.createChannelPairingController)({
345
+ core: require_runtime.getMSTeamsRuntime(),
336
346
  channel: "msteams",
337
- accountId: DEFAULT_ACCOUNT_ID
347
+ accountId: openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID
338
348
  });
339
349
  const dmPolicy = msteamsCfg?.dmPolicy ?? "pairing";
340
350
  const configuredDmAllowFrom = msteamsCfg?.allowFrom ?? [];
341
351
  const groupAllowFrom = msteamsCfg?.groupAllowFrom;
342
- const defaultGroupPolicy = resolveDefaultGroupPolicy(params.cfg);
352
+ const defaultGroupPolicy = (0, openclaw_plugin_sdk_runtime_group_policy.resolveDefaultGroupPolicy)(params.cfg);
343
353
  const groupPolicy = !isDirectMessage && msteamsCfg ? msteamsCfg.groupPolicy ?? defaultGroupPolicy ?? "allowlist" : "disabled";
344
- const allowNameMatching = isDangerousNameMatchingEnabled(msteamsCfg);
345
- const channelGate = resolveMSTeamsRouteConfig({
354
+ const allowNameMatching = (0, openclaw_plugin_sdk_dangerous_name_runtime.isDangerousNameMatchingEnabled)(msteamsCfg);
355
+ const channelGate = require_channel.resolveMSTeamsRouteConfig({
346
356
  cfg: msteamsCfg,
347
357
  teamId: activity.channelData?.team?.id,
348
358
  teamName: activity.channelData?.team?.name,
@@ -351,7 +361,7 @@ async function resolveMSTeamsSenderAccess(params) {
351
361
  allowNameMatching
352
362
  });
353
363
  return {
354
- ...await resolveStableChannelMessageIngress({
364
+ ...await (0, openclaw_plugin_sdk_channel_ingress_runtime.resolveStableChannelMessageIngress)({
355
365
  channelId: "msteams",
356
366
  accountId: pairing.accountId,
357
367
  identity: msteamsIngressIdentity,
@@ -366,7 +376,7 @@ async function resolveMSTeamsSenderAccess(params) {
366
376
  id: conversationId,
367
377
  parentId: activity.channelData?.team?.id
368
378
  },
369
- route: channelIngressRoutes(!isDirectMessage && channelGate.allowlistConfigured && {
379
+ route: (0, openclaw_plugin_sdk_channel_ingress_runtime.channelIngressRoutes)(!isDirectMessage && channelGate.allowlistConfigured && {
370
380
  id: "msteams:team-channel",
371
381
  kind: "nestedAllowlist",
372
382
  allowed: channelGate.allowed,
@@ -426,8 +436,8 @@ function normalizeServiceUrl(serviceUrl) {
426
436
  return serviceUrl.replace(/\/+$/, "");
427
437
  }
428
438
  function buildBotFrameworkAttachmentHeaders(params) {
429
- const headers = ensureUserAgentHeader();
430
- applyAuthorizationHeaderForUrl({
439
+ const headers = require_resolve_allowlist.ensureUserAgentHeader();
440
+ require_resolve_allowlist.applyAuthorizationHeaderForUrl({
431
441
  headers,
432
442
  url: params.url,
433
443
  authAllowHosts: params.policy.authAllowHosts,
@@ -439,7 +449,7 @@ async function fetchBotFrameworkAttachmentInfo(params) {
439
449
  const url = `${normalizeServiceUrl(params.serviceUrl)}/v3/attachments/${encodeURIComponent(params.attachmentId)}`;
440
450
  let response;
441
451
  try {
442
- response = await safeFetchWithPolicy({
452
+ response = await require_resolve_allowlist.safeFetchWithPolicy({
443
453
  url,
444
454
  policy: params.policy,
445
455
  fetchFn: params.fetchFn,
@@ -449,7 +459,8 @@ async function fetchBotFrameworkAttachmentInfo(params) {
449
459
  url,
450
460
  accessToken: params.accessToken,
451
461
  policy: params.policy
452
- }) }
462
+ }) },
463
+ timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
453
464
  });
454
465
  } catch (err) {
455
466
  params.logger?.warn?.("msteams botFramework attachmentInfo fetch failed", { error: err instanceof Error ? err.message : String(err) });
@@ -461,7 +472,7 @@ async function fetchBotFrameworkAttachmentInfo(params) {
461
472
  return;
462
473
  }
463
474
  try {
464
- return await readProviderJsonResponse(response, "msteams botFramework attachmentInfo");
475
+ return await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(response, "msteams botFramework attachmentInfo");
465
476
  } catch (err) {
466
477
  params.logger?.warn?.("msteams botFramework attachmentInfo parse failed", { error: err instanceof Error ? err.message : String(err) });
467
478
  return;
@@ -471,7 +482,7 @@ async function saveBotFrameworkAttachmentView(params) {
471
482
  const url = `${normalizeServiceUrl(params.serviceUrl)}/v3/attachments/${encodeURIComponent(params.attachmentId)}/views/${encodeURIComponent(params.viewId)}`;
472
483
  let response;
473
484
  try {
474
- response = await safeFetchWithPolicy({
485
+ response = await require_resolve_allowlist.safeFetchWithPolicy({
475
486
  url,
476
487
  policy: params.policy,
477
488
  fetchFn: params.fetchFn,
@@ -481,7 +492,8 @@ async function saveBotFrameworkAttachmentView(params) {
481
492
  url,
482
493
  accessToken: params.accessToken,
483
494
  policy: params.policy
484
- }) }
495
+ }) },
496
+ timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
485
497
  });
486
498
  } catch (err) {
487
499
  params.logger?.warn?.("msteams botFramework attachmentView fetch failed", { error: err instanceof Error ? err.message : String(err) });
@@ -494,7 +506,7 @@ async function saveBotFrameworkAttachmentView(params) {
494
506
  }
495
507
  let contentLength;
496
508
  try {
497
- contentLength = parseMediaContentLength(response.headers.get("content-length"));
509
+ contentLength = (0, openclaw_plugin_sdk_media_runtime.parseMediaContentLength)(response.headers.get("content-length"));
498
510
  } catch (err) {
499
511
  await response.body?.cancel();
500
512
  params.logger?.warn?.("msteams botFramework attachmentView invalid content-length", { error: err instanceof Error ? err.message : String(err) });
@@ -505,7 +517,7 @@ async function saveBotFrameworkAttachmentView(params) {
505
517
  return;
506
518
  }
507
519
  try {
508
- return await getMSTeamsRuntime().channel.media.saveResponseMedia(response, {
520
+ return await require_runtime.getMSTeamsRuntime().channel.media.saveResponseMedia(response, {
509
521
  sourceUrl: url,
510
522
  filePathHint: params.fileNameHint,
511
523
  maxBytes: params.maxBytes,
@@ -516,6 +528,8 @@ async function saveBotFrameworkAttachmentView(params) {
516
528
  } catch (err) {
517
529
  params.logger?.warn?.("msteams botFramework attachmentView save failed", { error: err instanceof Error ? err.message : String(err) });
518
530
  return;
531
+ } finally {
532
+ await response.body?.cancel().catch(() => void 0);
519
533
  }
520
534
  }
521
535
  /**
@@ -526,14 +540,19 @@ async function saveBotFrameworkAttachmentView(params) {
526
540
  */
527
541
  async function downloadMSTeamsBotFrameworkAttachment(params) {
528
542
  if (!params.serviceUrl || !params.attachmentId || !params.tokenProvider) return;
529
- const policy = resolveAttachmentFetchPolicy({
543
+ const tokenProvider = params.tokenProvider;
544
+ const policy = require_resolve_allowlist.resolveAttachmentFetchPolicy({
530
545
  allowHosts: params.allowHosts,
531
546
  authAllowHosts: params.authAllowHosts
532
547
  });
533
- if (!isUrlAllowed(`${normalizeServiceUrl(params.serviceUrl)}/v3/attachments/${encodeURIComponent(params.attachmentId)}`, policy.allowHosts)) return;
548
+ if (!require_resolve_allowlist.isUrlAllowed(`${normalizeServiceUrl(params.serviceUrl)}/v3/attachments/${encodeURIComponent(params.attachmentId)}`, policy.allowHosts)) return;
534
549
  let accessToken;
535
550
  try {
536
- accessToken = await params.tokenProvider.getAccessToken(BOT_FRAMEWORK_SCOPE);
551
+ accessToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
552
+ deadline: params.deadline,
553
+ label: "MS Teams Bot Framework token",
554
+ work: () => tokenProvider.getAccessToken(BOT_FRAMEWORK_SCOPE)
555
+ });
537
556
  } catch (err) {
538
557
  params.logger?.warn?.("msteams botFramework token acquisition failed", { error: err instanceof Error ? err.message : String(err) });
539
558
  return;
@@ -547,7 +566,8 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
547
566
  fetchFn: params.fetchFn,
548
567
  fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
549
568
  resolveFn: params.resolveFn,
550
- logger: params.logger
569
+ logger: params.logger,
570
+ deadline: params.deadline
551
571
  });
552
572
  if (!info) return;
553
573
  const views = Array.isArray(info.views) ? info.views : [];
@@ -570,13 +590,14 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
570
590
  fetchFn: params.fetchFn,
571
591
  fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
572
592
  resolveFn: params.resolveFn,
573
- logger: params.logger
593
+ logger: params.logger,
594
+ deadline: params.deadline
574
595
  });
575
596
  if (!saved) return;
576
597
  return {
577
598
  path: saved.path,
578
599
  contentType: saved.contentType,
579
- placeholder: inferPlaceholder({
600
+ placeholder: require_resolve_allowlist.inferPlaceholder({
580
601
  contentType: saved.contentType,
581
602
  fileName: fileNameHint
582
603
  })
@@ -614,6 +635,7 @@ async function downloadMSTeamsBotFrameworkAttachments(params) {
614
635
  fetchFn: params.fetchFn,
615
636
  fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
616
637
  resolveFn: params.resolveFn,
638
+ deadline: params.deadline,
617
639
  fileNameHint: params.fileNameHint,
618
640
  contentTypeHint: params.contentTypeHint,
619
641
  preserveFilenames: params.preserveFilenames,
@@ -639,13 +661,18 @@ async function downloadMSTeamsBotFrameworkAttachments(params) {
639
661
  * sequence while keeping redirect and DNS pinning inside `safeFetchWithPolicy`.
640
662
  */
641
663
  async function saveRemoteMediaDirect(params) {
642
- return await saveResponseMedia(await params.fetchImpl(params.url, { redirect: "follow" }), {
643
- sourceUrl: params.url,
644
- filePathHint: params.filePathHint,
645
- maxBytes: params.maxBytes,
646
- fallbackContentType: params.contentTypeHint,
647
- originalFilename: params.originalFilename
648
- });
664
+ const response = await params.fetchImpl(params.url, { redirect: "follow" });
665
+ try {
666
+ return await (0, openclaw_plugin_sdk_media_runtime.saveResponseMedia)(response, {
667
+ sourceUrl: params.url,
668
+ filePathHint: params.filePathHint,
669
+ maxBytes: params.maxBytes,
670
+ fallbackContentType: params.contentTypeHint,
671
+ originalFilename: params.originalFilename
672
+ });
673
+ } finally {
674
+ await response.body?.cancel().catch(() => void 0);
675
+ }
649
676
  }
650
677
  async function downloadAndStoreMSTeamsRemoteMedia(params) {
651
678
  const originalFilename = params.preserveFilenames ? params.filePathHint : void 0;
@@ -658,7 +685,7 @@ async function downloadAndStoreMSTeamsRemoteMedia(params) {
658
685
  contentTypeHint: params.contentTypeHint,
659
686
  originalFilename
660
687
  });
661
- else saved = await getMSTeamsRuntime().channel.media.saveRemoteMedia({
688
+ else saved = await require_runtime.getMSTeamsRuntime().channel.media.saveRemoteMedia({
662
689
  url: params.url,
663
690
  fetchImpl: params.fetchImpl,
664
691
  filePathHint: params.filePathHint,
@@ -670,7 +697,7 @@ async function downloadAndStoreMSTeamsRemoteMedia(params) {
670
697
  return {
671
698
  path: saved.path,
672
699
  contentType: saved.contentType,
673
- placeholder: params.placeholder ?? inferPlaceholder({
700
+ placeholder: params.placeholder ?? require_resolve_allowlist.inferPlaceholder({
674
701
  contentType: saved.contentType,
675
702
  fileName: params.filePathHint
676
703
  })
@@ -679,35 +706,35 @@ async function downloadAndStoreMSTeamsRemoteMedia(params) {
679
706
  //#endregion
680
707
  //#region extensions/msteams/src/attachments/download.ts
681
708
  function resolveDownloadCandidate(att) {
682
- const contentType = normalizeContentType(att.contentType);
683
- const name = normalizeOptionalString(att.name) ?? "";
709
+ const contentType = require_resolve_allowlist.normalizeContentType(att.contentType);
710
+ const name = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.name) ?? "";
684
711
  if (contentType === "application/vnd.microsoft.teams.file.download.info") {
685
- if (!isRecord$1(att.content)) return null;
686
- const downloadUrl = normalizeOptionalString(att.content.downloadUrl) ?? "";
712
+ if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(att.content)) return null;
713
+ const downloadUrl = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.content.downloadUrl) ?? "";
687
714
  if (!downloadUrl) return null;
688
- const fileType = normalizeOptionalString(att.content.fileType) ?? "";
689
- const uniqueId = normalizeOptionalString(att.content.uniqueId) ?? "";
690
- const fileName = normalizeOptionalString(att.content.fileName) ?? "";
715
+ const fileType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.content.fileType) ?? "";
716
+ const uniqueId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.content.uniqueId) ?? "";
717
+ const fileName = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.content.fileName) ?? "";
691
718
  const fileHint = name || fileName || (uniqueId && fileType ? `${uniqueId}.${fileType}` : "");
692
719
  return {
693
720
  url: downloadUrl,
694
721
  fileHint: fileHint || void 0,
695
722
  contentTypeHint: void 0,
696
- placeholder: inferPlaceholder({
723
+ placeholder: require_resolve_allowlist.inferPlaceholder({
697
724
  contentType,
698
725
  fileName: fileHint,
699
726
  fileType
700
727
  })
701
728
  };
702
729
  }
703
- const contentUrl = normalizeOptionalString(att.contentUrl) ?? "";
730
+ const contentUrl = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.contentUrl) ?? "";
704
731
  if (!contentUrl) return null;
705
- const sharesUrl = tryBuildGraphSharesUrlForSharedLink(contentUrl);
732
+ const sharesUrl = require_resolve_allowlist.tryBuildGraphSharesUrlForSharedLink(contentUrl);
706
733
  return {
707
734
  url: sharesUrl ?? contentUrl,
708
735
  fileHint: name || void 0,
709
736
  contentTypeHint: sharesUrl ? void 0 : contentType,
710
- placeholder: inferPlaceholder({
737
+ placeholder: require_resolve_allowlist.inferPlaceholder({
711
738
  contentType,
712
739
  fileName: name
713
740
  })
@@ -715,7 +742,7 @@ function resolveDownloadCandidate(att) {
715
742
  }
716
743
  function scopeCandidatesForUrl(url) {
717
744
  try {
718
- const host = normalizeLowercaseStringOrEmpty(new URL(url).hostname);
745
+ const host = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeLowercaseStringOrEmpty)(new URL(url).hostname);
719
746
  return host.endsWith("graph.microsoft.com") || host.endsWith("sharepoint.com") || host.endsWith("1drv.ms") || host.includes("sharepoint") ? ["https://graph.microsoft.com", "https://api.botframework.com"] : ["https://api.botframework.com", "https://graph.microsoft.com"];
720
747
  } catch {
721
748
  return ["https://api.botframework.com", "https://graph.microsoft.com"];
@@ -725,33 +752,39 @@ function isRedirectStatus(status) {
725
752
  return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
726
753
  }
727
754
  async function resolveInlineDataImageMime(inline) {
728
- const mime = normalizeOptionalLowercaseString(await getMSTeamsRuntime().media.detectMime({
755
+ const mime = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(await require_runtime.getMSTeamsRuntime().media.detectMime({
729
756
  buffer: inline.data,
730
757
  headerMime: inline.contentType
731
758
  }) ?? inline.contentType);
732
759
  return mime?.startsWith("image/") ? mime : void 0;
733
760
  }
734
761
  async function fetchWithAuthFallback(params) {
735
- const firstAttempt = await safeFetchWithPolicy({
762
+ const firstAttempt = await require_resolve_allowlist.safeFetchWithPolicy({
736
763
  url: params.url,
737
764
  policy: params.policy,
738
765
  fetchFn: params.fetchFn,
739
766
  fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
740
767
  requestInit: params.requestInit,
741
- resolveFn: params.resolveFn
768
+ resolveFn: params.resolveFn,
769
+ timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
742
770
  });
743
771
  if (firstAttempt.ok) return firstAttempt;
744
772
  if (!params.tokenProvider) return firstAttempt;
773
+ const tokenProvider = params.tokenProvider;
745
774
  if (firstAttempt.status !== 401 && firstAttempt.status !== 403) return firstAttempt;
746
- if (!isUrlAllowed(params.url, params.policy.authAllowHosts)) return firstAttempt;
775
+ if (!require_resolve_allowlist.isUrlAllowed(params.url, params.policy.authAllowHosts)) return firstAttempt;
747
776
  await firstAttempt.body?.cancel();
748
777
  const scopes = scopeCandidatesForUrl(params.url);
749
778
  const fetchFn = params.fetchFn ?? fetch;
750
779
  for (const scope of scopes) try {
751
- const token = await params.tokenProvider.getAccessToken(scope);
780
+ const token = await require_resolve_allowlist.withMSTeamsRequestDeadline({
781
+ deadline: params.deadline,
782
+ label: "MS Teams attachment token",
783
+ work: () => tokenProvider.getAccessToken(scope)
784
+ });
752
785
  const authHeaders = new Headers(params.requestInit?.headers);
753
786
  authHeaders.set("Authorization", `Bearer ${token}`);
754
- const authAttempt = await safeFetchWithPolicy({
787
+ const authAttempt = await require_resolve_allowlist.safeFetchWithPolicy({
755
788
  url: params.url,
756
789
  policy: params.policy,
757
790
  fetchFn,
@@ -760,7 +793,8 @@ async function fetchWithAuthFallback(params) {
760
793
  ...params.requestInit,
761
794
  headers: authHeaders
762
795
  },
763
- resolveFn: params.resolveFn
796
+ resolveFn: params.resolveFn,
797
+ timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
764
798
  });
765
799
  if (authAttempt.ok) return authAttempt;
766
800
  if (isRedirectStatus(authAttempt.status)) return authAttempt;
@@ -779,20 +813,20 @@ async function fetchWithAuthFallback(params) {
779
813
  async function downloadMSTeamsAttachments(params) {
780
814
  const list = Array.isArray(params.attachments) ? params.attachments : [];
781
815
  if (list.length === 0) return [];
782
- const policy = resolveAttachmentFetchPolicy({
816
+ const policy = require_resolve_allowlist.resolveAttachmentFetchPolicy({
783
817
  allowHosts: params.allowHosts,
784
818
  authAllowHosts: params.authAllowHosts
785
819
  });
786
820
  const allowHosts = policy.allowHosts;
787
- const ssrfPolicy = resolveMediaSsrfPolicy(allowHosts);
788
- const candidates = list.filter(isDownloadableAttachment).map(resolveDownloadCandidate).filter(Boolean);
789
- const inlineCandidates = extractInlineImageCandidates(list, {
821
+ const ssrfPolicy = require_resolve_allowlist.resolveMediaSsrfPolicy(allowHosts);
822
+ const candidates = list.filter(require_resolve_allowlist.isDownloadableAttachment).map(resolveDownloadCandidate).filter(Boolean);
823
+ const inlineCandidates = require_resolve_allowlist.extractInlineImageCandidates(list, {
790
824
  maxInlineBytes: params.maxBytes,
791
825
  maxInlineTotalBytes: params.maxBytes
792
826
  });
793
827
  const seenUrls = /* @__PURE__ */ new Set();
794
828
  for (const inline of inlineCandidates) if (inline.kind === "url") {
795
- if (!isUrlAllowed(inline.url, allowHosts)) continue;
829
+ if (!require_resolve_allowlist.isUrlAllowed(inline.url, allowHosts)) continue;
796
830
  if (seenUrls.has(inline.url)) continue;
797
831
  seenUrls.add(inline.url);
798
832
  candidates.push({
@@ -810,18 +844,18 @@ async function downloadMSTeamsAttachments(params) {
810
844
  try {
811
845
  const contentType = await resolveInlineDataImageMime(inline);
812
846
  if (!contentType) continue;
813
- const saved = await getMSTeamsRuntime().channel.media.saveMediaBuffer(inline.data, contentType, "inbound", params.maxBytes);
847
+ const saved = await require_runtime.getMSTeamsRuntime().channel.media.saveMediaBuffer(inline.data, contentType, "inbound", params.maxBytes);
814
848
  out.push({
815
849
  path: saved.path,
816
850
  contentType: saved.contentType,
817
- placeholder: inferPlaceholder({ contentType: saved.contentType ?? contentType })
851
+ placeholder: require_resolve_allowlist.inferPlaceholder({ contentType: saved.contentType ?? contentType })
818
852
  });
819
853
  } catch (err) {
820
854
  params.logger?.warn?.("msteams inline attachment decode failed", { error: err instanceof Error ? err.message : String(err) });
821
855
  }
822
856
  }
823
857
  for (const candidate of candidates) {
824
- if (!isUrlAllowed(candidate.url, allowHosts)) continue;
858
+ if (!require_resolve_allowlist.isUrlAllowed(candidate.url, allowHosts)) continue;
825
859
  try {
826
860
  const media = await downloadAndStoreMSTeamsRemoteMedia({
827
861
  url: candidate.url,
@@ -833,13 +867,14 @@ async function downloadMSTeamsAttachments(params) {
833
867
  ssrfPolicy,
834
868
  useDirectFetch: true,
835
869
  fetchImpl: (input, init) => fetchWithAuthFallback({
836
- url: resolveRequestUrl(input),
870
+ url: require_resolve_allowlist.resolveRequestUrl(input),
837
871
  tokenProvider: params.tokenProvider,
838
872
  fetchFn: params.fetchFn,
839
873
  fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
840
874
  requestInit: init,
841
875
  resolveFn: params.resolveFn,
842
- policy
876
+ policy,
877
+ deadline: params.deadline
843
878
  })
844
879
  });
845
880
  out.push(media);
@@ -859,44 +894,31 @@ function safeHostForLog(url) {
859
894
  }
860
895
  //#endregion
861
896
  //#region extensions/msteams/src/attachments/graph.ts
862
- function buildMSTeamsGraphMessageUrls(params) {
863
- const conversationType = normalizeLowercaseStringOrEmpty(params.conversationType ?? "");
864
- const messageIdCandidates = /* @__PURE__ */ new Set();
865
- const pushCandidate = (value) => {
866
- const trimmed = normalizeOptionalString(value) ?? "";
867
- if (trimmed) messageIdCandidates.add(trimmed);
868
- };
869
- pushCandidate(params.messageId);
870
- pushCandidate(params.conversationMessageId);
871
- pushCandidate(readNestedString(params.channelData, ["messageId"]));
872
- pushCandidate(readNestedString(params.channelData, ["teamsMessageId"]));
873
- const replyToId = normalizeOptionalString(params.replyToId) ?? "";
897
+ function buildMSTeamsGraphMessageUrl(params) {
898
+ const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeLowercaseStringOrEmpty)(params.conversationType ?? "");
899
+ const messageId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(params.messageId);
900
+ if (!messageId) return;
874
901
  if (conversationType === "channel") {
875
- const teamId = readNestedString(params.channelData, ["team", "id"]) ?? readNestedString(params.channelData, ["teamId"]);
876
- const channelId = readNestedString(params.channelData, ["channel", "id"]) ?? readNestedString(params.channelData, ["channelId"]) ?? readNestedString(params.channelData, ["teamsChannelId"]);
877
- if (!teamId || !channelId) return [];
878
- const urls = [];
879
- if (replyToId) for (const candidate of messageIdCandidates) {
880
- if (candidate === replyToId) continue;
881
- urls.push(`${GRAPH_ROOT}/teams/${encodeURIComponent(teamId)}/channels/${encodeURIComponent(channelId)}/messages/${encodeURIComponent(replyToId)}/replies/${encodeURIComponent(candidate)}`);
882
- }
883
- if (messageIdCandidates.size === 0 && replyToId) messageIdCandidates.add(replyToId);
884
- for (const candidate of messageIdCandidates) urls.push(`${GRAPH_ROOT}/teams/${encodeURIComponent(teamId)}/channels/${encodeURIComponent(channelId)}/messages/${encodeURIComponent(candidate)}`);
885
- return uniqueStrings(urls);
902
+ const teamAadGroupId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(params.teamAadGroupId);
903
+ const channelId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(params.channelId);
904
+ if (!teamAadGroupId || !channelId) return;
905
+ const messageRoot = `${require_resolve_allowlist.GRAPH_ROOT}/teams/${encodeURIComponent(teamAadGroupId)}/channels/${encodeURIComponent(channelId)}/messages`;
906
+ const threadRootMessageId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(params.threadRootMessageId);
907
+ return threadRootMessageId && threadRootMessageId !== messageId ? `${messageRoot}/${encodeURIComponent(threadRootMessageId)}/replies/${encodeURIComponent(messageId)}` : `${messageRoot}/${encodeURIComponent(messageId)}`;
886
908
  }
887
- const chatId = params.conversationId?.trim() || readNestedString(params.channelData, ["chatId"]);
888
- if (!chatId) return [];
889
- if (messageIdCandidates.size === 0 && replyToId) messageIdCandidates.add(replyToId);
890
- return uniqueStrings(Array.from(messageIdCandidates).map((candidate) => `${GRAPH_ROOT}/chats/${encodeURIComponent(chatId)}/messages/${encodeURIComponent(candidate)}`));
909
+ const chatId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(params.conversationId);
910
+ if (!chatId) return;
911
+ return `${require_resolve_allowlist.GRAPH_ROOT}/chats/${encodeURIComponent(chatId)}/messages/${encodeURIComponent(messageId)}`;
891
912
  }
892
913
  async function fetchGraphCollection(params) {
893
914
  const fetchFn = params.fetchFn ?? fetch;
894
- const { response, release } = await fetchWithSsrFGuard({
915
+ const { response, release } = await (0, openclaw_plugin_sdk_ssrf_runtime.fetchWithSsrFGuard)({
895
916
  url: params.url,
896
917
  fetchImpl: fetchFn,
897
- init: { headers: ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
918
+ init: { headers: require_resolve_allowlist.ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
898
919
  policy: params.ssrfPolicy,
899
- auditContext: "msteams.graph.collection"
920
+ auditContext: "msteams.graph.collection",
921
+ timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
900
922
  });
901
923
  try {
902
924
  const status = response.status;
@@ -907,7 +929,7 @@ async function fetchGraphCollection(params) {
907
929
  try {
908
930
  return {
909
931
  status,
910
- items: await readProviderJsonArrayFieldResponse(response, "MS Teams Graph collection", "value")
932
+ items: await (0, openclaw_plugin_sdk_provider_http.readProviderJsonArrayFieldResponse)(response, "MS Teams Graph collection", "value")
911
933
  };
912
934
  } catch {
913
935
  return {
@@ -925,7 +947,7 @@ function normalizeGraphAttachment(att) {
925
947
  content = JSON.parse(content);
926
948
  } catch {}
927
949
  return {
928
- contentType: normalizeContentType(att.contentType) ?? void 0,
950
+ contentType: require_resolve_allowlist.normalizeContentType(att.contentType) ?? void 0,
929
951
  contentUrl: att.contentUrl ?? void 0,
930
952
  name: att.name ?? void 0,
931
953
  thumbnailUrl: att.thumbnailUrl ?? void 0,
@@ -937,12 +959,22 @@ function normalizeGraphAttachment(att) {
937
959
  * Renamed from downloadGraphHostedImages to support all file types.
938
960
  */
939
961
  async function downloadGraphHostedContent(params) {
940
- const hosted = await fetchGraphCollection({
941
- url: `${params.messageUrl}/hostedContents`,
942
- accessToken: params.accessToken,
943
- fetchFn: params.fetchFn,
944
- ssrfPolicy: params.ssrfPolicy
945
- });
962
+ let hosted;
963
+ try {
964
+ hosted = await fetchGraphCollection({
965
+ url: `${params.messageUrl}/hostedContents`,
966
+ accessToken: params.accessToken,
967
+ fetchFn: params.fetchFn,
968
+ ssrfPolicy: params.ssrfPolicy,
969
+ deadline: params.deadline
970
+ });
971
+ } catch (err) {
972
+ params.logger?.warn?.("msteams graph hostedContents fetch failed", { error: err instanceof Error ? err.message : String(err) });
973
+ return {
974
+ media: [],
975
+ count: 0
976
+ };
977
+ }
946
978
  if (hosted.items.length === 0) return {
947
979
  media: [],
948
980
  status: hosted.status,
@@ -953,16 +985,17 @@ async function downloadGraphHostedContent(params) {
953
985
  if (!item.id) continue;
954
986
  try {
955
987
  const valueUrl = `${params.messageUrl}/hostedContents/${encodeURIComponent(item.id)}/$value`;
956
- const { response: valRes, release } = await fetchWithSsrFGuard({
988
+ const { response: valRes, release } = await (0, openclaw_plugin_sdk_ssrf_runtime.fetchWithSsrFGuard)({
957
989
  url: valueUrl,
958
990
  fetchImpl: params.fetchFn ?? fetch,
959
- init: { headers: ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
991
+ init: { headers: require_resolve_allowlist.ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
960
992
  policy: params.ssrfPolicy,
961
- auditContext: "msteams.graph.hostedContent.value"
993
+ auditContext: "msteams.graph.hostedContent.value",
994
+ timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
962
995
  });
963
996
  try {
964
997
  if (!valRes.ok) continue;
965
- const saved = await getMSTeamsRuntime().channel.media.saveResponseMedia(valRes, {
998
+ const saved = await require_runtime.getMSTeamsRuntime().channel.media.saveResponseMedia(valRes, {
966
999
  sourceUrl: valueUrl,
967
1000
  maxBytes: params.maxBytes,
968
1001
  fallbackContentType: item.contentType ?? void 0,
@@ -971,7 +1004,7 @@ async function downloadGraphHostedContent(params) {
971
1004
  out.push({
972
1005
  path: saved.path,
973
1006
  contentType: saved.contentType,
974
- placeholder: inferPlaceholder({ contentType: saved.contentType })
1007
+ placeholder: require_resolve_allowlist.inferPlaceholder({ contentType: saved.contentType })
975
1008
  });
976
1009
  } finally {
977
1010
  await release();
@@ -989,18 +1022,22 @@ async function downloadGraphHostedContent(params) {
989
1022
  }
990
1023
  async function downloadMSTeamsGraphMedia(params) {
991
1024
  if (!params.messageUrl || !params.tokenProvider) return { media: [] };
992
- const policy = resolveAttachmentFetchPolicy({
1025
+ const tokenProvider = params.tokenProvider;
1026
+ const policy = require_resolve_allowlist.resolveAttachmentFetchPolicy({
993
1027
  allowHosts: params.allowHosts,
994
1028
  authAllowHosts: params.authAllowHosts
995
1029
  });
996
- const ssrfPolicy = resolveMediaSsrfPolicy(policy.allowHosts);
1030
+ const ssrfPolicy = require_resolve_allowlist.resolveMediaSsrfPolicy(policy.allowHosts);
997
1031
  const messageUrl = params.messageUrl;
998
- const debugLog = params.log ?? params.logger ?? void 0;
999
1032
  let accessToken;
1000
1033
  try {
1001
- accessToken = await params.tokenProvider.getAccessToken("https://graph.microsoft.com");
1034
+ accessToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
1035
+ deadline: params.deadline,
1036
+ label: "MS Teams Graph media token",
1037
+ work: () => tokenProvider.getAccessToken("https://graph.microsoft.com")
1038
+ });
1002
1039
  } catch (err) {
1003
- debugLog?.debug?.("graph media token acquisition failed", {
1040
+ params.logger?.debug?.("graph media token acquisition failed", {
1004
1041
  messageUrl,
1005
1042
  error: err instanceof Error ? err.message : String(err)
1006
1043
  });
@@ -1015,23 +1052,25 @@ async function downloadMSTeamsGraphMedia(params) {
1015
1052
  const sharePointMedia = [];
1016
1053
  const downloadedReferenceUrls = /* @__PURE__ */ new Set();
1017
1054
  let messageAttachments = [];
1055
+ let referenceAttachments = [];
1018
1056
  let messageStatus;
1019
1057
  try {
1020
- const { response: msgRes, release } = await fetchWithSsrFGuard({
1058
+ const { response: msgRes, release } = await (0, openclaw_plugin_sdk_ssrf_runtime.fetchWithSsrFGuard)({
1021
1059
  url: messageUrl,
1022
1060
  fetchImpl: fetchFn,
1023
- init: { headers: ensureUserAgentHeader({ Authorization: `Bearer ${accessToken}` }) },
1061
+ init: { headers: require_resolve_allowlist.ensureUserAgentHeader({ Authorization: `Bearer ${accessToken}` }) },
1024
1062
  policy: ssrfPolicy,
1025
- auditContext: "msteams.graph.message"
1063
+ auditContext: "msteams.graph.message",
1064
+ timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
1026
1065
  });
1027
1066
  try {
1028
1067
  messageStatus = msgRes.status;
1029
1068
  if (msgRes.ok) {
1030
1069
  let msgData;
1031
1070
  try {
1032
- msgData = await readProviderJsonResponse(msgRes, "MS Teams Graph message");
1071
+ msgData = await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(msgRes, "MS Teams Graph message");
1033
1072
  } catch (err) {
1034
- debugLog?.debug?.("graph media message parse failed", {
1073
+ params.logger?.debug?.("graph media message parse failed", {
1035
1074
  messageUrl,
1036
1075
  error: err instanceof Error ? err.message : String(err)
1037
1076
  });
@@ -1042,60 +1081,8 @@ async function downloadMSTeamsGraphMedia(params) {
1042
1081
  msgData = {};
1043
1082
  }
1044
1083
  messageAttachments = Array.isArray(msgData.attachments) ? msgData.attachments : [];
1045
- const spAttachments = messageAttachments.filter((a) => a.contentType === "reference" && a.contentUrl && a.name);
1046
- for (const att of spAttachments) {
1047
- const name = att.name ?? "file";
1048
- const shareUrl = att.contentUrl ?? "";
1049
- if (!shareUrl) continue;
1050
- try {
1051
- const sharesUrl = `${GRAPH_ROOT}/shares/${encodeGraphShareId(shareUrl)}/driveItem/content`;
1052
- if (!isUrlAllowed(sharesUrl, policy.allowHosts)) {
1053
- debugLog?.debug?.("graph media sharepoint url not in allowHosts", {
1054
- messageUrl,
1055
- sharesUrl
1056
- });
1057
- continue;
1058
- }
1059
- const media = await downloadAndStoreMSTeamsRemoteMedia({
1060
- url: sharesUrl,
1061
- filePathHint: name,
1062
- maxBytes: params.maxBytes,
1063
- contentTypeHint: "application/octet-stream",
1064
- preserveFilenames: params.preserveFilenames,
1065
- ssrfPolicy,
1066
- useDirectFetch: true,
1067
- fetchImpl: async (input, init) => {
1068
- const requestUrl = resolveRequestUrl(input);
1069
- const headers = ensureUserAgentHeader(init?.headers);
1070
- applyAuthorizationHeaderForUrl({
1071
- headers,
1072
- url: requestUrl,
1073
- authAllowHosts: policy.authAllowHosts,
1074
- bearerToken: accessToken
1075
- });
1076
- return await safeFetchWithPolicy({
1077
- url: requestUrl,
1078
- policy,
1079
- fetchFn,
1080
- fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
1081
- requestInit: {
1082
- ...init,
1083
- headers
1084
- },
1085
- resolveFn: params.resolveFn
1086
- });
1087
- }
1088
- });
1089
- sharePointMedia.push(media);
1090
- downloadedReferenceUrls.add(shareUrl);
1091
- } catch (err) {
1092
- params.logger?.warn?.("msteams SharePoint reference download failed", {
1093
- error: err instanceof Error ? err.message : String(err),
1094
- name
1095
- });
1096
- }
1097
- }
1098
- } else debugLog?.debug?.("graph media message fetch not ok", {
1084
+ referenceAttachments = messageAttachments.filter((a) => a.contentType === "reference" && a.contentUrl && a.name);
1085
+ } else params.logger?.debug?.("graph media message fetch not ok", {
1099
1086
  messageUrl,
1100
1087
  status: messageStatus
1101
1088
  });
@@ -1103,12 +1090,65 @@ async function downloadMSTeamsGraphMedia(params) {
1103
1090
  await release();
1104
1091
  }
1105
1092
  } catch (err) {
1106
- debugLog?.debug?.("graph media message fetch failed", {
1093
+ params.logger?.debug?.("graph media message fetch failed", {
1107
1094
  messageUrl,
1108
1095
  error: err instanceof Error ? err.message : String(err)
1109
1096
  });
1110
1097
  params.logger?.warn?.("msteams graph message fetch failed", { error: err instanceof Error ? err.message : String(err) });
1111
1098
  }
1099
+ for (const att of referenceAttachments) {
1100
+ const name = att.name ?? "file";
1101
+ const shareUrl = att.contentUrl ?? "";
1102
+ if (!shareUrl) continue;
1103
+ try {
1104
+ const sharesUrl = `${require_resolve_allowlist.GRAPH_ROOT}/shares/${require_resolve_allowlist.encodeGraphShareId(shareUrl)}/driveItem/content`;
1105
+ if (!require_resolve_allowlist.isUrlAllowed(sharesUrl, policy.allowHosts)) {
1106
+ params.logger?.debug?.("graph media sharepoint url not in allowHosts", {
1107
+ messageUrl,
1108
+ sharesUrl
1109
+ });
1110
+ continue;
1111
+ }
1112
+ const media = await downloadAndStoreMSTeamsRemoteMedia({
1113
+ url: sharesUrl,
1114
+ filePathHint: name,
1115
+ maxBytes: params.maxBytes,
1116
+ contentTypeHint: "application/octet-stream",
1117
+ preserveFilenames: params.preserveFilenames,
1118
+ ssrfPolicy,
1119
+ useDirectFetch: true,
1120
+ fetchImpl: async (input, init) => {
1121
+ const requestUrl = require_resolve_allowlist.resolveRequestUrl(input);
1122
+ const headers = require_resolve_allowlist.ensureUserAgentHeader(init?.headers);
1123
+ require_resolve_allowlist.applyAuthorizationHeaderForUrl({
1124
+ headers,
1125
+ url: requestUrl,
1126
+ authAllowHosts: policy.authAllowHosts,
1127
+ bearerToken: accessToken
1128
+ });
1129
+ return await require_resolve_allowlist.safeFetchWithPolicy({
1130
+ url: requestUrl,
1131
+ policy,
1132
+ fetchFn,
1133
+ fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
1134
+ requestInit: {
1135
+ ...init,
1136
+ headers
1137
+ },
1138
+ resolveFn: params.resolveFn,
1139
+ timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
1140
+ });
1141
+ }
1142
+ });
1143
+ sharePointMedia.push(media);
1144
+ downloadedReferenceUrls.add(shareUrl);
1145
+ } catch (err) {
1146
+ params.logger?.warn?.("msteams SharePoint reference download failed", {
1147
+ error: err instanceof Error ? err.message : String(err),
1148
+ name
1149
+ });
1150
+ }
1151
+ }
1112
1152
  const hosted = await downloadGraphHostedContent({
1113
1153
  accessToken,
1114
1154
  messageUrl,
@@ -1116,11 +1156,12 @@ async function downloadMSTeamsGraphMedia(params) {
1116
1156
  fetchFn: params.fetchFn,
1117
1157
  preserveFilenames: params.preserveFilenames,
1118
1158
  ssrfPolicy,
1119
- logger: params.logger
1159
+ logger: params.logger,
1160
+ deadline: params.deadline
1120
1161
  });
1121
1162
  const normalizedAttachments = messageAttachments.map(normalizeGraphAttachment);
1122
1163
  const filteredAttachments = sharePointMedia.length > 0 ? normalizedAttachments.filter((att) => {
1123
- if (normalizeOptionalLowercaseString(att.contentType) !== "reference") return true;
1164
+ if ((0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(att.contentType) !== "reference") return true;
1124
1165
  const url = typeof att.contentUrl === "string" ? att.contentUrl : "";
1125
1166
  if (!url) return true;
1126
1167
  return !downloadedReferenceUrls.has(url);
@@ -1136,6 +1177,7 @@ async function downloadMSTeamsGraphMedia(params) {
1136
1177
  fetchFn: params.fetchFn,
1137
1178
  fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
1138
1179
  resolveFn: params.resolveFn,
1180
+ deadline: params.deadline,
1139
1181
  preserveFilenames: params.preserveFilenames,
1140
1182
  logger: params.logger
1141
1183
  });
@@ -1170,14 +1212,14 @@ function extractMSTeamsHtmlAttachmentIds(attachments) {
1170
1212
  if (list.length === 0) return [];
1171
1213
  const ids = /* @__PURE__ */ new Set();
1172
1214
  for (const att of list) {
1173
- const html = extractHtmlFromAttachment(att);
1215
+ const html = require_resolve_allowlist.extractHtmlFromAttachment(att);
1174
1216
  if (!html) continue;
1175
- ATTACHMENT_TAG_RE.lastIndex = 0;
1176
- let match = ATTACHMENT_TAG_RE.exec(html);
1217
+ require_resolve_allowlist.ATTACHMENT_TAG_RE.lastIndex = 0;
1218
+ let match = require_resolve_allowlist.ATTACHMENT_TAG_RE.exec(html);
1177
1219
  while (match) {
1178
1220
  const id = match[1]?.trim();
1179
1221
  if (id) ids.add(id);
1180
- match = ATTACHMENT_TAG_RE.exec(html);
1222
+ match = require_resolve_allowlist.ATTACHMENT_TAG_RE.exec(html);
1181
1223
  }
1182
1224
  }
1183
1225
  return Array.from(ids);
@@ -1193,26 +1235,26 @@ function summarizeMSTeamsHtmlAttachments(attachments) {
1193
1235
  let attachmentTags = 0;
1194
1236
  const attachmentIds = /* @__PURE__ */ new Set();
1195
1237
  for (const att of list) {
1196
- const html = extractHtmlFromAttachment(att);
1238
+ const html = require_resolve_allowlist.extractHtmlFromAttachment(att);
1197
1239
  if (!html) continue;
1198
1240
  htmlAttachments += 1;
1199
- IMG_SRC_RE.lastIndex = 0;
1200
- let match = IMG_SRC_RE.exec(html);
1241
+ require_resolve_allowlist.IMG_SRC_RE.lastIndex = 0;
1242
+ let match = require_resolve_allowlist.IMG_SRC_RE.exec(html);
1201
1243
  while (match) {
1202
1244
  imgTags += 1;
1203
1245
  const src = match[1]?.trim();
1204
1246
  if (src) if (src.startsWith("data:")) dataImages += 1;
1205
1247
  else if (src.startsWith("cid:")) cidImages += 1;
1206
- else srcHosts.add(safeHostForUrl(src));
1207
- match = IMG_SRC_RE.exec(html);
1248
+ else srcHosts.add(require_resolve_allowlist.safeHostForUrl(src));
1249
+ match = require_resolve_allowlist.IMG_SRC_RE.exec(html);
1208
1250
  }
1209
- ATTACHMENT_TAG_RE.lastIndex = 0;
1210
- let attachmentMatch = ATTACHMENT_TAG_RE.exec(html);
1251
+ require_resolve_allowlist.ATTACHMENT_TAG_RE.lastIndex = 0;
1252
+ let attachmentMatch = require_resolve_allowlist.ATTACHMENT_TAG_RE.exec(html);
1211
1253
  while (attachmentMatch) {
1212
1254
  attachmentTags += 1;
1213
1255
  const id = attachmentMatch[1]?.trim();
1214
1256
  if (id) attachmentIds.add(id);
1215
- attachmentMatch = ATTACHMENT_TAG_RE.exec(html);
1257
+ attachmentMatch = require_resolve_allowlist.ATTACHMENT_TAG_RE.exec(html);
1216
1258
  }
1217
1259
  }
1218
1260
  if (htmlAttachments === 0) return;
@@ -1227,18 +1269,18 @@ function summarizeMSTeamsHtmlAttachments(attachments) {
1227
1269
  };
1228
1270
  }
1229
1271
  function isAdvertisedFileAttachment(attachment) {
1230
- const contentType = normalizeContentType(attachment.contentType) ?? "";
1272
+ const contentType = require_resolve_allowlist.normalizeContentType(attachment.contentType) ?? "";
1231
1273
  if (contentType.startsWith("text/html") || contentType.startsWith("application/vnd.microsoft.card.") || contentType.startsWith("application/vnd.microsoft.teams.card.")) return false;
1232
- return Boolean(isDownloadableAttachment(attachment) || isLikelyImageAttachment(attachment) || attachment.name?.trim() || contentType);
1274
+ return Boolean(require_resolve_allowlist.isDownloadableAttachment(attachment) || require_resolve_allowlist.isLikelyImageAttachment(attachment) || attachment.name?.trim() || contentType);
1233
1275
  }
1234
1276
  function countDistinctInlineImages(attachments) {
1235
1277
  const seenReferences = /* @__PURE__ */ new Set();
1236
1278
  let count = 0;
1237
1279
  for (const attachment of attachments) {
1238
- const html = extractHtmlFromAttachment(attachment);
1280
+ const html = require_resolve_allowlist.extractHtmlFromAttachment(attachment);
1239
1281
  if (!html) continue;
1240
- IMG_SRC_RE.lastIndex = 0;
1241
- let match = IMG_SRC_RE.exec(html);
1282
+ require_resolve_allowlist.IMG_SRC_RE.lastIndex = 0;
1283
+ let match = require_resolve_allowlist.IMG_SRC_RE.exec(html);
1242
1284
  while (match) {
1243
1285
  const src = match[1]?.trim();
1244
1286
  if (src?.startsWith("data:")) count += 1;
@@ -1246,7 +1288,7 @@ function countDistinctInlineImages(attachments) {
1246
1288
  seenReferences.add(src);
1247
1289
  count += 1;
1248
1290
  }
1249
- match = IMG_SRC_RE.exec(html);
1291
+ match = require_resolve_allowlist.IMG_SRC_RE.exec(html);
1250
1292
  }
1251
1293
  }
1252
1294
  return count;
@@ -1254,14 +1296,14 @@ function countDistinctInlineImages(attachments) {
1254
1296
  function countDistinctInlineCandidates(attachments, limits) {
1255
1297
  const seenUrls = /* @__PURE__ */ new Set();
1256
1298
  let dataCount = 0;
1257
- for (const candidate of extractInlineImageCandidates(attachments, limits)) if (candidate.kind === "data") dataCount += 1;
1299
+ for (const candidate of require_resolve_allowlist.extractInlineImageCandidates(attachments, limits)) if (candidate.kind === "data") dataCount += 1;
1258
1300
  else seenUrls.add(candidate.url);
1259
1301
  return dataCount + seenUrls.size;
1260
1302
  }
1261
1303
  function countUnrepresentedHtmlAttachmentIds(attachments) {
1262
1304
  const representedIds = /* @__PURE__ */ new Set();
1263
1305
  for (const attachment of attachments) {
1264
- if ((normalizeContentType(attachment.contentType) ?? "").startsWith("text/html")) continue;
1306
+ if ((require_resolve_allowlist.normalizeContentType(attachment.contentType) ?? "").startsWith("text/html")) continue;
1265
1307
  const id = attachment.id?.trim();
1266
1308
  if (id) representedIds.add(id);
1267
1309
  }
@@ -1282,7 +1324,7 @@ function resolveMSTeamsInboundAttachmentPresentation(attachments, limits) {
1282
1324
  placeholder: "",
1283
1325
  expectedMediaCount: 0
1284
1326
  };
1285
- const totalImages = fileAttachments.filter(isLikelyImageAttachment).length + inlinePlaceholderCount;
1327
+ const totalImages = fileAttachments.filter(require_resolve_allowlist.isLikelyImageAttachment).length + inlinePlaceholderCount;
1286
1328
  if (totalImages > 0) return {
1287
1329
  placeholder: `<media:image>${totalImages > 1 ? ` (${totalImages} images)` : ""}`,
1288
1330
  expectedMediaCount
@@ -1295,100 +1337,7 @@ function resolveMSTeamsInboundAttachmentPresentation(attachments, limits) {
1295
1337
  //#endregion
1296
1338
  //#region extensions/msteams/src/attachments/payload.ts
1297
1339
  function buildMSTeamsMediaPayload(mediaList) {
1298
- return buildMediaPayload(mediaList, { preserveMediaTypeCardinality: true });
1299
- }
1300
- //#endregion
1301
- //#region extensions/msteams/src/graph-thread.ts
1302
- const teamGroupIdCache = /* @__PURE__ */ new Map();
1303
- const CACHE_TTL_MS = 600 * 1e3;
1304
- function resolveTeamGroupIdCacheExpiresAt(nowRaw = Date.now()) {
1305
- const now = asDateTimestampMs(nowRaw);
1306
- return now === void 0 ? void 0 : resolveExpiresAtMsFromDurationMs(CACHE_TTL_MS, { nowMs: now });
1307
- }
1308
- /**
1309
- * Strip HTML tags from Teams message content, preserving @mention display names.
1310
- * Teams wraps mentions in <at>Name</at> tags.
1311
- */
1312
- function stripHtmlFromTeamsMessage(html) {
1313
- let text = html.replace(/<at[^>]*>(.*?)<\/at>/gi, "@$1");
1314
- text = text.replace(/<[^>]*>/g, " ");
1315
- text = text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, "\"").replace(/&#39;/g, "'").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&");
1316
- return text.replace(/\s+/g, " ").trim();
1317
- }
1318
- /**
1319
- * Resolve the Azure AD group GUID for a Teams conversation team ID.
1320
- * Results are cached with a TTL to avoid repeated Graph API calls.
1321
- */
1322
- async function resolveTeamGroupId(token, conversationTeamId) {
1323
- const cached = teamGroupIdCache.get(conversationTeamId);
1324
- if (cached) {
1325
- const now = asDateTimestampMs(Date.now());
1326
- const expiresAt = asDateTimestampMs(cached.expiresAt);
1327
- if (now !== void 0 && expiresAt !== void 0 && expiresAt > now) return cached.groupId;
1328
- teamGroupIdCache.delete(conversationTeamId);
1329
- }
1330
- try {
1331
- const groupId = (await fetchGraphJson({
1332
- token,
1333
- path: `/teams/${encodeURIComponent(conversationTeamId)}?$select=id`
1334
- })).id ?? conversationTeamId;
1335
- const expiresAt = resolveTeamGroupIdCacheExpiresAt();
1336
- if (expiresAt !== void 0) teamGroupIdCache.set(conversationTeamId, {
1337
- groupId,
1338
- expiresAt
1339
- });
1340
- return groupId;
1341
- } catch {
1342
- return conversationTeamId;
1343
- }
1344
- }
1345
- /**
1346
- * Fetch a single channel message (the parent/root of a thread).
1347
- * Returns undefined on error so callers can degrade gracefully.
1348
- */
1349
- async function fetchChannelMessage(token, groupId, channelId, messageId) {
1350
- const path = `/teams/${encodeURIComponent(groupId)}/channels/${encodeURIComponent(channelId)}/messages/${encodeURIComponent(messageId)}?$select=id,from,body,createdDateTime`;
1351
- try {
1352
- return await fetchGraphJson({
1353
- token,
1354
- path
1355
- });
1356
- } catch {
1357
- return;
1358
- }
1359
- }
1360
- /**
1361
- * Fetch thread replies for a channel message, ordered chronologically.
1362
- *
1363
- * **Limitation:** The Graph API replies endpoint (`/messages/{id}/replies`) does not
1364
- * support `$orderby`, so results are always returned in ascending (oldest-first) order.
1365
- * Combined with the `$top` cap of 50, this means only the **oldest 50 replies** are
1366
- * returned for long threads — newer replies are silently omitted. There is currently no
1367
- * Graph API workaround for this; pagination via `@odata.nextLink` can retrieve more
1368
- * replies but still in ascending order only.
1369
- */
1370
- async function fetchThreadReplies(token, groupId, channelId, messageId, limit = 50) {
1371
- return (await fetchGraphJson({
1372
- token,
1373
- path: `/teams/${encodeURIComponent(groupId)}/channels/${encodeURIComponent(channelId)}/messages/${encodeURIComponent(messageId)}/replies?$top=${Math.min(Math.max(limit, 1), 50)}&$select=id,from,body,createdDateTime`
1374
- })).value ?? [];
1375
- }
1376
- /**
1377
- * Format thread messages into a context string for the agent.
1378
- * Skips the current message (by id) and blank messages.
1379
- */
1380
- function formatThreadContext(messages, currentMessageId) {
1381
- const lines = [];
1382
- for (const msg of messages) {
1383
- if (msg.id && msg.id === currentMessageId) continue;
1384
- const sender = msg.from?.user?.displayName ?? msg.from?.application?.displayName ?? "unknown";
1385
- const contentType = msg.body?.contentType ?? "text";
1386
- const rawContent = msg.body?.content ?? "";
1387
- const content = contentType === "html" ? stripHtmlFromTeamsMessage(rawContent) : rawContent.trim();
1388
- if (!content) continue;
1389
- lines.push(`${sender}: ${content}`);
1390
- }
1391
- return lines.join("\n");
1340
+ return (0, openclaw_plugin_sdk_reply_payload.buildMediaPayload)(mediaList, { preserveMediaTypeCardinality: true });
1392
1341
  }
1393
1342
  //#endregion
1394
1343
  //#region extensions/msteams/src/thread-parent-context.ts
@@ -1409,8 +1358,8 @@ function buildParentCacheKey(groupId, channelId, parentId) {
1409
1358
  return `${groupId}\u0000${channelId}\u0000${parentId}`;
1410
1359
  }
1411
1360
  function resolveParentCacheExpiresAt(nowRaw) {
1412
- const nowMs = asDateTimestampMs(nowRaw);
1413
- return nowMs === void 0 ? void 0 : resolveExpiresAtMsFromDurationMs(PARENT_CACHE_TTL_MS, { nowMs });
1361
+ const nowMs = (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(nowRaw);
1362
+ return nowMs === void 0 ? void 0 : (0, openclaw_plugin_sdk_number_runtime.resolveExpiresAtMsFromDurationMs)(PARENT_CACHE_TTL_MS, { nowMs });
1414
1363
  }
1415
1364
  /**
1416
1365
  * Fetch a channel parent message with an LRU+TTL cache.
@@ -1418,11 +1367,11 @@ function resolveParentCacheExpiresAt(nowRaw) {
1418
1367
  * Uses the injected `fetchParent` (defaults to `fetchChannelMessage`) so
1419
1368
  * tests can swap in a stub without mocking the Graph transport.
1420
1369
  */
1421
- async function fetchParentMessageCached(token, groupId, channelId, parentId, fetchParent = fetchChannelMessage) {
1370
+ async function fetchParentMessageCached(token, groupId, channelId, parentId, fetchParent = require_messenger.fetchChannelMessage) {
1422
1371
  const key = buildParentCacheKey(groupId, channelId, parentId);
1423
- const now = asDateTimestampMs(Date.now());
1372
+ const now = (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(Date.now());
1424
1373
  const cached = parentCache.get(key);
1425
- const cachedExpiresAt = cached ? asDateTimestampMs(cached.expiresAt) : void 0;
1374
+ const cachedExpiresAt = cached ? (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(cached.expiresAt) : void 0;
1426
1375
  if (cached && now !== void 0 && cachedExpiresAt !== void 0 && cachedExpiresAt > now) {
1427
1376
  parentCache.delete(key);
1428
1377
  parentCache.set(key, cached);
@@ -1448,11 +1397,11 @@ function summarizeParentMessage(message) {
1448
1397
  const sender = message.from?.user?.displayName ?? message.from?.application?.displayName ?? "unknown";
1449
1398
  const contentType = message.body?.contentType ?? "text";
1450
1399
  const raw = message.body?.content ?? "";
1451
- const text = contentType === "html" ? stripHtmlFromTeamsMessage(raw) : raw.replace(/\s+/g, " ").trim();
1400
+ const text = contentType === "html" ? require_messenger.stripHtmlFromTeamsMessage(raw) : raw.replace(/\s+/g, " ").trim();
1452
1401
  if (!text) return;
1453
1402
  return {
1454
1403
  sender,
1455
- text: text.length > PARENT_TEXT_MAX_CHARS ? `${truncateUtf16Safe(text, PARENT_TEXT_MAX_CHARS - 1)}…` : text
1404
+ text: text.length > PARENT_TEXT_MAX_CHARS ? `${(0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(text, PARENT_TEXT_MAX_CHARS - 1)}…` : text
1456
1405
  };
1457
1406
  }
1458
1407
  /**
@@ -1501,10 +1450,10 @@ function isStreamCancelledError(err) {
1501
1450
  * block message. We bypass the controller in that case.
1502
1451
  */
1503
1452
  function createTeamsReplyStreamController(params) {
1504
- const isPersonal = normalizeOptionalLowercaseString(params.conversationType) === "personal";
1505
- const streamMode = resolveChannelPreviewStreamMode(params.msteamsConfig, "partial");
1453
+ const isPersonal = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationType) === "personal";
1454
+ const streamMode = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelPreviewStreamMode)(params.msteamsConfig, "partial");
1506
1455
  const shouldUseNativeStream = isPersonal && (streamMode === "partial" || streamMode === "progress");
1507
- const shouldStreamPreviewToolProgress = streamMode === "progress" && resolveChannelStreamingPreviewToolProgress(params.msteamsConfig);
1456
+ const shouldStreamPreviewToolProgress = streamMode === "progress" && (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingPreviewToolProgress)(params.msteamsConfig);
1508
1457
  const stream = shouldUseNativeStream ? params.context.stream : void 0;
1509
1458
  let tokensEmitted = false;
1510
1459
  let streamFinalizationPending = false;
@@ -1513,7 +1462,7 @@ function createTeamsReplyStreamController(params) {
1513
1462
  let lastInformativeText = "";
1514
1463
  let progressLines = [];
1515
1464
  let pendingFinalPayload;
1516
- let emittedTextLength = 0;
1465
+ let emittedText = "";
1517
1466
  const wasCanceled = () => canceledLocally || Boolean(stream?.canceled);
1518
1467
  const fallbackPayloadForSuppressedFinal = (payload) => {
1519
1468
  return Boolean(payload.mediaUrl || payload.mediaUrls?.length) ? {
@@ -1528,8 +1477,8 @@ function createTeamsReplyStreamController(params) {
1528
1477
  * default) and prepends collected tool-progress lines when configured.
1529
1478
  */
1530
1479
  const renderInformativeUpdate = () => {
1531
- if (!stream || wasCanceled()) return;
1532
- const informativeText = formatChannelProgressDraftText({
1480
+ if (!stream || wasCanceled() || streamFinalizationPending) return;
1481
+ const informativeText = (0, openclaw_plugin_sdk_channel_outbound.formatChannelProgressDraftText)({
1533
1482
  entry: params.msteamsConfig,
1534
1483
  lines: shouldStreamPreviewToolProgress ? progressLines : [],
1535
1484
  seed: params.progressSeed,
@@ -1547,17 +1496,26 @@ function createTeamsReplyStreamController(params) {
1547
1496
  params.log?.debug?.(`stream informative update failed: ${err instanceof Error ? err.message : String(err)}`);
1548
1497
  }
1549
1498
  };
1550
- const progressDraftGate = createChannelProgressDraftGate({ onStart: renderInformativeUpdate });
1499
+ const progressDraftGate = (0, openclaw_plugin_sdk_channel_outbound.createChannelProgressDraftGate)({ onStart: renderInformativeUpdate });
1551
1500
  return {
1552
1501
  async onReplyStart() {},
1553
1502
  onPartialReply(payload) {
1554
1503
  if (!stream || !payload.text || wasCanceled() || streamMode !== "partial" || streamFinalizationPending) return;
1555
1504
  const fullText = payload.text;
1556
- if (fullText.length <= emittedTextLength) return;
1557
- const delta = fullText.slice(emittedTextLength);
1505
+ let prefixLength = 0;
1506
+ while (prefixLength < emittedText.length && prefixLength < fullText.length && emittedText[prefixLength] === fullText[prefixLength]) prefixLength += 1;
1507
+ const previousRemainder = emittedText.slice(prefixLength);
1508
+ const delta = fullText.slice(prefixLength);
1509
+ if (!delta) return;
1510
+ if (previousRemainder.trim()) {
1511
+ stream.clearText();
1512
+ streamFailed = true;
1513
+ streamFinalizationPending = true;
1514
+ return;
1515
+ }
1558
1516
  try {
1559
1517
  stream.emit(delta);
1560
- emittedTextLength = fullText.length;
1518
+ emittedText = fullText;
1561
1519
  tokensEmitted = true;
1562
1520
  } catch (err) {
1563
1521
  if (isStreamCancelledError(err)) {
@@ -1575,7 +1533,7 @@ function createTeamsReplyStreamController(params) {
1575
1533
  */
1576
1534
  async noteProgressWork(options) {
1577
1535
  if (!stream || streamMode !== "progress") return;
1578
- if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
1536
+ if (options?.toolName !== void 0 && !(0, openclaw_plugin_sdk_channel_outbound.isChannelProgressDraftWorkToolName)(options.toolName)) return;
1579
1537
  const hadStarted = progressDraftGate.hasStarted;
1580
1538
  const progressActive = await progressDraftGate.noteWork();
1581
1539
  if ((hadStarted || progressActive) && progressDraftGate.hasStarted) renderInformativeUpdate();
@@ -1587,10 +1545,10 @@ function createTeamsReplyStreamController(params) {
1587
1545
  */
1588
1546
  async pushProgressLine(line, options) {
1589
1547
  if (!stream || streamMode !== "progress") return;
1590
- if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
1548
+ if (options?.toolName !== void 0 && !(0, openclaw_plugin_sdk_channel_outbound.isChannelProgressDraftWorkToolName)(options.toolName)) return;
1591
1549
  if (shouldStreamPreviewToolProgress) {
1592
- const normalized = normalizeChannelProgressDraftLineIdentity(line);
1593
- if (normalized) progressLines = mergeChannelProgressDraftLine(progressLines, typeof line === "object" && line !== void 0 ? line : normalized, { maxLines: resolveChannelProgressDraftMaxLines(params.msteamsConfig) });
1550
+ const normalized = (0, openclaw_plugin_sdk_channel_outbound.normalizeChannelProgressDraftLineIdentity)(line);
1551
+ if (normalized) progressLines = (0, openclaw_plugin_sdk_channel_outbound.mergeChannelProgressDraftLine)(progressLines, typeof line === "object" && line !== void 0 ? line : normalized, { maxLines: (0, openclaw_plugin_sdk_channel_outbound.resolveChannelProgressDraftMaxLines)(params.msteamsConfig) });
1594
1552
  }
1595
1553
  const hadStarted = progressDraftGate.hasStarted;
1596
1554
  const progressActive = await progressDraftGate.noteWork();
@@ -1627,6 +1585,7 @@ function createTeamsReplyStreamController(params) {
1627
1585
  return payload;
1628
1586
  },
1629
1587
  async finalize() {
1588
+ progressDraftGate.cancel();
1630
1589
  if (!stream || !streamFinalizationPending || wasCanceled()) return;
1631
1590
  const finalEntities = [{
1632
1591
  type: "https://schema.org/Message",
@@ -1678,9 +1637,9 @@ function createTeamsReplyStreamController(params) {
1678
1637
  //#endregion
1679
1638
  //#region extensions/msteams/src/reply-dispatcher.ts
1680
1639
  function createMSTeamsReplyDispatcher(params) {
1681
- const core = getMSTeamsRuntime();
1640
+ const core = require_runtime.getMSTeamsRuntime();
1682
1641
  const msteamsCfg = params.cfg.channels?.msteams;
1683
- const conversationType = normalizeOptionalLowercaseString(params.conversationRef.conversation?.conversationType);
1642
+ const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationRef.conversation?.conversationType);
1684
1643
  const isTypingSupported = conversationType === "personal" || conversationType === "groupchat";
1685
1644
  /**
1686
1645
  * Keepalive cadence for the typing indicator while the bot is running
@@ -1701,13 +1660,13 @@ function createMSTeamsReplyDispatcher(params) {
1701
1660
  const streamActiveRef = { current: () => false };
1702
1661
  const streamCanceledRef = { current: () => false };
1703
1662
  const rawSendTypingIndicator = async () => {
1704
- await withRevokedProxyFallback({
1663
+ await require_messenger.withRevokedProxyFallback({
1705
1664
  run: async () => {
1706
1665
  await params.context.sendActivity({ type: "typing" });
1707
1666
  },
1708
1667
  onRevoked: async () => {
1709
- const baseRef = buildConversationReference(params.conversationRef);
1710
- await sendMSTeamsActivityWithReference(params.app, baseRef, { type: "typing" }, { serviceUrlBoundary: resolveMSTeamsSdkCloudOptions(msteamsCfg) });
1668
+ const baseRef = require_messenger.buildConversationReference(params.conversationRef);
1669
+ await require_messenger.sendMSTeamsActivityWithReference(params.app, baseRef, { type: "typing" }, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg) });
1711
1670
  },
1712
1671
  onRevokedLog: () => {
1713
1672
  params.log.debug?.("turn context revoked, sending typing via proactive messaging");
@@ -1719,7 +1678,7 @@ function createMSTeamsReplyDispatcher(params) {
1719
1678
  if (streamCanceledRef.current()) return;
1720
1679
  await rawSendTypingIndicator();
1721
1680
  } : async () => {};
1722
- const { onModelSelected, typingCallbacks, ...replyPipeline } = createChannelMessageReplyPipeline({
1681
+ const { onModelSelected, typingCallbacks, ...replyPipeline } = (0, openclaw_plugin_sdk_channel_outbound.createChannelMessageReplyPipeline)({
1723
1682
  cfg: params.cfg,
1724
1683
  agentId: params.agentId,
1725
1684
  channel: "msteams",
@@ -1729,7 +1688,7 @@ function createMSTeamsReplyDispatcher(params) {
1729
1688
  keepaliveIntervalMs: TYPING_KEEPALIVE_INTERVAL_MS,
1730
1689
  maxDurationMs: TYPING_KEEPALIVE_MAX_DURATION_MS,
1731
1690
  onStartError: (err) => {
1732
- logTypingFailure({
1691
+ (0, openclaw_plugin_sdk_channel_outbound.logTypingFailure)({
1733
1692
  log: (message) => params.log.debug?.(message),
1734
1693
  channel: "msteams",
1735
1694
  action: "start",
@@ -1743,7 +1702,7 @@ function createMSTeamsReplyDispatcher(params) {
1743
1702
  cfg: params.cfg,
1744
1703
  channel: "msteams"
1745
1704
  });
1746
- const mediaMaxBytes = resolveChannelMediaMaxBytes({
1705
+ const mediaMaxBytes = (0, openclaw_plugin_sdk_media_runtime.resolveChannelMediaMaxBytes)({
1747
1706
  cfg: params.cfg,
1748
1707
  resolveChannelLimitMb: ({ cfg }) => cfg.channels?.msteams?.mediaMaxMb
1749
1708
  });
@@ -1758,13 +1717,13 @@ function createMSTeamsReplyDispatcher(params) {
1758
1717
  });
1759
1718
  streamActiveRef.current = () => streamController.isStreamActive();
1760
1719
  streamCanceledRef.current = () => streamController.wasCanceled();
1761
- const teamsStreamMode = resolveChannelPreviewStreamMode(msteamsCfg, "partial");
1762
- const blockStreamingResolved = teamsStreamMode === "block" ? true : resolveChannelStreamingBlockEnabled(msteamsCfg);
1720
+ const teamsStreamMode = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelPreviewStreamMode)(msteamsCfg, "partial");
1721
+ const blockStreamingResolved = teamsStreamMode === "block" ? true : (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingBlockEnabled)(msteamsCfg);
1763
1722
  const blockStreamingEnabled = blockStreamingResolved ?? false;
1764
1723
  const typingIndicatorEnabled = typeof msteamsCfg?.typingIndicator === "boolean" ? msteamsCfg.typingIndicator : true;
1765
1724
  const pendingMessages = [];
1766
1725
  const sendMessages = async (messages) => {
1767
- return sendMSTeamsMessages({
1726
+ return require_messenger.sendMSTeamsMessages({
1768
1727
  replyStyle: params.replyStyle,
1769
1728
  app: params.app,
1770
1729
  appId: params.appId,
@@ -1782,12 +1741,12 @@ function createMSTeamsReplyDispatcher(params) {
1782
1741
  sharePointSiteId: params.sharePointSiteId,
1783
1742
  mediaMaxBytes,
1784
1743
  feedbackLoopEnabled,
1785
- serviceUrlBoundary: resolveMSTeamsSdkCloudOptions(msteamsCfg)
1744
+ serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg)
1786
1745
  });
1787
1746
  };
1788
1747
  const queueDeliveryFailureSystemEvent = (failure) => {
1789
- const classification = classifyMSTeamsSendError(failure.error);
1790
- const errorText = formatUnknownError(failure.error);
1748
+ const classification = require_setup_surface.classifyMSTeamsSendError(failure.error);
1749
+ const errorText = require_setup_surface.formatUnknownError(failure.error);
1791
1750
  const failedAll = failure.failed >= failure.total;
1792
1751
  const sentences = [
1793
1752
  `Microsoft Teams delivery failed: ${failedAll ? "the previous reply was not delivered" : `${failure.failed} of ${failure.total} message blocks were not delivered`}.`,
@@ -1802,7 +1761,7 @@ function createMSTeamsReplyDispatcher(params) {
1802
1761
  });
1803
1762
  };
1804
1763
  const queueReplyPayload = (payload) => {
1805
- const messages = renderReplyPayloadsToMessages([payload], {
1764
+ const messages = require_messenger.renderReplyPayloadsToMessages([payload], {
1806
1765
  textChunkLimit: params.textLimit,
1807
1766
  chunkText: true,
1808
1767
  mediaMode: "split",
@@ -1859,9 +1818,9 @@ function createMSTeamsReplyDispatcher(params) {
1859
1818
  if (blockStreamingEnabled) await flushPendingMessages();
1860
1819
  },
1861
1820
  onError: (err, info) => {
1862
- const errMsg = formatUnknownError(err);
1863
- const classification = classifyMSTeamsSendError(err);
1864
- const hint = formatMSTeamsSendErrorHint(classification);
1821
+ const errMsg = require_setup_surface.formatUnknownError(err);
1822
+ const classification = require_setup_surface.classifyMSTeamsSendError(err);
1823
+ const hint = require_setup_surface.formatMSTeamsSendErrorHint(classification);
1865
1824
  params.runtime.error?.(`msteams ${info.kind} reply failed: ${errMsg}${hint ? ` (${hint})` : ""}`);
1866
1825
  params.log.error("reply failed", {
1867
1826
  kind: info.kind,
@@ -1873,9 +1832,9 @@ function createMSTeamsReplyDispatcher(params) {
1873
1832
  });
1874
1833
  const markDispatchIdle = () => {
1875
1834
  return flushPendingMessages().catch((err) => {
1876
- const errMsg = formatUnknownError(err);
1877
- const classification = classifyMSTeamsSendError(err);
1878
- const hint = formatMSTeamsSendErrorHint(classification);
1835
+ const errMsg = require_setup_surface.formatUnknownError(err);
1836
+ const classification = require_setup_surface.classifyMSTeamsSendError(err);
1837
+ const hint = require_setup_surface.formatMSTeamsSendErrorHint(classification);
1879
1838
  params.runtime.error?.(`msteams flush reply failed: ${errMsg}${hint ? ` (${hint})` : ""}`);
1880
1839
  params.log.error("flush reply failed", {
1881
1840
  error: errMsg,
@@ -1884,7 +1843,7 @@ function createMSTeamsReplyDispatcher(params) {
1884
1843
  });
1885
1844
  }).then(async () => {
1886
1845
  const fallbackPayload = await streamController.finalize().catch((err) => {
1887
- params.log.debug?.("stream finalize failed", { error: formatUnknownError(err) });
1846
+ params.log.debug?.("stream finalize failed", { error: require_setup_surface.formatUnknownError(err) });
1888
1847
  });
1889
1848
  if (fallbackPayload) {
1890
1849
  queueReplyPayload(fallbackPayload);
@@ -1894,8 +1853,8 @@ function createMSTeamsReplyDispatcher(params) {
1894
1853
  baseMarkDispatchIdle();
1895
1854
  });
1896
1855
  };
1897
- const previewToolProgressEnabled = resolveChannelStreamingPreviewToolProgress(msteamsCfg);
1898
- const suppressDefaultToolProgressMessages = resolveChannelStreamingSuppressDefaultToolProgressMessages(msteamsCfg);
1856
+ const previewToolProgressEnabled = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingPreviewToolProgress)(msteamsCfg);
1857
+ const suppressDefaultToolProgressMessages = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingSuppressDefaultToolProgressMessages)(msteamsCfg);
1899
1858
  const shouldSuppressDefaultToolProgressMessages = teamsStreamMode === "progress" && suppressDefaultToolProgressMessages && previewToolProgressEnabled;
1900
1859
  const progressCallbacks = streamController.hasStream() ? {
1901
1860
  onReasoningStream: async (payload) => {
@@ -1905,7 +1864,7 @@ function createMSTeamsReplyDispatcher(params) {
1905
1864
  await streamController.pushProgressLine(text);
1906
1865
  return;
1907
1866
  }
1908
- await streamController.pushProgressLine(buildChannelProgressDraftLine({
1867
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
1909
1868
  event: "item",
1910
1869
  itemId: "reasoning",
1911
1870
  itemKind: "analysis",
@@ -1916,7 +1875,7 @@ function createMSTeamsReplyDispatcher(params) {
1916
1875
  onToolStart: async (payload) => {
1917
1876
  const name = typeof payload?.name === "string" ? payload.name : void 0;
1918
1877
  const detailMode = typeof payload?.detailMode === "string" ? payload.detailMode : void 0;
1919
- await streamController.pushProgressLine(buildChannelProgressDraftLineForEntry(msteamsCfg, {
1878
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
1920
1879
  event: "tool",
1921
1880
  ...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
1922
1881
  ...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
@@ -1926,7 +1885,7 @@ function createMSTeamsReplyDispatcher(params) {
1926
1885
  }, detailMode === "explain" || detailMode === "raw" ? { detailMode } : void 0), name ? { toolName: name } : void 0);
1927
1886
  },
1928
1887
  onItemEvent: async (payload) => {
1929
- await streamController.pushProgressLine(buildChannelProgressDraftLineForEntry(msteamsCfg, {
1888
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
1930
1889
  event: "item",
1931
1890
  ...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
1932
1891
  ...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
@@ -1942,7 +1901,7 @@ function createMSTeamsReplyDispatcher(params) {
1942
1901
  },
1943
1902
  onPlanUpdate: async (payload) => {
1944
1903
  if (payload?.phase !== "update") return;
1945
- await streamController.pushProgressLine(buildChannelProgressDraftLine({
1904
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
1946
1905
  event: "plan",
1947
1906
  phase: payload.phase,
1948
1907
  ...typeof payload?.title === "string" ? { title: payload.title } : {},
@@ -1952,7 +1911,7 @@ function createMSTeamsReplyDispatcher(params) {
1952
1911
  },
1953
1912
  onApprovalEvent: async (payload) => {
1954
1913
  if (payload?.phase !== "requested") return;
1955
- await streamController.pushProgressLine(buildChannelProgressDraftLine({
1914
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
1956
1915
  event: "approval",
1957
1916
  phase: payload.phase,
1958
1917
  ...typeof payload?.title === "string" ? { title: payload.title } : {},
@@ -1963,7 +1922,7 @@ function createMSTeamsReplyDispatcher(params) {
1963
1922
  },
1964
1923
  onCommandOutput: async (payload) => {
1965
1924
  if (payload?.phase !== "end") return;
1966
- await streamController.pushProgressLine(buildChannelProgressDraftLine({
1925
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
1967
1926
  event: "command-output",
1968
1927
  ...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
1969
1928
  ...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
@@ -1976,7 +1935,7 @@ function createMSTeamsReplyDispatcher(params) {
1976
1935
  },
1977
1936
  onPatchSummary: async (payload) => {
1978
1937
  if (payload?.phase !== "end") return;
1979
- await streamController.pushProgressLine(buildChannelProgressDraftLine({
1938
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
1980
1939
  event: "patch",
1981
1940
  ...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
1982
1941
  ...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
@@ -2005,17 +1964,17 @@ function createMSTeamsReplyDispatcher(params) {
2005
1964
  }
2006
1965
  //#endregion
2007
1966
  //#region extensions/msteams/src/sent-message-cache.ts
2008
- const sentMessages = createPersistentDedupeCache({
1967
+ const sentMessages = (0, openclaw_plugin_sdk_dedupe_runtime.createPersistentDedupeCache)({
2009
1968
  globalKey: Symbol.for("openclaw.msteamsSentMessages"),
2010
1969
  ttlMs: 1440 * 60 * 1e3,
2011
1970
  maxSize: 2e4,
2012
1971
  persistent: {
2013
1972
  namespace: "msteams.sent-messages",
2014
1973
  maxEntries: 1e3,
2015
- openStore: (options) => getOptionalMSTeamsRuntime()?.state.openKeyedStore(options),
1974
+ openStore: (options) => require_runtime.getOptionalMSTeamsRuntime()?.state.openKeyedStore(options),
2016
1975
  logError: (error) => {
2017
1976
  try {
2018
- getOptionalMSTeamsRuntime()?.logging.getChildLogger({
1977
+ require_runtime.getOptionalMSTeamsRuntime()?.logging.getChildLogger({
2019
1978
  plugin: "msteams",
2020
1979
  feature: "sent-message-state"
2021
1980
  }).warn("Microsoft Teams persistent sent-message state failed", { error: String(error) });
@@ -2037,18 +1996,50 @@ async function wasMSTeamsMessageSentWithPersistence(params) {
2037
1996
  return await sentMessages.lookup(makeKey(params.conversationId, params.messageId));
2038
1997
  }
2039
1998
  //#endregion
1999
+ //#region extensions/msteams/src/team-identity.ts
2000
+ const teamGroupIdCache = /* @__PURE__ */ new Map();
2001
+ const TEAM_GROUP_ID_CACHE_MAX_ENTRIES = 500;
2002
+ function cacheTeamGroupId(conversationTeamId, groupId) {
2003
+ teamGroupIdCache.set(conversationTeamId, groupId);
2004
+ (0, openclaw_plugin_sdk_collection_runtime.pruneMapToMaxSize)(teamGroupIdCache, TEAM_GROUP_ID_CACHE_MAX_ENTRIES);
2005
+ }
2006
+ /** Resolve the Graph team GUID without ever treating a Bot Framework team ID as equivalent. */
2007
+ async function resolveTeamGroupId(params) {
2008
+ const activityGroupId = params.aadGroupId?.trim();
2009
+ if (activityGroupId) {
2010
+ cacheTeamGroupId(params.conversationTeamId, activityGroupId);
2011
+ return activityGroupId;
2012
+ }
2013
+ const cached = teamGroupIdCache.get(params.conversationTeamId);
2014
+ if (cached) return cached;
2015
+ const getTeamDetails = params.getTeamDetails;
2016
+ if (!getTeamDetails) return;
2017
+ const groupId = (await require_resolve_allowlist.withMSTeamsRequestDeadline({
2018
+ deadline: params.deadline,
2019
+ label: "MS Teams team details",
2020
+ work: () => getTeamDetails(params.conversationTeamId)
2021
+ })).aadGroupId?.trim();
2022
+ if (!groupId) return;
2023
+ cacheTeamGroupId(params.conversationTeamId, groupId);
2024
+ return groupId;
2025
+ }
2026
+ //#endregion
2040
2027
  //#region extensions/msteams/src/monitor-handler/inbound-media.ts
2028
+ function shouldAttemptMSTeamsGraphMediaFallback(params) {
2029
+ const conversationType = params.conversationType.trim().toLowerCase();
2030
+ return params.graphMediaFallback === true && (conversationType === "channel" || conversationType === "groupchat") && (params.htmlSummary?.htmlAttachments ?? 0) > 0;
2031
+ }
2041
2032
  function resolveMSTeamsInboundMediaBody(params) {
2042
2033
  const unavailableCount = Math.max(0, params.expectedMediaCount - params.mediaCount);
2043
2034
  if (unavailableCount === 0) return params.body;
2044
- return formatInboundMediaUnavailableText({
2035
+ return (0, openclaw_plugin_sdk_channel_inbound.formatInboundMediaUnavailableText)({
2045
2036
  body: params.mediaCount > 0 && params.body === params.mediaPlaceholder ? params.materializedMediaPlaceholder : params.body,
2046
2037
  mediaPlaceholder: params.mediaCount === 0 ? params.mediaPlaceholder : void 0,
2047
2038
  notice: `[msteams ${unavailableCount > 1 ? `${unavailableCount} attachments` : "attachment"} unavailable]`
2048
2039
  });
2049
2040
  }
2050
2041
  async function resolveMSTeamsInboundMedia(params) {
2051
- const { attachments, htmlSummary, maxBytes, tokenProvider, allowHosts, conversationType, conversationId, conversationMessageId, serviceUrl, activity, log, preserveFilenames } = params;
2042
+ const { attachments, htmlSummary, maxBytes, tokenProvider, allowHosts, conversationType, conversationId, conversationMessageId, teamAadGroupId, serviceUrl, activity, log, preserveFilenames } = params;
2052
2043
  let mediaList = await downloadMSTeamsAttachments({
2053
2044
  attachments,
2054
2045
  maxBytes,
@@ -2056,12 +2047,20 @@ async function resolveMSTeamsInboundMedia(params) {
2056
2047
  allowHosts,
2057
2048
  authAllowHosts: params.authAllowHosts,
2058
2049
  preserveFilenames,
2050
+ deadline: params.deadline,
2059
2051
  logger: log
2060
2052
  });
2061
2053
  if (mediaList.length === 0) {
2062
2054
  const attachmentIds = extractMSTeamsHtmlAttachmentIds(attachments);
2063
2055
  const hasHtmlFileAttachment = attachmentIds.length > 0;
2064
- if (hasHtmlFileAttachment && isBotFrameworkPersonalChatId(conversationId)) if (!serviceUrl) log.debug?.("bot framework attachment skipped (missing serviceUrl)", {
2056
+ const hasChannelOrGroupHtml = shouldAttemptMSTeamsGraphMediaFallback({
2057
+ conversationType,
2058
+ htmlSummary,
2059
+ graphMediaFallback: params.graphMediaFallback
2060
+ });
2061
+ const shouldFetchGraphMessage = hasHtmlFileAttachment || hasChannelOrGroupHtml;
2062
+ const isBotFrameworkPersonalChat = isBotFrameworkPersonalChatId(conversationId);
2063
+ if (hasHtmlFileAttachment && isBotFrameworkPersonalChat) if (!serviceUrl) log.debug?.("bot framework attachment skipped (missing serviceUrl)", {
2065
2064
  conversationType,
2066
2065
  conversationId
2067
2066
  });
@@ -2073,7 +2072,8 @@ async function resolveMSTeamsInboundMedia(params) {
2073
2072
  maxBytes,
2074
2073
  allowHosts,
2075
2074
  authAllowHosts: params.authAllowHosts,
2076
- preserveFilenames
2075
+ preserveFilenames,
2076
+ deadline: params.deadline
2077
2077
  });
2078
2078
  if (bfMedia.media.length > 0) mediaList = bfMedia.media;
2079
2079
  else log.debug?.("bot framework attachments fetch empty", {
@@ -2081,50 +2081,41 @@ async function resolveMSTeamsInboundMedia(params) {
2081
2081
  attachmentCount: bfMedia.attachmentCount ?? attachmentIds.length
2082
2082
  });
2083
2083
  }
2084
- if (hasHtmlFileAttachment && mediaList.length === 0 && !isBotFrameworkPersonalChatId(conversationId)) {
2085
- const messageUrls = buildMSTeamsGraphMessageUrls({
2084
+ if (shouldFetchGraphMessage && mediaList.length === 0 && !isBotFrameworkPersonalChat) {
2085
+ const graphTeamAadGroupId = conversationType.trim().toLowerCase() === "channel" && !teamAadGroupId ? await params.resolveTeamAadGroupId?.() : teamAadGroupId;
2086
+ const messageUrl = buildMSTeamsGraphMessageUrl({
2086
2087
  conversationType,
2087
2088
  conversationId,
2088
2089
  messageId: activity.id ?? void 0,
2089
- replyToId: activity.replyToId ?? void 0,
2090
- conversationMessageId,
2091
- channelData: activity.channelData
2090
+ threadRootMessageId: conversationMessageId ?? activity.replyToId,
2091
+ teamAadGroupId: graphTeamAadGroupId,
2092
+ channelId: activity.channelData?.channel?.id
2092
2093
  });
2093
- if (messageUrls.length === 0) log.debug?.("graph message url unavailable", {
2094
+ if (!messageUrl) log.debug?.("graph message url unavailable", {
2094
2095
  conversationType,
2095
2096
  hasChannelData: Boolean(activity.channelData),
2096
2097
  messageId: activity.id ?? void 0,
2097
2098
  replyToId: activity.replyToId ?? void 0
2098
2099
  });
2099
2100
  else {
2100
- const attempts = [];
2101
- for (const messageUrl of messageUrls) {
2102
- const graphMedia = await downloadMSTeamsGraphMedia({
2103
- messageUrl,
2104
- tokenProvider,
2105
- maxBytes,
2106
- allowHosts,
2107
- authAllowHosts: params.authAllowHosts,
2108
- preserveFilenames,
2109
- log,
2110
- logger: log
2111
- });
2112
- attempts.push({
2113
- url: messageUrl,
2114
- hostedStatus: graphMedia.hostedStatus,
2115
- attachmentStatus: graphMedia.attachmentStatus,
2116
- hostedCount: graphMedia.hostedCount,
2117
- attachmentCount: graphMedia.attachmentCount,
2118
- tokenError: graphMedia.tokenError
2119
- });
2120
- if (graphMedia.media.length > 0) {
2121
- mediaList = graphMedia.media;
2122
- break;
2123
- }
2124
- if (graphMedia.tokenError) break;
2125
- }
2101
+ const graphMedia = await downloadMSTeamsGraphMedia({
2102
+ messageUrl,
2103
+ tokenProvider,
2104
+ maxBytes,
2105
+ allowHosts,
2106
+ authAllowHosts: params.authAllowHosts,
2107
+ preserveFilenames,
2108
+ deadline: params.deadline,
2109
+ logger: log
2110
+ });
2111
+ if (graphMedia.media.length > 0) mediaList = graphMedia.media;
2126
2112
  if (mediaList.length === 0) log.debug?.("graph media fetch empty", {
2127
- attempts,
2113
+ messageUrl,
2114
+ hostedStatus: graphMedia.hostedStatus,
2115
+ attachmentStatus: graphMedia.attachmentStatus,
2116
+ hostedCount: graphMedia.hostedCount,
2117
+ attachmentCount: graphMedia.attachmentCount,
2118
+ tokenError: graphMedia.tokenError,
2128
2119
  attachmentIdCount: attachmentIds.length
2129
2120
  });
2130
2121
  }
@@ -2143,9 +2134,7 @@ async function resolveMSTeamsInboundMedia(params) {
2143
2134
  //#region extensions/msteams/src/monitor-handler/message-handler.ts
2144
2135
  function extractTextFromHtmlAttachments(attachments) {
2145
2136
  for (const attachment of attachments) {
2146
- if (attachment.contentType !== "text/html") continue;
2147
- const content = attachment.content;
2148
- const raw = typeof content === "string" ? content : isRecord$1(content) && typeof content.text === "string" ? content.text : isRecord$1(content) && typeof content.body === "string" ? content.body : "";
2137
+ const raw = require_resolve_allowlist.extractHtmlFromAttachment(attachment);
2149
2138
  if (!raw) continue;
2150
2139
  const text = raw.replace(/<at[^>]*>.*?<\/at>/gis, " ").replace(/<a\b[^>]*href=["']([^"']+)["'][^>]*>(.*?)<\/a>/gis, "$2 $1").replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<[^>]+>/g, " ").replace(/&nbsp;/gi, " ").replace(/&amp;/gi, "&").replace(/\s+/g, " ").trim();
2151
2140
  if (text) return text;
@@ -2176,7 +2165,7 @@ function buildStoredConversationReference(params) {
2176
2165
  const clientInfo = activity.entities?.find((e) => e.type === "clientInfo");
2177
2166
  const tenantId = activity.channelData?.tenant?.id ?? conversation?.tenantId;
2178
2167
  const aadObjectId = from?.aadObjectId;
2179
- const serviceUrl = tryNormalizeBotFrameworkServiceUrl(activity.serviceUrl);
2168
+ const serviceUrl = require_resolve_allowlist.tryNormalizeBotFrameworkServiceUrl(activity.serviceUrl);
2180
2169
  return {
2181
2170
  activityId: activity.id,
2182
2171
  user: from ? {
@@ -2185,10 +2174,6 @@ function buildStoredConversationReference(params) {
2185
2174
  aadObjectId: from.aadObjectId
2186
2175
  } : void 0,
2187
2176
  agent,
2188
- bot: agent ? {
2189
- id: agent.id,
2190
- name: agent.name
2191
- } : void 0,
2192
2177
  conversation: {
2193
2178
  id: conversationId,
2194
2179
  conversationType,
@@ -2206,16 +2191,16 @@ function buildStoredConversationReference(params) {
2206
2191
  }
2207
2192
  function createMSTeamsMessageHandler(deps) {
2208
2193
  const { cfg, runtime, appId, app, tokenProvider, textLimit, mediaMaxBytes, conversationStore, pollStore, log } = deps;
2209
- const core = getMSTeamsRuntime();
2194
+ const core = require_runtime.getMSTeamsRuntime();
2210
2195
  const logVerboseMessage = (message) => {
2211
2196
  if (core.logging.shouldLogVerbose()) log.debug?.(message);
2212
2197
  };
2213
2198
  const msteamsCfg = cfg.channels?.msteams;
2214
- const contextVisibilityMode = resolveChannelContextVisibilityMode({
2199
+ const contextVisibilityMode = (0, openclaw_plugin_sdk_context_visibility_runtime.resolveChannelContextVisibilityMode)({
2215
2200
  cfg,
2216
2201
  channel: "msteams"
2217
2202
  });
2218
- const historyLimit = Math.max(0, msteamsCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT);
2203
+ const historyLimit = Math.max(0, msteamsCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? openclaw_plugin_sdk_reply_history.DEFAULT_GROUP_HISTORY_LIMIT);
2219
2204
  const conversationHistories = /* @__PURE__ */ new Map();
2220
2205
  const inboundDebounceMs = core.channel.debounce.resolveInboundDebounceMs({
2221
2206
  cfg,
@@ -2233,7 +2218,7 @@ function createMSTeamsMessageHandler(deps) {
2233
2218
  });
2234
2219
  const attachmentPlaceholder = attachmentPresentation.placeholder;
2235
2220
  const rawBody = text || attachmentPlaceholder;
2236
- const quoteInfo = extractMSTeamsQuoteInfo(attachments);
2221
+ const quoteInfo = require_channel.extractMSTeamsQuoteInfo(attachments);
2237
2222
  let quoteSenderId;
2238
2223
  let quoteSenderName;
2239
2224
  const from = activity.from;
@@ -2241,8 +2226,8 @@ function createMSTeamsMessageHandler(deps) {
2241
2226
  const attachmentTypes = attachments.map((att) => typeof att.contentType === "string" ? att.contentType : void 0).filter(Boolean).slice(0, 3);
2242
2227
  const htmlSummary = summarizeMSTeamsHtmlAttachments(attachments);
2243
2228
  log.info("received message", {
2244
- rawText: truncateUtf16Safe(rawText, 50),
2245
- text: truncateUtf16Safe(text, 50),
2229
+ rawText: (0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(rawText, 50),
2230
+ text: (0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(text, 50),
2246
2231
  attachments: attachments.length,
2247
2232
  attachmentTypes,
2248
2233
  from: from?.id,
@@ -2254,10 +2239,11 @@ function createMSTeamsMessageHandler(deps) {
2254
2239
  return;
2255
2240
  }
2256
2241
  const rawConversationId = conversation?.id ?? "";
2257
- const conversationId = normalizeMSTeamsConversationId(rawConversationId);
2258
- const conversationMessageId = extractMSTeamsConversationMessageId(rawConversationId);
2242
+ const conversationId = require_channel.normalizeMSTeamsConversationId(rawConversationId);
2243
+ const conversationMessageId = require_channel.extractMSTeamsConversationMessageId(rawConversationId);
2259
2244
  const conversationType = conversation?.conversationType ?? "personal";
2260
2245
  const teamId = activity.channelData?.team?.id;
2246
+ const graphChannelId = activity.channelData?.channel?.id?.trim() || conversationId;
2261
2247
  const conversationRef = buildStoredConversationReference({
2262
2248
  activity,
2263
2249
  conversationId,
@@ -2288,7 +2274,7 @@ function createMSTeamsMessageHandler(deps) {
2288
2274
  log.debug?.("dropping dm (dms disabled)");
2289
2275
  return;
2290
2276
  }
2291
- const allowMatch = resolveMSTeamsAllowlistMatch({
2277
+ const allowMatch = require_channel.resolveMSTeamsAllowlistMatch({
2292
2278
  allowFrom: effectiveDmAllowFrom,
2293
2279
  senderId,
2294
2280
  senderName,
@@ -2296,7 +2282,7 @@ function createMSTeamsMessageHandler(deps) {
2296
2282
  });
2297
2283
  if (senderAccess.decision === "pairing") {
2298
2284
  conversationStore.upsert(conversationId, conversationRef).catch((err) => {
2299
- log.debug?.("failed to save conversation reference", { error: formatUnknownError(err) });
2285
+ log.debug?.("failed to save conversation reference", { error: require_setup_surface.formatUnknownError(err) });
2300
2286
  });
2301
2287
  if (await pairing.upsertPairingRequest({
2302
2288
  id: senderId,
@@ -2309,7 +2295,7 @@ function createMSTeamsMessageHandler(deps) {
2309
2295
  log.debug?.("dropping dm (not allowlisted)", {
2310
2296
  sender: senderId,
2311
2297
  label: senderName,
2312
- allowlistMatch: formatAllowlistMatchMeta(allowMatch)
2298
+ allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
2313
2299
  });
2314
2300
  log.info("dropping dm (not allowlisted)", {
2315
2301
  sender: senderId,
@@ -2320,7 +2306,7 @@ function createMSTeamsMessageHandler(deps) {
2320
2306
  dmPolicy,
2321
2307
  groupPolicy
2322
2308
  }),
2323
- allowlistMatch: formatAllowlistMatchMeta(allowMatch)
2309
+ allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
2324
2310
  });
2325
2311
  return;
2326
2312
  }
@@ -2353,7 +2339,7 @@ function createMSTeamsMessageHandler(deps) {
2353
2339
  return;
2354
2340
  }
2355
2341
  if (!senderAccess.allowed && senderAccess.reasonCode === "group_policy_not_allowlisted") {
2356
- const allowMatch = resolveMSTeamsAllowlistMatch({
2342
+ const allowMatch = require_channel.resolveMSTeamsAllowlistMatch({
2357
2343
  allowFrom: effectiveGroupAllowFrom,
2358
2344
  senderId,
2359
2345
  senderName,
@@ -2362,18 +2348,18 @@ function createMSTeamsMessageHandler(deps) {
2362
2348
  log.debug?.("dropping group message (not in groupAllowFrom)", {
2363
2349
  sender: senderId,
2364
2350
  label: senderName,
2365
- allowlistMatch: formatAllowlistMatchMeta(allowMatch)
2351
+ allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
2366
2352
  });
2367
2353
  log.info("dropping group message (not in groupAllowFrom)", {
2368
2354
  sender: senderId,
2369
2355
  label: senderName,
2370
- allowlistMatch: formatAllowlistMatchMeta(allowMatch)
2356
+ allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
2371
2357
  });
2372
2358
  return;
2373
2359
  }
2374
2360
  }
2375
2361
  if (commandAccess.shouldBlockControlCommand) {
2376
- logInboundDrop({
2362
+ (0, openclaw_plugin_sdk_channel_inbound.logInboundDrop)({
2377
2363
  log: logVerboseMessage,
2378
2364
  channel: "msteams",
2379
2365
  reason: "control command (unauthorized)",
@@ -2382,9 +2368,9 @@ function createMSTeamsMessageHandler(deps) {
2382
2368
  return;
2383
2369
  }
2384
2370
  conversationStore.upsert(conversationId, conversationRef).catch((err) => {
2385
- log.debug?.("failed to save conversation reference", { error: formatUnknownError(err) });
2371
+ log.debug?.("failed to save conversation reference", { error: require_setup_surface.formatUnknownError(err) });
2386
2372
  });
2387
- const pollVote = extractMSTeamsPollVote(activity);
2373
+ const pollVote = require_polls.extractMSTeamsPollVote(activity);
2388
2374
  if (pollVote) {
2389
2375
  try {
2390
2376
  if (!await pollStore.recordVote({
@@ -2400,12 +2386,17 @@ function createMSTeamsMessageHandler(deps) {
2400
2386
  } catch (err) {
2401
2387
  log.error("failed to record poll vote", {
2402
2388
  pollId: pollVote.pollId,
2403
- error: formatUnknownError(err)
2389
+ error: require_setup_surface.formatUnknownError(err)
2404
2390
  });
2405
2391
  }
2406
2392
  return;
2407
2393
  }
2408
- if (!rawBody) {
2394
+ const mayRecoverGraphMedia = Boolean(htmlSummary?.attachmentIds.length) || shouldAttemptMSTeamsGraphMediaFallback({
2395
+ conversationType,
2396
+ htmlSummary: htmlSummary ?? void 0,
2397
+ graphMediaFallback: msteamsCfg?.graphMediaFallback
2398
+ });
2399
+ if (!rawBody && !mayRecoverGraphMedia) {
2409
2400
  log.debug?.("skipping empty message after stripping mentions");
2410
2401
  return;
2411
2402
  }
@@ -2420,13 +2411,13 @@ function createMSTeamsMessageHandler(deps) {
2420
2411
  id: isDirectMessage ? senderId : conversationId
2421
2412
  }
2422
2413
  });
2423
- route.sessionKey = resolveMSTeamsRouteSessionKey({
2414
+ route.sessionKey = require_channel.resolveMSTeamsRouteSessionKey({
2424
2415
  baseSessionKey: route.sessionKey,
2425
2416
  isChannel,
2426
2417
  conversationMessageId,
2427
2418
  replyToId: activity.replyToId
2428
2419
  });
2429
- const preview = sliceUtf16Safe(rawBody.replace(/\s+/g, " "), 0, 160);
2420
+ const preview = (0, openclaw_plugin_sdk_text_utility_runtime.sliceUtf16Safe)(rawBody.replace(/\s+/g, " "), 0, 160);
2430
2421
  const inboundLabel = isDirectMessage ? `Teams DM from ${senderName}` : `Teams message in ${conversationType} from ${senderName}`;
2431
2422
  const enqueuePrimaryMessageSystemEvent = () => core.system.enqueueSystemEvent(inboundLabel, {
2432
2423
  sessionKey: route.sessionKey,
@@ -2434,14 +2425,14 @@ function createMSTeamsMessageHandler(deps) {
2434
2425
  });
2435
2426
  const channelId = conversationId;
2436
2427
  const { teamConfig, channelConfig } = channelGate;
2437
- const { requireMention, replyStyle } = resolveMSTeamsReplyPolicy({
2428
+ const { requireMention, replyStyle } = require_channel.resolveMSTeamsReplyPolicy({
2438
2429
  isDirectMessage,
2439
2430
  globalConfig: msteamsCfg,
2440
2431
  teamConfig,
2441
2432
  channelConfig
2442
2433
  });
2443
- const timestamp = parseMSTeamsActivityTimestamp(activity.timestamp);
2444
- const mentionDecision = resolveInboundMentionDecision({
2434
+ const timestamp = require_channel.parseMSTeamsActivityTimestamp(activity.timestamp);
2435
+ const mentionDecision = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundMentionDecision)({
2445
2436
  facts: {
2446
2437
  canDetectMention: true,
2447
2438
  wasMentioned: params.wasMentioned,
@@ -2464,124 +2455,163 @@ function createMSTeamsMessageHandler(deps) {
2464
2455
  requireMention,
2465
2456
  mentioned
2466
2457
  });
2467
- enqueuePrimaryMessageSystemEvent();
2468
- createChannelHistoryWindow({ historyMap: conversationHistories }).record({
2469
- historyKey: conversationId,
2470
- limit: historyLimit,
2471
- entry: {
2472
- sender: senderName,
2473
- body: rawBody,
2474
- timestamp: timestamp?.getTime(),
2475
- messageId: activity.id ?? void 0
2476
- }
2477
- });
2458
+ if (rawBody) {
2459
+ enqueuePrimaryMessageSystemEvent();
2460
+ (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).record({
2461
+ historyKey: conversationId,
2462
+ limit: historyLimit,
2463
+ entry: {
2464
+ sender: senderName,
2465
+ body: rawBody,
2466
+ timestamp: timestamp?.getTime(),
2467
+ messageId: activity.id ?? void 0
2468
+ }
2469
+ });
2470
+ }
2478
2471
  return;
2479
2472
  }
2480
2473
  }
2481
- enqueuePrimaryMessageSystemEvent();
2482
- let graphConversationId = translateMSTeamsDmConversationIdForGraph({
2483
- isDirectMessage,
2484
- conversationId,
2485
- aadObjectId: from.aadObjectId,
2486
- appId
2487
- });
2488
- if (isDirectMessage && conversationId.startsWith("a:")) {
2489
- const cached = await conversationStore.get(conversationId);
2490
- if (cached?.graphChatId) graphConversationId = cached.graphChatId;
2491
- else try {
2492
- const resolved = await resolveGraphChatId({
2493
- botFrameworkConversationId: conversationId,
2494
- userAadObjectId: from.aadObjectId ?? void 0,
2495
- tokenProvider
2474
+ const preprocessingDeadline = require_resolve_allowlist.createMSTeamsInboundDeadline();
2475
+ let teamAadGroupId = activity.channelData?.team?.aadGroupId?.trim() || void 0;
2476
+ const conversationTeamId = isChannel ? teamId : void 0;
2477
+ let teamGroupIdPromise;
2478
+ const resolveChannelTeamGroupId = async () => {
2479
+ if (!conversationTeamId) return;
2480
+ teamGroupIdPromise ??= resolveTeamGroupId({
2481
+ conversationTeamId,
2482
+ aadGroupId: teamAadGroupId,
2483
+ getTeamDetails: context.getTeamDetails,
2484
+ deadline: preprocessingDeadline
2485
+ }).catch((err) => {
2486
+ log.debug?.("failed to resolve Teams AAD group ID", {
2487
+ teamId: conversationTeamId,
2488
+ error: require_setup_surface.formatUnknownError(err)
2496
2489
  });
2497
- if (resolved) {
2498
- graphConversationId = resolved;
2499
- conversationStore.upsert(conversationId, {
2500
- ...conversationRef,
2501
- graphChatId: resolved
2502
- }).catch(() => {});
2503
- }
2504
- } catch {
2505
- log.debug?.("failed to resolve Graph chat ID for inbound media", { conversationId });
2506
- }
2490
+ });
2491
+ teamAadGroupId = await teamGroupIdPromise;
2492
+ return teamAadGroupId;
2493
+ };
2494
+ let mediaList = [];
2495
+ try {
2496
+ mediaList = await require_resolve_allowlist.withMSTeamsRequestDeadline({
2497
+ deadline: preprocessingDeadline,
2498
+ label: "MS Teams inbound media",
2499
+ work: () => resolveMSTeamsInboundMedia({
2500
+ attachments,
2501
+ htmlSummary: htmlSummary ?? void 0,
2502
+ maxBytes: mediaMaxBytes,
2503
+ tokenProvider,
2504
+ allowHosts: msteamsCfg?.mediaAllowHosts,
2505
+ authAllowHosts: msteamsCfg?.mediaAuthAllowHosts,
2506
+ graphMediaFallback: msteamsCfg?.graphMediaFallback,
2507
+ conversationType,
2508
+ conversationId,
2509
+ conversationMessageId: conversationMessageId ?? void 0,
2510
+ teamAadGroupId,
2511
+ resolveTeamAadGroupId: resolveChannelTeamGroupId,
2512
+ serviceUrl: activity.serviceUrl,
2513
+ activity: {
2514
+ id: activity.id,
2515
+ replyToId: activity.replyToId,
2516
+ channelData: activity.channelData
2517
+ },
2518
+ log,
2519
+ deadline: preprocessingDeadline,
2520
+ preserveFilenames: cfg.media?.preserveFilenames
2521
+ })
2522
+ });
2523
+ } catch (err) {
2524
+ log.debug?.("failed to resolve inbound Teams media", { error: require_setup_surface.formatUnknownError(err) });
2507
2525
  }
2508
- const mediaList = await resolveMSTeamsInboundMedia({
2509
- attachments,
2510
- htmlSummary: htmlSummary ?? void 0,
2511
- maxBytes: mediaMaxBytes,
2512
- tokenProvider,
2513
- allowHosts: msteamsCfg?.mediaAllowHosts,
2514
- authAllowHosts: msteamsCfg?.mediaAuthAllowHosts,
2515
- conversationType,
2516
- conversationId: graphConversationId,
2517
- conversationMessageId: conversationMessageId ?? void 0,
2518
- serviceUrl: activity.serviceUrl,
2519
- activity: {
2520
- id: activity.id,
2521
- replyToId: activity.replyToId,
2522
- channelData: activity.channelData
2523
- },
2524
- log,
2525
- preserveFilenames: cfg.media?.preserveFilenames
2526
- });
2527
2526
  const mediaPayload = buildMSTeamsMediaPayload(mediaList);
2528
2527
  const materializedMediaPlaceholder = resolveMSTeamsInboundAttachmentPresentation(mediaList.map((media) => ({
2529
2528
  contentType: media.contentType,
2530
2529
  name: media.path
2531
2530
  }))).placeholder;
2532
2531
  const agentBody = resolveMSTeamsInboundMediaBody({
2533
- body: rawBody,
2532
+ body: rawBody || materializedMediaPlaceholder,
2534
2533
  mediaPlaceholder: attachmentPlaceholder,
2535
2534
  materializedMediaPlaceholder,
2536
2535
  expectedMediaCount: attachmentPresentation.expectedMediaCount,
2537
2536
  mediaCount: mediaList.length
2538
2537
  });
2538
+ if (!agentBody) {
2539
+ log.debug?.("skipping empty message after Graph media recovery");
2540
+ return;
2541
+ }
2542
+ enqueuePrimaryMessageSystemEvent();
2543
+ teamAadGroupId = await resolveChannelTeamGroupId();
2544
+ let quoteBodyFull;
2545
+ const quoteMessageId = quoteInfo?.id;
2546
+ if (quoteMessageId && isDirectMessage && conversationId.startsWith("19:")) try {
2547
+ const graphToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
2548
+ deadline: preprocessingDeadline,
2549
+ label: "MS Teams quote token",
2550
+ work: () => tokenProvider.getAccessToken("https://graph.microsoft.com")
2551
+ });
2552
+ quoteBodyFull = await require_resolve_allowlist.withMSTeamsRequestDeadline({
2553
+ deadline: preprocessingDeadline,
2554
+ label: "MS Teams quote lookup",
2555
+ work: () => require_messenger.fetchChatMessageText(graphToken, conversationId, quoteMessageId, preprocessingDeadline)
2556
+ });
2557
+ } catch (err) {
2558
+ log.debug?.("failed to fetch full quoted message text", { error: require_setup_surface.formatUnknownError(err) });
2559
+ }
2539
2560
  let threadContext;
2540
- if (activity.replyToId && isChannel && teamId) try {
2541
- const graphToken = await tokenProvider.getAccessToken("https://graph.microsoft.com");
2542
- const groupId = await resolveTeamGroupId(graphToken, teamId);
2543
- const [parentResult, repliesResult] = await Promise.allSettled([fetchParentMessageCached(graphToken, groupId, conversationId, activity.replyToId), fetchThreadReplies(graphToken, groupId, conversationId, activity.replyToId)]);
2561
+ const threadParentId = activity.replyToId;
2562
+ const channelGroupId = teamAadGroupId;
2563
+ if (threadParentId && isChannel && channelGroupId) try {
2564
+ const graphToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
2565
+ deadline: preprocessingDeadline,
2566
+ label: "MS Teams thread token",
2567
+ work: () => tokenProvider.getAccessToken("https://graph.microsoft.com")
2568
+ });
2569
+ const [parentResult, repliesResult] = await require_resolve_allowlist.withMSTeamsRequestDeadline({
2570
+ deadline: preprocessingDeadline,
2571
+ label: "MS Teams thread history",
2572
+ work: () => Promise.allSettled([fetchParentMessageCached(graphToken, channelGroupId, conversationId, threadParentId, (token, groupId, requestedChannelId, messageId) => require_messenger.fetchChannelMessage(token, groupId, requestedChannelId, messageId, preprocessingDeadline)), require_messenger.fetchThreadReplies(graphToken, channelGroupId, conversationId, threadParentId, 50, preprocessingDeadline)])
2573
+ });
2544
2574
  const parentMsg = parentResult.status === "fulfilled" ? parentResult.value : void 0;
2545
2575
  const replies = repliesResult.status === "fulfilled" ? repliesResult.value : [];
2546
- if (parentResult.status === "rejected") log.debug?.("failed to fetch parent message", { error: formatUnknownError(parentResult.reason) });
2547
- if (repliesResult.status === "rejected") log.debug?.("failed to fetch thread replies", { error: formatUnknownError(repliesResult.reason) });
2548
- const isThreadSenderAllowed = (msg) => groupPolicy === "allowlist" ? resolveMSTeamsAllowlistMatch({
2576
+ if (parentResult.status === "rejected") log.debug?.("failed to fetch parent message", { error: require_setup_surface.formatUnknownError(parentResult.reason) });
2577
+ if (repliesResult.status === "rejected") log.debug?.("failed to fetch thread replies", { error: require_setup_surface.formatUnknownError(repliesResult.reason) });
2578
+ const isThreadSenderAllowed = (msg) => groupPolicy === "allowlist" ? require_channel.resolveMSTeamsAllowlistMatch({
2549
2579
  allowFrom: effectiveGroupAllowFrom,
2550
2580
  senderId: msg.from?.user?.id ?? "",
2551
2581
  senderName: msg.from?.user?.displayName,
2552
2582
  allowNameMatching
2553
2583
  }).allowed : true;
2554
2584
  const parentSummary = summarizeParentMessage(parentMsg);
2555
- const visibleParentMessages = parentMsg ? filterSupplementalContextItems({
2585
+ const visibleParentMessages = parentMsg ? (0, openclaw_plugin_sdk_context_visibility_runtime.filterSupplementalContextItems)({
2556
2586
  items: [parentMsg],
2557
2587
  mode: contextVisibilityMode,
2558
2588
  kind: "thread",
2559
2589
  isSenderAllowed: isThreadSenderAllowed
2560
2590
  }).items : [];
2561
- if (parentSummary && visibleParentMessages.length > 0 && shouldInjectParentContext(route.sessionKey, activity.replyToId)) {
2591
+ if (parentSummary && visibleParentMessages.length > 0 && shouldInjectParentContext(route.sessionKey, threadParentId)) {
2562
2592
  core.system.enqueueSystemEvent(formatParentContextEvent(parentSummary), {
2563
2593
  sessionKey: route.sessionKey,
2564
- contextKey: `msteams:thread-parent:${conversationId}:${activity.replyToId}`
2594
+ contextKey: `msteams:thread-parent:${conversationId}:${threadParentId}`
2565
2595
  });
2566
- markParentContextInjected(route.sessionKey, activity.replyToId);
2596
+ markParentContextInjected(route.sessionKey, threadParentId);
2567
2597
  }
2568
2598
  const allMessages = parentMsg ? [parentMsg, ...replies] : replies;
2569
2599
  quoteSenderId = parentMsg?.from?.user?.id ?? parentMsg?.from?.application?.id ?? void 0;
2570
2600
  quoteSenderName = parentMsg?.from?.user?.displayName ?? parentMsg?.from?.application?.displayName ?? quoteInfo?.sender;
2571
- const { items: threadMessages } = filterSupplementalContextItems({
2601
+ const { items: threadMessages } = (0, openclaw_plugin_sdk_context_visibility_runtime.filterSupplementalContextItems)({
2572
2602
  items: allMessages,
2573
2603
  mode: contextVisibilityMode,
2574
2604
  kind: "thread",
2575
2605
  isSenderAllowed: isThreadSenderAllowed
2576
2606
  });
2577
- const formatted = formatThreadContext(threadMessages, activity.id);
2607
+ const formatted = require_messenger.formatThreadContext(threadMessages, activity.id);
2578
2608
  if (formatted) threadContext = formatted;
2579
2609
  } catch (err) {
2580
- log.debug?.("failed to fetch thread history", { error: formatUnknownError(err) });
2610
+ log.debug?.("failed to fetch thread history", { error: require_setup_surface.formatUnknownError(err) });
2581
2611
  }
2582
2612
  quoteSenderName ??= quoteInfo?.sender;
2583
2613
  const envelopeFrom = isDirectMessage ? senderName : conversationType;
2584
- const { storePath, envelopeOptions, previousTimestamp } = resolveInboundSessionEnvelopeContext({
2614
+ const { storePath, envelopeOptions, previousTimestamp } = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundSessionEnvelopeContext)({
2585
2615
  cfg,
2586
2616
  agentId: route.agentId,
2587
2617
  sessionKey: route.sessionKey
@@ -2596,7 +2626,7 @@ function createMSTeamsMessageHandler(deps) {
2596
2626
  });
2597
2627
  const isRoomish = !isDirectMessage;
2598
2628
  const historyKey = isRoomish ? conversationId : void 0;
2599
- if (isRoomish && historyKey) combinedBody = createChannelHistoryWindow({ historyMap: conversationHistories }).buildPendingContext({
2629
+ if (isRoomish && historyKey) combinedBody = (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).buildPendingContext({
2600
2630
  historyKey,
2601
2631
  limit: historyLimit,
2602
2632
  currentMessage: combinedBody,
@@ -2608,26 +2638,26 @@ function createMSTeamsMessageHandler(deps) {
2608
2638
  envelope: envelopeOptions
2609
2639
  })
2610
2640
  });
2611
- const inboundHistory = isRoomish && historyKey && historyLimit > 0 ? createChannelHistoryWindow({ historyMap: conversationHistories }).buildInboundHistory({
2641
+ const inboundHistory = isRoomish && historyKey && historyLimit > 0 ? (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).buildInboundHistory({
2612
2642
  historyKey,
2613
2643
  limit: historyLimit
2614
2644
  }) : void 0;
2615
2645
  const commandBody = text.trim();
2616
- const quoteSenderAllowed = quoteInfo && quoteInfo.sender ? !isChannel || groupPolicy !== "allowlist" ? true : resolveMSTeamsAllowlistMatch({
2646
+ const quoteSenderAllowed = quoteInfo && quoteInfo.sender ? !isChannel || groupPolicy !== "allowlist" ? true : require_channel.resolveMSTeamsAllowlistMatch({
2617
2647
  allowFrom: effectiveGroupAllowFrom,
2618
2648
  senderId: quoteSenderId ?? "",
2619
2649
  senderName: quoteSenderName,
2620
2650
  allowNameMatching
2621
2651
  }).allowed : true;
2622
2652
  const bodyForAgent = threadContext ? `[Thread history]\n${threadContext}\n[/Thread history]\n\n${agentBody}` : agentBody;
2623
- const nativeChannelId = isChannel && teamId ? `${teamId}/${conversationId}` : void 0;
2624
- const ctxPayload = buildChannelInboundEventContext({
2653
+ const nativeChannelId = isChannel && teamAadGroupId ? `${teamAadGroupId}/${graphChannelId}` : void 0;
2654
+ const ctxPayload = (0, openclaw_plugin_sdk_channel_inbound.buildChannelInboundEventContext)({
2625
2655
  channel: "msteams",
2626
2656
  finalize: core.channel.reply.finalizeInboundContext,
2627
2657
  contextVisibility: contextVisibilityMode,
2628
2658
  supplemental: { quote: quoteInfo ? {
2629
- id: activity.replyToId ?? void 0,
2630
- body: quoteInfo.body,
2659
+ id: quoteInfo.id ?? activity.replyToId ?? void 0,
2660
+ body: quoteBodyFull ?? quoteInfo.body,
2631
2661
  sender: quoteInfo.sender,
2632
2662
  senderAllowed: quoteSenderAllowed,
2633
2663
  isQuote: true
@@ -2725,7 +2755,7 @@ function createMSTeamsMessageHandler(deps) {
2725
2755
  ctxPayload,
2726
2756
  recordInboundSession: core.channel.session.recordInboundSession,
2727
2757
  record: { onRecordError: (err) => {
2728
- logVerboseMessage(`msteams: failed updating session meta: ${formatUnknownError(err)}`);
2758
+ logVerboseMessage(`msteams: failed updating session meta: ${require_setup_surface.formatUnknownError(err)}`);
2729
2759
  } },
2730
2760
  history: {
2731
2761
  isGroup: isRoomish,
@@ -2737,7 +2767,7 @@ function createMSTeamsMessageHandler(deps) {
2737
2767
  dispatcher,
2738
2768
  onSettled: () => markDispatchIdle()
2739
2769
  }),
2740
- runDispatch: () => dispatchReplyFromConfigWithSettledDispatcher({
2770
+ runDispatch: () => (0, openclaw_plugin_sdk_channel_inbound.dispatchReplyFromConfigWithSettledDispatcher)({
2741
2771
  cfg,
2742
2772
  ctxPayload,
2743
2773
  dispatcher,
@@ -2750,8 +2780,8 @@ function createMSTeamsMessageHandler(deps) {
2750
2780
  });
2751
2781
  const dispatchResult = turnResult.dispatched ? turnResult.dispatchResult : void 0;
2752
2782
  const queuedFinal = dispatchResult?.queuedFinal ?? false;
2753
- const counts = resolveInboundReplyDispatchCounts(dispatchResult);
2754
- const hasFinalResponse = hasFinalInboundReplyDispatch(dispatchResult);
2783
+ const counts = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundReplyDispatchCounts)(dispatchResult);
2784
+ const hasFinalResponse = (0, openclaw_plugin_sdk_channel_inbound.hasFinalInboundReplyDispatch)(dispatchResult);
2755
2785
  log.info("dispatch complete", {
2756
2786
  queuedFinal,
2757
2787
  counts
@@ -2760,8 +2790,8 @@ function createMSTeamsMessageHandler(deps) {
2760
2790
  const finalCount = counts.final;
2761
2791
  logVerboseMessage(`msteams: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${teamsTo}`);
2762
2792
  } catch (err) {
2763
- log.error("dispatch failed", { error: formatUnknownError(err) });
2764
- runtime.error(`msteams dispatch failed: ${formatUnknownError(err)}`);
2793
+ log.error("dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
2794
+ runtime.error(`msteams dispatch failed: ${require_setup_surface.formatUnknownError(err)}`);
2765
2795
  try {
2766
2796
  await context.sendActivity("⚠️ Something went wrong. Please try again.");
2767
2797
  } catch {}
@@ -2770,7 +2800,7 @@ function createMSTeamsMessageHandler(deps) {
2770
2800
  const inboundDebouncer = core.channel.debounce.createInboundDebouncer({
2771
2801
  debounceMs: inboundDebounceMs,
2772
2802
  buildKey: (entry) => {
2773
- const conversationId = normalizeMSTeamsConversationId(entry.context.activity.conversation?.id ?? "");
2803
+ const conversationId = require_channel.normalizeMSTeamsConversationId(entry.context.activity.conversation?.id ?? "");
2774
2804
  const senderId = entry.context.activity.from?.aadObjectId ?? entry.context.activity.from?.id ?? "";
2775
2805
  if (!senderId || !conversationId) return null;
2776
2806
  return `msteams:${appId}:${conversationId}:${senderId}`;
@@ -2802,7 +2832,7 @@ function createMSTeamsMessageHandler(deps) {
2802
2832
  });
2803
2833
  },
2804
2834
  onError: (err) => {
2805
- runtime.error(`msteams debounce flush failed: ${formatUnknownError(err)}`);
2835
+ runtime.error(`msteams debounce flush failed: ${require_setup_surface.formatUnknownError(err)}`);
2806
2836
  }
2807
2837
  });
2808
2838
  return async function handleTeamsMessage(context) {
@@ -2811,9 +2841,9 @@ function createMSTeamsMessageHandler(deps) {
2811
2841
  const rawText = activity.text?.trim() ?? "";
2812
2842
  const htmlText = extractTextFromHtmlAttachments(attachments);
2813
2843
  const valueText = rawText || htmlText ? "" : serializeMSTeamsAdaptiveCardActionValue(activity.value);
2814
- const text = stripMSTeamsMentionTags(rawText || htmlText || valueText || "");
2815
- const wasMentioned = wasMSTeamsBotMentioned(activity);
2816
- const conversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
2844
+ const text = require_channel.stripMSTeamsMentionTags(rawText || htmlText || valueText || "");
2845
+ const wasMentioned = require_channel.wasMSTeamsBotMentioned(activity);
2846
+ const conversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
2817
2847
  const replyToId = activity.replyToId ?? void 0;
2818
2848
  const implicitMentionKinds = conversationId && replyToId && await wasMSTeamsMessageSentWithPersistence({
2819
2849
  conversationId,
@@ -2831,29 +2861,13 @@ function createMSTeamsMessageHandler(deps) {
2831
2861
  }
2832
2862
  //#endregion
2833
2863
  //#region extensions/msteams/src/monitor-handler/reaction-handler.ts
2834
- /** Teams reaction type names → Unicode emoji. */
2835
- const TEAMS_REACTION_EMOJI = {
2836
- like: "👍",
2837
- heart: "❤️",
2838
- laugh: "😆",
2839
- surprised: "😮",
2840
- sad: "😢",
2841
- angry: "😡"
2842
- };
2843
- /**
2844
- * Map a Teams reaction type string to a Unicode emoji.
2845
- * Falls back to the raw type if not recognized.
2846
- */
2847
- function mapReactionEmoji(reactionType) {
2848
- return TEAMS_REACTION_EMOJI[reactionType] ?? reactionType;
2849
- }
2850
2864
  /**
2851
2865
  * Create a handler for MS Teams reaction activities (reactionsAdded / reactionsRemoved).
2852
2866
  * The returned function accepts a turn context and a direction string.
2853
2867
  */
2854
2868
  function createMSTeamsReactionHandler(deps) {
2855
2869
  const { cfg, log } = deps;
2856
- const core = getMSTeamsRuntime();
2870
+ const core = require_runtime.getMSTeamsRuntime();
2857
2871
  const msteamsCfg = cfg.channels?.msteams;
2858
2872
  return async function handleReaction(context, direction) {
2859
2873
  const activity = context.activity;
@@ -2867,7 +2881,7 @@ function createMSTeamsReactionHandler(deps) {
2867
2881
  log.debug?.("reaction activity missing from.id; skipping");
2868
2882
  return;
2869
2883
  }
2870
- const conversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
2884
+ const conversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
2871
2885
  const conversationType = activity.conversation?.conversationType ?? "personal";
2872
2886
  const isGroupChat = conversationType === "groupChat" || activity.conversation?.isGroup === true;
2873
2887
  const isChannel = conversationType === "channel";
@@ -2900,7 +2914,7 @@ function createMSTeamsReactionHandler(deps) {
2900
2914
  const targetMessageId = activity.replyToId ?? "unknown";
2901
2915
  for (const reaction of reactions) {
2902
2916
  const reactionType = reaction.type ?? "unknown";
2903
- const emoji = mapReactionEmoji(reactionType);
2917
+ const emoji = require_messenger.resolveMSTeamsReactionEmoji(reactionType);
2904
2918
  const label = direction === "added" ? `Teams reaction ${emoji} added by ${senderName} on message ${targetMessageId}` : `Teams reaction ${emoji} removed by ${senderName} from message ${targetMessageId}`;
2905
2919
  log.info(`reaction ${direction}`, {
2906
2920
  sender: senderId,
@@ -3059,7 +3073,7 @@ function registerMSTeamsHandlers(handler, deps) {
3059
3073
  try {
3060
3074
  await handleTeamsMessage(context);
3061
3075
  } catch (err) {
3062
- deps.runtime.error(`msteams handler failed: ${formatUnknownError(err)}`);
3076
+ deps.runtime.error(`msteams handler failed: ${require_setup_surface.formatUnknownError(err)}`);
3063
3077
  }
3064
3078
  await next();
3065
3079
  });
@@ -3069,7 +3083,7 @@ function registerMSTeamsHandlers(handler, deps) {
3069
3083
  const botId = ctx.activity?.recipient?.id;
3070
3084
  const msteamsCfg = deps.cfg.channels?.msteams;
3071
3085
  for (const member of membersAdded) if (member.id === botId) {
3072
- const isPersonal = (normalizeOptionalLowercaseString(ctx.activity?.conversation?.conversationType) ?? "personal") === "personal";
3086
+ const isPersonal = ((0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(ctx.activity?.conversation?.conversationType) ?? "personal") === "personal";
3073
3087
  if (isPersonal && msteamsCfg?.welcomeCard !== false) {
3074
3088
  const card = buildWelcomeCard({
3075
3089
  botName: ctx.activity?.recipient?.name ?? void 0,
@@ -3085,7 +3099,7 @@ function registerMSTeamsHandlers(handler, deps) {
3085
3099
  });
3086
3100
  deps.log.info("sent welcome card");
3087
3101
  } catch (err) {
3088
- deps.log.debug?.("failed to send welcome card", { error: formatUnknownError(err) });
3102
+ deps.log.debug?.("failed to send welcome card", { error: require_setup_surface.formatUnknownError(err) });
3089
3103
  }
3090
3104
  } else if (!isPersonal && msteamsCfg?.groupWelcomeCard === true) {
3091
3105
  const botName = ctx.activity?.recipient?.name ?? void 0;
@@ -3093,7 +3107,7 @@ function registerMSTeamsHandlers(handler, deps) {
3093
3107
  await ctx.sendActivity(buildGroupWelcomeText(botName));
3094
3108
  deps.log.info("sent group welcome message");
3095
3109
  } catch (err) {
3096
- deps.log.debug?.("failed to send group welcome", { error: formatUnknownError(err) });
3110
+ deps.log.debug?.("failed to send group welcome", { error: require_setup_surface.formatUnknownError(err) });
3097
3111
  }
3098
3112
  } else deps.log.debug?.("skipping welcome (disabled by config or conversation type)");
3099
3113
  } else deps.log.debug?.("member added", { member: member.id });
@@ -3155,14 +3169,14 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3155
3169
  userComment = JSON.parse(value.actionValue.feedback).feedbackText || void 0;
3156
3170
  } catch {}
3157
3171
  const rawConversationId = activity.conversation?.id ?? "unknown";
3158
- const conversationId = normalizeMSTeamsConversationId(rawConversationId);
3172
+ const conversationId = require_channel.normalizeMSTeamsConversationId(rawConversationId);
3159
3173
  const senderId = activity.from?.aadObjectId ?? activity.from?.id ?? "unknown";
3160
3174
  const messageId = value.replyToId ?? activity.replyToId ?? "unknown";
3161
3175
  const isNegative = reaction === "dislike";
3162
- const convType = normalizeOptionalLowercaseString(activity.conversation?.conversationType);
3176
+ const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(activity.conversation?.conversationType);
3163
3177
  const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
3164
3178
  const isChannel = convType === "channel";
3165
- const core = getMSTeamsRuntime();
3179
+ const core = require_runtime.getMSTeamsRuntime();
3166
3180
  const route = core.channel.routing.resolveAgentRoute({
3167
3181
  cfg: deps.cfg,
3168
3182
  channel: "msteams",
@@ -3171,8 +3185,8 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3171
3185
  id: isDirectMessage ? senderId : conversationId
3172
3186
  }
3173
3187
  });
3174
- const feedbackThreadId = isChannel ? extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId ?? void 0 : void 0;
3175
- if (feedbackThreadId) route.sessionKey = resolveThreadSessionKeys({
3188
+ const feedbackThreadId = isChannel ? require_channel.extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId ?? void 0 : void 0;
3189
+ if (feedbackThreadId) route.sessionKey = (0, openclaw_plugin_sdk_routing.resolveThreadSessionKeys)({
3176
3190
  baseSessionKey: route.sessionKey,
3177
3191
  threadId: feedbackThreadId,
3178
3192
  parentSessionKey: route.sessionKey
@@ -3194,8 +3208,8 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3194
3208
  try {
3195
3209
  const storePath = core.channel.session.resolveStorePath(deps.cfg.session?.store, { agentId: route.agentId });
3196
3210
  const safeKey = route.sessionKey.replace(/[^a-zA-Z0-9_-]/g, "_");
3197
- await appendRegularFile({
3198
- filePath: path.join(storePath, `${safeKey}.jsonl`),
3211
+ await (0, openclaw_plugin_sdk_security_runtime.appendRegularFile)({
3212
+ filePath: node_path.default.join(storePath, `${safeKey}.jsonl`),
3199
3213
  content: `${JSON.stringify(feedbackEvent)}\n`,
3200
3214
  rejectSymlinkParents: true
3201
3215
  }).catch(() => {});
@@ -3211,10 +3225,6 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3211
3225
  id: activity.recipient.id,
3212
3226
  name: activity.recipient.name
3213
3227
  } : void 0,
3214
- bot: activity.recipient ? {
3215
- id: activity.recipient.id,
3216
- name: activity.recipient.name
3217
- } : void 0,
3218
3228
  conversation: {
3219
3229
  id: conversationId,
3220
3230
  conversationType: activity.conversation?.conversationType,
@@ -3236,7 +3246,7 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3236
3246
  userComment,
3237
3247
  log: deps.log
3238
3248
  }).catch((err) => {
3239
- deps.log.error("feedback reflection failed", { error: formatUnknownError(err) });
3249
+ deps.log.error("feedback reflection failed", { error: require_setup_surface.formatUnknownError(err) });
3240
3250
  });
3241
3251
  return true;
3242
3252
  }
@@ -3249,18 +3259,18 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3249
3259
  const expiredUploadMessage = "The file upload request has expired. Please try sending the file again.";
3250
3260
  const activity = context.activity;
3251
3261
  if (activity.type !== "invoke" || activity.name !== "fileConsent/invoke") return false;
3252
- const consentResponse = parseFileConsentInvoke(activity);
3262
+ const consentResponse = require_messenger.parseFileConsentInvoke(activity);
3253
3263
  if (!consentResponse) {
3254
3264
  log.debug?.("invalid file consent invoke", { value: activity.value });
3255
3265
  return false;
3256
3266
  }
3257
3267
  const uploadId = typeof consentResponse.context?.uploadId === "string" ? consentResponse.context.uploadId : void 0;
3258
- const inMemoryFile = getPendingUpload(uploadId);
3259
- const fsFile = inMemoryFile ? void 0 : await getPendingUploadFs(uploadId);
3268
+ const inMemoryFile = require_messenger.getPendingUpload(uploadId);
3269
+ const fsFile = inMemoryFile ? void 0 : await require_messenger.getPendingUploadFs(uploadId);
3260
3270
  const pendingFile = inMemoryFile ?? fsFile;
3261
3271
  if (pendingFile) {
3262
- const pendingConversationId = normalizeMSTeamsConversationId(pendingFile.conversationId);
3263
- const invokeConversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
3272
+ const pendingConversationId = require_channel.normalizeMSTeamsConversationId(pendingFile.conversationId);
3273
+ const invokeConversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
3264
3274
  if (!invokeConversationId || pendingConversationId !== invokeConversationId) {
3265
3275
  log.info("file consent conversation mismatch", {
3266
3276
  uploadId,
@@ -3278,12 +3288,12 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3278
3288
  size: pendingFile.buffer.length
3279
3289
  });
3280
3290
  try {
3281
- await uploadToConsentUrl({
3291
+ await require_messenger.uploadToConsentUrl({
3282
3292
  url: consentResponse.uploadInfo.uploadUrl,
3283
3293
  buffer: pendingFile.buffer,
3284
3294
  contentType: pendingFile.contentType
3285
3295
  });
3286
- const fileInfoCard = buildFileInfoCard({
3296
+ const fileInfoCard = require_messenger.buildFileInfoCard({
3287
3297
  filename: consentResponse.uploadInfo.name,
3288
3298
  contentUrl: consentResponse.uploadInfo.contentUrl,
3289
3299
  uniqueId: consentResponse.uploadInfo.uniqueId,
@@ -3313,12 +3323,12 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3313
3323
  } catch (err) {
3314
3324
  log.error("file upload failed", {
3315
3325
  uploadId,
3316
- error: formatUnknownError(err)
3326
+ error: require_setup_surface.formatUnknownError(err)
3317
3327
  });
3318
3328
  await context.sendActivity("File upload failed. Please try again.");
3319
3329
  } finally {
3320
- removePendingUpload(uploadId);
3321
- await removePendingUploadFs(uploadId);
3330
+ require_messenger.removePendingUpload(uploadId);
3331
+ await require_messenger.removePendingUploadFs(uploadId);
3322
3332
  }
3323
3333
  } else {
3324
3334
  log.debug?.("pending file not found for consent", { uploadId });
@@ -3326,8 +3336,8 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3326
3336
  }
3327
3337
  else {
3328
3338
  log.debug?.("user declined file consent", { uploadId });
3329
- removePendingUpload(uploadId);
3330
- await removePendingUploadFs(uploadId);
3339
+ require_messenger.removePendingUpload(uploadId);
3340
+ await require_messenger.removePendingUploadFs(uploadId);
3331
3341
  }
3332
3342
  return true;
3333
3343
  }
@@ -3338,7 +3348,7 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3338
3348
  */
3339
3349
  async function runMSTeamsFileConsentInvokeHandler(context, log) {
3340
3350
  try {
3341
- await withRevokedProxyFallback({
3351
+ await require_messenger.withRevokedProxyFallback({
3342
3352
  run: async () => await handleMSTeamsFileConsentInvoke(context, log),
3343
3353
  onRevoked: async () => true,
3344
3354
  onRevokedLog: () => {
@@ -3346,7 +3356,7 @@ async function runMSTeamsFileConsentInvokeHandler(context, log) {
3346
3356
  }
3347
3357
  });
3348
3358
  } catch (err) {
3349
- log.debug?.("file consent handler error", { error: formatUnknownError(err) });
3359
+ log.debug?.("file consent handler error", { error: require_setup_surface.formatUnknownError(err) });
3350
3360
  }
3351
3361
  }
3352
3362
  //#endregion
@@ -3365,7 +3375,7 @@ function applyMSTeamsWebhookTimeouts(httpServer, opts) {
3365
3375
  //#endregion
3366
3376
  //#region extensions/msteams/src/monitor.ts
3367
3377
  async function monitorMSTeamsProvider(opts) {
3368
- const core = getMSTeamsRuntime();
3378
+ const core = require_runtime.getMSTeamsRuntime();
3369
3379
  const log = core.logging.getChildLogger({ name: "msteams" });
3370
3380
  let cfg = opts.cfg;
3371
3381
  let msteamsCfg = cfg.channels?.msteams;
@@ -3376,7 +3386,7 @@ async function monitorMSTeamsProvider(opts) {
3376
3386
  shutdown: async () => {}
3377
3387
  };
3378
3388
  }
3379
- const creds = resolveMSTeamsCredentials(msteamsCfg);
3389
+ const creds = require_resolve_allowlist.resolveMSTeamsCredentials(msteamsCfg);
3380
3390
  if (!creds) {
3381
3391
  log.error("msteams credentials not configured");
3382
3392
  return {
@@ -3392,26 +3402,22 @@ async function monitorMSTeamsProvider(opts) {
3392
3402
  throw new Error(`exit ${code}`);
3393
3403
  }
3394
3404
  };
3395
- let allowFrom = msteamsCfg.allowFrom;
3396
- let groupAllowFrom = msteamsCfg.groupAllowFrom;
3397
- let teamsConfig = msteamsCfg.teams;
3398
- const allowNameMatching = isDangerousNameMatchingEnabled(msteamsCfg);
3405
+ const configuredAllowFrom = msteamsCfg.allowFrom;
3406
+ const configuredGroupAllowFrom = msteamsCfg.groupAllowFrom;
3407
+ let allowFrom = require_resolve_allowlist.projectStableMSTeamsUserAllowlist(configuredAllowFrom);
3408
+ let groupAllowFrom = require_resolve_allowlist.projectStableMSTeamsUserAllowlist(configuredGroupAllowFrom);
3409
+ let teamsConfig = require_resolve_allowlist.projectStableMSTeamsTeamsConfig(msteamsCfg.teams);
3410
+ const allowNameMatching = (0, openclaw_plugin_sdk_dangerous_name_runtime.isDangerousNameMatchingEnabled)(msteamsCfg);
3399
3411
  const cleanAllowEntry = (entry) => entry.replace(/^(msteams|teams):/i, "").replace(/^user:/i, "").trim();
3400
3412
  const isStableUserId = (entry) => /^[0-9a-fA-F-]{16,}$/.test(entry);
3401
3413
  const cleanAllowEntries = (entries) => entries?.map((entry) => cleanAllowEntry(entry)).filter((entry) => entry && entry !== "*") ?? [];
3402
- const mergeStableUserIds = (entries) => {
3403
- const additions = cleanAllowEntries(entries).filter((entry) => isStableUserId(entry));
3404
- return additions.length > 0 ? mergeAllowlist({
3405
- existing: entries,
3406
- additions
3407
- }) : entries;
3408
- };
3414
+ const isMutableUserEntry = (entry) => !isStableUserId(entry) && !/^accessGroup:/i.test(entry);
3409
3415
  const resolveAllowlistUsers = async (label, entries) => {
3410
3416
  if (entries.length === 0) return {
3411
3417
  additions: [],
3412
3418
  unresolved: []
3413
3419
  };
3414
- const resolved = await resolveMSTeamsUserAllowlist({
3420
+ const resolved = await require_resolve_allowlist.resolveMSTeamsUserAllowlist({
3415
3421
  cfg,
3416
3422
  entries
3417
3423
  });
@@ -3419,102 +3425,44 @@ async function monitorMSTeamsProvider(opts) {
3419
3425
  const unresolved = [];
3420
3426
  for (const entry of resolved) if (entry.resolved && entry.id) additions.push(entry.id);
3421
3427
  else unresolved.push(entry.input);
3422
- summarizeMapping(label, resolved.filter((entry) => entry.resolved && entry.id).map((entry) => `${entry.input}→${entry.id}`), unresolved, runtime);
3428
+ (0, openclaw_plugin_sdk_allow_from.summarizeMapping)(label, resolved.filter((entry) => entry.resolved && entry.id).map((entry) => `${entry.input}→${entry.id}`), unresolved, runtime);
3423
3429
  return {
3424
3430
  additions,
3425
3431
  unresolved
3426
3432
  };
3427
3433
  };
3428
3434
  try {
3429
- allowFrom = mergeStableUserIds(allowFrom);
3430
- if (Array.isArray(groupAllowFrom) && groupAllowFrom.length > 0) groupAllowFrom = mergeStableUserIds(groupAllowFrom);
3431
3435
  if (allowNameMatching) {
3432
- const allowEntries = cleanAllowEntries(allowFrom).filter((entry) => !isStableUserId(entry));
3436
+ const allowEntries = cleanAllowEntries(configuredAllowFrom).filter(isMutableUserEntry);
3433
3437
  if (allowEntries.length > 0) {
3434
3438
  const { additions } = await resolveAllowlistUsers("msteams users", allowEntries);
3435
- allowFrom = mergeAllowlist({
3439
+ allowFrom = (0, openclaw_plugin_sdk_allow_from.mergeAllowlist)({
3436
3440
  existing: allowFrom,
3437
3441
  additions
3438
3442
  });
3439
3443
  }
3440
- if (Array.isArray(groupAllowFrom) && groupAllowFrom.length > 0) {
3441
- const groupEntries = cleanAllowEntries(groupAllowFrom).filter((entry) => !isStableUserId(entry));
3444
+ if (Array.isArray(configuredGroupAllowFrom) && configuredGroupAllowFrom.length > 0) {
3445
+ const groupEntries = cleanAllowEntries(configuredGroupAllowFrom).filter(isMutableUserEntry);
3442
3446
  if (groupEntries.length > 0) {
3443
3447
  const { additions } = await resolveAllowlistUsers("msteams group users", groupEntries);
3444
- groupAllowFrom = mergeAllowlist({
3448
+ groupAllowFrom = (0, openclaw_plugin_sdk_allow_from.mergeAllowlist)({
3445
3449
  existing: groupAllowFrom,
3446
3450
  additions
3447
3451
  });
3448
3452
  }
3449
3453
  }
3450
3454
  }
3451
- if (teamsConfig && Object.keys(teamsConfig).length > 0) {
3452
- const entries = [];
3453
- for (const [teamKey, teamCfg] of Object.entries(teamsConfig)) {
3454
- if (teamKey === "*") continue;
3455
- const channels = teamCfg?.channels ?? {};
3456
- const channelKeys = Object.keys(channels).filter((key) => key !== "*");
3457
- if (channelKeys.length === 0) {
3458
- entries.push({
3459
- input: teamKey,
3460
- teamKey
3461
- });
3462
- continue;
3463
- }
3464
- for (const channelKey of channelKeys) entries.push({
3465
- input: `${teamKey}/${channelKey}`,
3466
- teamKey,
3467
- channelKey
3468
- });
3469
- }
3470
- if (entries.length > 0) {
3471
- const resolved = await resolveMSTeamsChannelAllowlist({
3472
- cfg,
3473
- entries: entries.map((entry) => entry.input)
3474
- });
3475
- const mapping = [];
3476
- const unresolved = [];
3477
- const nextTeams = { ...teamsConfig };
3478
- resolved.forEach((entry, idx) => {
3479
- const source = entries[idx];
3480
- if (!source) return;
3481
- const sourceTeam = teamsConfig?.[source.teamKey] ?? {};
3482
- if (!entry.resolved || !entry.teamId) {
3483
- unresolved.push(entry.input);
3484
- return;
3485
- }
3486
- mapping.push(entry.channelId ? `${entry.input}→${entry.teamId}/${entry.channelId}` : `${entry.input}→${entry.teamId}`);
3487
- const existing = nextTeams[entry.teamId] ?? {};
3488
- const mergedChannels = {
3489
- ...sourceTeam.channels,
3490
- ...existing.channels
3491
- };
3492
- const mergedTeam = {
3493
- ...sourceTeam,
3494
- ...existing,
3495
- channels: mergedChannels
3496
- };
3497
- nextTeams[entry.teamId] = mergedTeam;
3498
- if (source.channelKey && entry.channelId) {
3499
- const sourceChannel = sourceTeam.channels?.[source.channelKey];
3500
- if (sourceChannel) nextTeams[entry.teamId] = {
3501
- ...mergedTeam,
3502
- channels: {
3503
- ...mergedChannels,
3504
- [entry.channelId]: {
3505
- ...sourceChannel,
3506
- ...mergedChannels?.[entry.channelId]
3507
- }
3508
- }
3509
- };
3510
- }
3511
- });
3512
- teamsConfig = nextTeams;
3513
- summarizeMapping("msteams channels", mapping, unresolved, runtime);
3514
- }
3455
+ if (msteamsCfg.teams && Object.keys(msteamsCfg.teams).length > 0) {
3456
+ const resolved = await require_resolve_allowlist.resolveMSTeamsTeamsConfig({
3457
+ cfg,
3458
+ teamIdMode: "bot-framework",
3459
+ teams: msteamsCfg.teams
3460
+ });
3461
+ teamsConfig = resolved.teams;
3462
+ (0, openclaw_plugin_sdk_allow_from.summarizeMapping)("msteams channels", resolved.mapping, resolved.unresolved, runtime);
3515
3463
  }
3516
3464
  } catch (err) {
3517
- runtime.error?.(`msteams resolve failed; falling back to raw config entries — allowlist members resolved via Graph may be missing. ${formatUnknownError(err)}`);
3465
+ runtime.error?.(`msteams resolve failed; mutable allowlist entries are disabled. ${require_setup_surface.formatUnknownError(err)}`);
3518
3466
  }
3519
3467
  msteamsCfg = {
3520
3468
  ...msteamsCfg,
@@ -3534,8 +3482,8 @@ async function monitorMSTeamsProvider(opts) {
3534
3482
  const MB = 1024 * 1024;
3535
3483
  const agentDefaults = cfg.agents?.defaults;
3536
3484
  const mediaMaxBytes = typeof agentDefaults?.mediaMaxMb === "number" && agentDefaults.mediaMaxMb > 0 ? Math.floor(agentDefaults.mediaMaxMb * MB) : 8 * MB;
3537
- const conversationStore = opts.conversationStore ?? createMSTeamsConversationStoreState();
3538
- const pollStore = opts.pollStore ?? createMSTeamsPollStoreState();
3485
+ const conversationStore = opts.conversationStore ?? require_polls.createMSTeamsConversationStoreState();
3486
+ const pollStore = opts.pollStore ?? require_polls.createMSTeamsPollStoreState();
3539
3487
  log.info(`starting provider (port ${port})`);
3540
3488
  const express = await import("express");
3541
3489
  const expressApp = express.default();
@@ -3547,7 +3495,7 @@ async function monitorMSTeamsProvider(opts) {
3547
3495
  }
3548
3496
  next();
3549
3497
  });
3550
- expressApp.use(express.json({ limit: DEFAULT_WEBHOOK_MAX_BODY_BYTES }));
3498
+ expressApp.use(express.json({ limit: openclaw_plugin_sdk_webhook_ingress.DEFAULT_WEBHOOK_MAX_BODY_BYTES }));
3551
3499
  expressApp.use((err, _req, res, next) => {
3552
3500
  if (err && typeof err === "object" && "status" in err && err.status === 413) {
3553
3501
  res.status(413).json({ error: "Payload too large" });
@@ -3556,11 +3504,12 @@ async function monitorMSTeamsProvider(opts) {
3556
3504
  next(err);
3557
3505
  });
3558
3506
  const configuredPath = msteamsCfg.webhook?.path ?? "/api/messages";
3559
- const { app } = await loadMSTeamsSdkWithAuth(creds, {
3560
- ...resolveMSTeamsSdkCloudOptions(msteamsCfg),
3561
- httpServerAdapter: await createMSTeamsExpressAdapter(expressApp),
3507
+ const ssoConnectionName = msteamsCfg.sso?.enabled && msteamsCfg.sso.connectionName ? msteamsCfg.sso.connectionName : void 0;
3508
+ const { app } = await require_resolve_allowlist.loadMSTeamsSdkWithAuth(creds, {
3509
+ ...require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg),
3510
+ httpServerAdapter: await require_resolve_allowlist.createMSTeamsExpressAdapter(expressApp),
3562
3511
  messagingEndpoint: configuredPath,
3563
- ...msteamsCfg.sso?.enabled && msteamsCfg.sso.connectionName ? { oauthDefaultConnectionName: msteamsCfg.sso.connectionName } : {}
3512
+ ...ssoConnectionName ? { oauthDefaultConnectionName: ssoConnectionName } : {}
3564
3513
  });
3565
3514
  if (configuredPath !== "/api/messages") {
3566
3515
  let warnedLegacyMessagesRoute = false;
@@ -3573,16 +3522,12 @@ async function monitorMSTeamsProvider(opts) {
3573
3522
  expressApp(req, res, next);
3574
3523
  });
3575
3524
  }
3576
- const tokenProvider = createMSTeamsTokenProvider(app);
3577
- let ssoDeps;
3578
- if (msteamsCfg.sso?.enabled && msteamsCfg.sso.connectionName) {
3579
- ssoDeps = {
3580
- tokenProvider,
3581
- tokenStore: createMSTeamsSsoTokenStoreFs(),
3582
- connectionName: msteamsCfg.sso.connectionName
3583
- };
3584
- log.debug?.("msteams sso enabled", { connectionName: msteamsCfg.sso.connectionName });
3585
- }
3525
+ const tokenProvider = require_resolve_allowlist.createMSTeamsTokenProvider(app);
3526
+ const ssoDeps = ssoConnectionName ? {
3527
+ tokenStore: require_sso_token_store.createMSTeamsSsoTokenStoreFs(),
3528
+ connectionName: ssoConnectionName
3529
+ } : void 0;
3530
+ if (ssoDeps) log.debug?.("msteams sso enabled", { connectionName: ssoDeps.connectionName });
3586
3531
  const handler = buildActivityHandler();
3587
3532
  const handlerDeps = {
3588
3533
  cfg,
@@ -3594,15 +3539,14 @@ async function monitorMSTeamsProvider(opts) {
3594
3539
  mediaMaxBytes,
3595
3540
  conversationStore,
3596
3541
  pollStore,
3597
- log,
3598
- sso: ssoDeps
3542
+ log
3599
3543
  };
3600
3544
  registerMSTeamsHandlers(handler, handlerDeps);
3601
3545
  app.on("card.action", async (ctx) => {
3602
3546
  const adaptedCtx = adaptSdkContext(ctx, app);
3603
3547
  try {
3604
3548
  const activity = adaptedCtx.activity;
3605
- const vote = extractMSTeamsPollVote(activity);
3549
+ const vote = require_polls.extractMSTeamsPollVote(activity);
3606
3550
  if (vote) {
3607
3551
  const voterId = activity?.from?.aadObjectId ?? activity?.from?.id ?? "unknown";
3608
3552
  try {
@@ -3620,8 +3564,8 @@ async function monitorMSTeamsProvider(opts) {
3620
3564
  value: "Poll not found."
3621
3565
  };
3622
3566
  }
3623
- const pollConversationId = existingPoll.conversationId ? normalizeMSTeamsConversationId(existingPoll.conversationId) : void 0;
3624
- const activityConversationId = normalizeMSTeamsConversationId(activity?.conversation?.id ?? "");
3567
+ const pollConversationId = existingPoll.conversationId ? require_channel.normalizeMSTeamsConversationId(existingPoll.conversationId) : void 0;
3568
+ const activityConversationId = require_channel.normalizeMSTeamsConversationId(activity?.conversation?.id ?? "");
3625
3569
  if (pollConversationId && pollConversationId !== activityConversationId) {
3626
3570
  log.info("poll vote ignored (conversation mismatch)", {
3627
3571
  pollId: vote.pollId,
@@ -3658,7 +3602,7 @@ async function monitorMSTeamsProvider(opts) {
3658
3602
  } catch (err) {
3659
3603
  log.error("failed to record poll vote", {
3660
3604
  pollId: vote.pollId,
3661
- error: formatUnknownError(err)
3605
+ error: require_setup_surface.formatUnknownError(err)
3662
3606
  });
3663
3607
  return {
3664
3608
  statusCode: 500,
@@ -3675,7 +3619,7 @@ async function monitorMSTeamsProvider(opts) {
3675
3619
  }
3676
3620
  }
3677
3621
  handler.run(adaptedCtx).catch((err) => {
3678
- log.error("msteams card.action dispatch failed", { error: formatUnknownError(err) });
3622
+ log.error("msteams card.action dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
3679
3623
  });
3680
3624
  return {
3681
3625
  statusCode: 200,
@@ -3683,7 +3627,7 @@ async function monitorMSTeamsProvider(opts) {
3683
3627
  value: "OK"
3684
3628
  };
3685
3629
  } catch (err) {
3686
- log.error("msteams card.action failed", { error: formatUnknownError(err) });
3630
+ log.error("msteams card.action failed", { error: require_setup_surface.formatUnknownError(err) });
3687
3631
  return {
3688
3632
  statusCode: 500,
3689
3633
  type: "application/vnd.microsoft.error",
@@ -3751,7 +3695,7 @@ async function monitorMSTeamsProvider(opts) {
3751
3695
  hasExpiry: Boolean(ctx.token.expiration)
3752
3696
  });
3753
3697
  })().catch((err) => {
3754
- log.error("msteams sso token persistence failed", { error: formatUnknownError(err) });
3698
+ log.error("msteams sso token persistence failed", { error: require_setup_surface.formatUnknownError(err) });
3755
3699
  });
3756
3700
  });
3757
3701
  app.on("message.submit", async (ctx) => {
@@ -3768,39 +3712,31 @@ async function monitorMSTeamsProvider(opts) {
3768
3712
  }
3769
3713
  await handler.run(adaptedCtx);
3770
3714
  } catch (err) {
3771
- log.error("msteams webhook failed", { error: formatUnknownError(err) });
3715
+ log.error("msteams webhook failed", { error: require_setup_surface.formatUnknownError(err) });
3772
3716
  }
3773
3717
  });
3774
3718
  await app.initialize();
3775
- const httpServer = expressApp.listen(port);
3776
- await new Promise((resolve, reject) => {
3777
- const onListening = () => {
3778
- httpServer.off("error", onError);
3779
- log.info(`msteams provider started on port ${port}`);
3780
- resolve();
3781
- };
3782
- const onError = (err) => {
3783
- httpServer.off("listening", onListening);
3784
- log.error("msteams server error", { error: formatUnknownError(err) });
3785
- reject(toLintErrorObject(err, "MSTeams server failed"));
3786
- };
3787
- httpServer.once("listening", onListening);
3788
- httpServer.once("error", onError);
3719
+ const httpServer = await new Promise((resolve, reject) => {
3720
+ const server = expressApp.listen(port, (err) => err ? reject(err) : resolve(server));
3721
+ }).catch((err) => {
3722
+ log.error("msteams server error", { error: require_setup_surface.formatUnknownError(err) });
3723
+ throw err;
3789
3724
  });
3725
+ log.info(`msteams provider started on port ${port}`);
3790
3726
  applyMSTeamsWebhookTimeouts(httpServer);
3791
3727
  httpServer.on("error", (err) => {
3792
- log.error("msteams server error", { error: formatUnknownError(err) });
3728
+ log.error("msteams server error", { error: require_setup_surface.formatUnknownError(err) });
3793
3729
  });
3794
3730
  const shutdown = async () => {
3795
3731
  log.info("shutting down msteams provider");
3796
3732
  return new Promise((resolve) => {
3797
3733
  httpServer.close((err) => {
3798
- if (err) log.debug?.("msteams server close error", { error: formatUnknownError(err) });
3734
+ if (err) log.debug?.("msteams server close error", { error: require_setup_surface.formatUnknownError(err) });
3799
3735
  resolve();
3800
3736
  });
3801
3737
  });
3802
3738
  };
3803
- await keepHttpServerTaskAlive({
3739
+ await (0, openclaw_plugin_sdk_channel_outbound.keepHttpServerTaskAlive)({
3804
3740
  server: httpServer,
3805
3741
  abortSignal: opts.abortSignal,
3806
3742
  onAbort: shutdown
@@ -3810,13 +3746,6 @@ async function monitorMSTeamsProvider(opts) {
3810
3746
  shutdown
3811
3747
  };
3812
3748
  }
3813
- function toLintErrorObject(value, fallbackMessage) {
3814
- if (value instanceof Error) return value;
3815
- if (typeof value === "string") return new Error(value);
3816
- const error = new Error(fallbackMessage, { cause: value });
3817
- if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
3818
- return error;
3819
- }
3820
3749
  /**
3821
3750
  * Build a minimal ActivityHandler-compatible object that supports
3822
3751
  * onMessage / onMembersAdded registration and a run() method.
@@ -3866,7 +3795,9 @@ function adaptSdkContext(ctx, app) {
3866
3795
  const sdkCtx = ctx ?? {};
3867
3796
  if (typeof sdkCtx.reply !== "function" && typeof sdkCtx.send !== "function") return ctx;
3868
3797
  const conversationId = sdkCtx.activity?.conversation?.id ?? "";
3869
- const activityApi = sdkCtx.api ?? app.api;
3798
+ const inboundApi = sdkCtx.api;
3799
+ const activityApi = inboundApi ?? app.api;
3800
+ const getTeamDetails = inboundApi ? (teamId) => inboundApi.teams.getById(teamId) : void 0;
3870
3801
  const conversationType = (sdkCtx.activity?.conversation?.conversationType ?? "").toLowerCase();
3871
3802
  const isThreadable = conversationType === "channel" || conversationType === "groupchat";
3872
3803
  const sendActivity = (activity) => isThreadable ? sdkCtx.reply(activity) : sdkCtx.send(activity);
@@ -3884,8 +3815,9 @@ function adaptSdkContext(ctx, app) {
3884
3815
  deleteActivity: async (activityId) => {
3885
3816
  return activityApi.conversations.activities(conversationId).delete(activityId);
3886
3817
  },
3818
+ getTeamDetails,
3887
3819
  stream: sdkCtx.stream
3888
3820
  });
3889
3821
  }
3890
3822
  //#endregion
3891
- export { monitorMSTeamsProvider };
3823
+ exports.monitorMSTeamsProvider = monitorMSTeamsProvider;