@openclaw/msteams 2026.7.1 → 2026.7.2-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/api.cjs +8 -0
  2. package/dist/{channel-Cf5LJ4aO.js → channel-BKc8zroK.cjs} +648 -240
  3. package/dist/channel-config-api.cjs +3 -0
  4. package/dist/channel-plugin-api.cjs +3 -0
  5. package/dist/channel.runtime-hd1Zsoo2.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-BVcKl6dv.cjs} +436 -891
  11. package/dist/{oauth-BifWxWzq.js → oauth-yNoKtU_3.cjs} +21 -21
  12. package/dist/{polls-B_hs4aKm.js → polls-BhoI4VaN.cjs} +188 -38
  13. package/dist/{resolve-allowlist-Dnw4aNOu.js → resolve-allowlist-DXd097EP.cjs} +849 -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-D4f6x5cx.cjs} +127 -61
  21. package/dist/{src-DZFs5XtZ.js → src-20YasZEp.cjs} +686 -748
  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-DXd097EP.cjs");
5
+ const require_channel = require("./channel-BKc8zroK.cjs");
6
+ const require_setup_surface = require("./setup-surface-D4f6x5cx.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-BVcKl6dv.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;
@@ -1512,8 +1461,10 @@ function createTeamsReplyStreamController(params) {
1512
1461
  let streamFailed = false;
1513
1462
  let lastInformativeText = "";
1514
1463
  let progressLines = [];
1464
+ let latestPlan;
1465
+ let latestPlanExplanation;
1515
1466
  let pendingFinalPayload;
1516
- let emittedTextLength = 0;
1467
+ let emittedText = "";
1517
1468
  const wasCanceled = () => canceledLocally || Boolean(stream?.canceled);
1518
1469
  const fallbackPayloadForSuppressedFinal = (payload) => {
1519
1470
  return Boolean(payload.mediaUrl || payload.mediaUrls?.length) ? {
@@ -1528,12 +1479,14 @@ function createTeamsReplyStreamController(params) {
1528
1479
  * default) and prepends collected tool-progress lines when configured.
1529
1480
  */
1530
1481
  const renderInformativeUpdate = () => {
1531
- if (!stream || wasCanceled()) return;
1532
- const informativeText = formatChannelProgressDraftText({
1482
+ if (!stream || wasCanceled() || streamFinalizationPending) return;
1483
+ const informativeText = (0, openclaw_plugin_sdk_channel_outbound.formatChannelProgressDraftText)({
1533
1484
  entry: params.msteamsConfig,
1534
1485
  lines: shouldStreamPreviewToolProgress ? progressLines : [],
1535
1486
  seed: params.progressSeed,
1536
- bullet: "-"
1487
+ bullet: "-",
1488
+ narration: latestPlanExplanation,
1489
+ plan: latestPlan
1537
1490
  });
1538
1491
  if (!informativeText || informativeText === lastInformativeText) return;
1539
1492
  lastInformativeText = informativeText;
@@ -1547,17 +1500,26 @@ function createTeamsReplyStreamController(params) {
1547
1500
  params.log?.debug?.(`stream informative update failed: ${err instanceof Error ? err.message : String(err)}`);
1548
1501
  }
1549
1502
  };
1550
- const progressDraftGate = createChannelProgressDraftGate({ onStart: renderInformativeUpdate });
1503
+ const progressDraftGate = (0, openclaw_plugin_sdk_channel_outbound.createChannelProgressDraftGate)({ onStart: renderInformativeUpdate });
1551
1504
  return {
1552
1505
  async onReplyStart() {},
1553
1506
  onPartialReply(payload) {
1554
1507
  if (!stream || !payload.text || wasCanceled() || streamMode !== "partial" || streamFinalizationPending) return;
1555
1508
  const fullText = payload.text;
1556
- if (fullText.length <= emittedTextLength) return;
1557
- const delta = fullText.slice(emittedTextLength);
1509
+ let prefixLength = 0;
1510
+ while (prefixLength < emittedText.length && prefixLength < fullText.length && emittedText[prefixLength] === fullText[prefixLength]) prefixLength += 1;
1511
+ const previousRemainder = emittedText.slice(prefixLength);
1512
+ const delta = fullText.slice(prefixLength);
1513
+ if (!delta) return;
1514
+ if (previousRemainder.trim()) {
1515
+ stream.clearText();
1516
+ streamFailed = true;
1517
+ streamFinalizationPending = true;
1518
+ return;
1519
+ }
1558
1520
  try {
1559
1521
  stream.emit(delta);
1560
- emittedTextLength = fullText.length;
1522
+ emittedText = fullText;
1561
1523
  tokensEmitted = true;
1562
1524
  } catch (err) {
1563
1525
  if (isStreamCancelledError(err)) {
@@ -1575,7 +1537,7 @@ function createTeamsReplyStreamController(params) {
1575
1537
  */
1576
1538
  async noteProgressWork(options) {
1577
1539
  if (!stream || streamMode !== "progress") return;
1578
- if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
1540
+ if (options?.toolName !== void 0 && !(0, openclaw_plugin_sdk_channel_outbound.isChannelProgressDraftWorkToolName)(options.toolName)) return;
1579
1541
  const hadStarted = progressDraftGate.hasStarted;
1580
1542
  const progressActive = await progressDraftGate.noteWork();
1581
1543
  if ((hadStarted || progressActive) && progressDraftGate.hasStarted) renderInformativeUpdate();
@@ -1587,15 +1549,23 @@ function createTeamsReplyStreamController(params) {
1587
1549
  */
1588
1550
  async pushProgressLine(line, options) {
1589
1551
  if (!stream || streamMode !== "progress") return;
1590
- if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
1552
+ if (options?.toolName !== void 0 && !(0, openclaw_plugin_sdk_channel_outbound.isChannelProgressDraftWorkToolName)(options.toolName)) return;
1591
1553
  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) });
1554
+ const normalized = (0, openclaw_plugin_sdk_channel_outbound.normalizeChannelProgressDraftLineIdentity)(line);
1555
+ 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
1556
  }
1595
1557
  const hadStarted = progressDraftGate.hasStarted;
1596
1558
  const progressActive = await progressDraftGate.noteWork();
1597
1559
  if ((hadStarted || progressActive) && progressDraftGate.hasStarted) renderInformativeUpdate();
1598
1560
  },
1561
+ async pushPlanProgress(steps, options) {
1562
+ if (!stream || streamMode !== "progress" || streamFinalizationPending) return;
1563
+ latestPlan = steps?.length ? steps.map((entry) => ({ ...entry })) : void 0;
1564
+ latestPlanExplanation = options?.explanation?.replace(/\s+/g, " ").trim() || void 0;
1565
+ const hadStarted = progressDraftGate.hasStarted;
1566
+ await progressDraftGate.startNow();
1567
+ if (hadStarted && progressDraftGate.hasStarted) renderInformativeUpdate();
1568
+ },
1599
1569
  preparePayload(payload) {
1600
1570
  if (!stream) return payload;
1601
1571
  if (wasCanceled()) return;
@@ -1627,6 +1597,7 @@ function createTeamsReplyStreamController(params) {
1627
1597
  return payload;
1628
1598
  },
1629
1599
  async finalize() {
1600
+ progressDraftGate.cancel();
1630
1601
  if (!stream || !streamFinalizationPending || wasCanceled()) return;
1631
1602
  const finalEntities = [{
1632
1603
  type: "https://schema.org/Message",
@@ -1678,9 +1649,9 @@ function createTeamsReplyStreamController(params) {
1678
1649
  //#endregion
1679
1650
  //#region extensions/msteams/src/reply-dispatcher.ts
1680
1651
  function createMSTeamsReplyDispatcher(params) {
1681
- const core = getMSTeamsRuntime();
1652
+ const core = require_runtime.getMSTeamsRuntime();
1682
1653
  const msteamsCfg = params.cfg.channels?.msteams;
1683
- const conversationType = normalizeOptionalLowercaseString(params.conversationRef.conversation?.conversationType);
1654
+ const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationRef.conversation?.conversationType);
1684
1655
  const isTypingSupported = conversationType === "personal" || conversationType === "groupchat";
1685
1656
  /**
1686
1657
  * Keepalive cadence for the typing indicator while the bot is running
@@ -1701,13 +1672,13 @@ function createMSTeamsReplyDispatcher(params) {
1701
1672
  const streamActiveRef = { current: () => false };
1702
1673
  const streamCanceledRef = { current: () => false };
1703
1674
  const rawSendTypingIndicator = async () => {
1704
- await withRevokedProxyFallback({
1675
+ await require_messenger.withRevokedProxyFallback({
1705
1676
  run: async () => {
1706
1677
  await params.context.sendActivity({ type: "typing" });
1707
1678
  },
1708
1679
  onRevoked: async () => {
1709
- const baseRef = buildConversationReference(params.conversationRef);
1710
- await sendMSTeamsActivityWithReference(params.app, baseRef, { type: "typing" }, { serviceUrlBoundary: resolveMSTeamsSdkCloudOptions(msteamsCfg) });
1680
+ const baseRef = require_messenger.buildConversationReference(params.conversationRef);
1681
+ await require_messenger.sendMSTeamsActivityWithReference(params.app, baseRef, { type: "typing" }, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg) });
1711
1682
  },
1712
1683
  onRevokedLog: () => {
1713
1684
  params.log.debug?.("turn context revoked, sending typing via proactive messaging");
@@ -1719,7 +1690,7 @@ function createMSTeamsReplyDispatcher(params) {
1719
1690
  if (streamCanceledRef.current()) return;
1720
1691
  await rawSendTypingIndicator();
1721
1692
  } : async () => {};
1722
- const { onModelSelected, typingCallbacks, ...replyPipeline } = createChannelMessageReplyPipeline({
1693
+ const { onModelSelected, typingCallbacks, ...replyPipeline } = (0, openclaw_plugin_sdk_channel_outbound.createChannelMessageReplyPipeline)({
1723
1694
  cfg: params.cfg,
1724
1695
  agentId: params.agentId,
1725
1696
  channel: "msteams",
@@ -1729,7 +1700,7 @@ function createMSTeamsReplyDispatcher(params) {
1729
1700
  keepaliveIntervalMs: TYPING_KEEPALIVE_INTERVAL_MS,
1730
1701
  maxDurationMs: TYPING_KEEPALIVE_MAX_DURATION_MS,
1731
1702
  onStartError: (err) => {
1732
- logTypingFailure({
1703
+ (0, openclaw_plugin_sdk_channel_outbound.logTypingFailure)({
1733
1704
  log: (message) => params.log.debug?.(message),
1734
1705
  channel: "msteams",
1735
1706
  action: "start",
@@ -1743,7 +1714,7 @@ function createMSTeamsReplyDispatcher(params) {
1743
1714
  cfg: params.cfg,
1744
1715
  channel: "msteams"
1745
1716
  });
1746
- const mediaMaxBytes = resolveChannelMediaMaxBytes({
1717
+ const mediaMaxBytes = (0, openclaw_plugin_sdk_media_runtime.resolveChannelMediaMaxBytes)({
1747
1718
  cfg: params.cfg,
1748
1719
  resolveChannelLimitMb: ({ cfg }) => cfg.channels?.msteams?.mediaMaxMb
1749
1720
  });
@@ -1758,13 +1729,13 @@ function createMSTeamsReplyDispatcher(params) {
1758
1729
  });
1759
1730
  streamActiveRef.current = () => streamController.isStreamActive();
1760
1731
  streamCanceledRef.current = () => streamController.wasCanceled();
1761
- const teamsStreamMode = resolveChannelPreviewStreamMode(msteamsCfg, "partial");
1762
- const blockStreamingResolved = teamsStreamMode === "block" ? true : resolveChannelStreamingBlockEnabled(msteamsCfg);
1732
+ const teamsStreamMode = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelPreviewStreamMode)(msteamsCfg, "partial");
1733
+ const blockStreamingResolved = teamsStreamMode === "block" ? true : (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingBlockEnabled)(msteamsCfg);
1763
1734
  const blockStreamingEnabled = blockStreamingResolved ?? false;
1764
1735
  const typingIndicatorEnabled = typeof msteamsCfg?.typingIndicator === "boolean" ? msteamsCfg.typingIndicator : true;
1765
1736
  const pendingMessages = [];
1766
1737
  const sendMessages = async (messages) => {
1767
- return sendMSTeamsMessages({
1738
+ return require_messenger.sendMSTeamsMessages({
1768
1739
  replyStyle: params.replyStyle,
1769
1740
  app: params.app,
1770
1741
  appId: params.appId,
@@ -1782,12 +1753,12 @@ function createMSTeamsReplyDispatcher(params) {
1782
1753
  sharePointSiteId: params.sharePointSiteId,
1783
1754
  mediaMaxBytes,
1784
1755
  feedbackLoopEnabled,
1785
- serviceUrlBoundary: resolveMSTeamsSdkCloudOptions(msteamsCfg)
1756
+ serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg)
1786
1757
  });
1787
1758
  };
1788
1759
  const queueDeliveryFailureSystemEvent = (failure) => {
1789
- const classification = classifyMSTeamsSendError(failure.error);
1790
- const errorText = formatUnknownError(failure.error);
1760
+ const classification = require_setup_surface.classifyMSTeamsSendError(failure.error);
1761
+ const errorText = require_setup_surface.formatUnknownError(failure.error);
1791
1762
  const failedAll = failure.failed >= failure.total;
1792
1763
  const sentences = [
1793
1764
  `Microsoft Teams delivery failed: ${failedAll ? "the previous reply was not delivered" : `${failure.failed} of ${failure.total} message blocks were not delivered`}.`,
@@ -1802,7 +1773,7 @@ function createMSTeamsReplyDispatcher(params) {
1802
1773
  });
1803
1774
  };
1804
1775
  const queueReplyPayload = (payload) => {
1805
- const messages = renderReplyPayloadsToMessages([payload], {
1776
+ const messages = require_messenger.renderReplyPayloadsToMessages([payload], {
1806
1777
  textChunkLimit: params.textLimit,
1807
1778
  chunkText: true,
1808
1779
  mediaMode: "split",
@@ -1859,9 +1830,9 @@ function createMSTeamsReplyDispatcher(params) {
1859
1830
  if (blockStreamingEnabled) await flushPendingMessages();
1860
1831
  },
1861
1832
  onError: (err, info) => {
1862
- const errMsg = formatUnknownError(err);
1863
- const classification = classifyMSTeamsSendError(err);
1864
- const hint = formatMSTeamsSendErrorHint(classification);
1833
+ const errMsg = require_setup_surface.formatUnknownError(err);
1834
+ const classification = require_setup_surface.classifyMSTeamsSendError(err);
1835
+ const hint = require_setup_surface.formatMSTeamsSendErrorHint(classification);
1865
1836
  params.runtime.error?.(`msteams ${info.kind} reply failed: ${errMsg}${hint ? ` (${hint})` : ""}`);
1866
1837
  params.log.error("reply failed", {
1867
1838
  kind: info.kind,
@@ -1873,9 +1844,9 @@ function createMSTeamsReplyDispatcher(params) {
1873
1844
  });
1874
1845
  const markDispatchIdle = () => {
1875
1846
  return flushPendingMessages().catch((err) => {
1876
- const errMsg = formatUnknownError(err);
1877
- const classification = classifyMSTeamsSendError(err);
1878
- const hint = formatMSTeamsSendErrorHint(classification);
1847
+ const errMsg = require_setup_surface.formatUnknownError(err);
1848
+ const classification = require_setup_surface.classifyMSTeamsSendError(err);
1849
+ const hint = require_setup_surface.formatMSTeamsSendErrorHint(classification);
1879
1850
  params.runtime.error?.(`msteams flush reply failed: ${errMsg}${hint ? ` (${hint})` : ""}`);
1880
1851
  params.log.error("flush reply failed", {
1881
1852
  error: errMsg,
@@ -1884,7 +1855,7 @@ function createMSTeamsReplyDispatcher(params) {
1884
1855
  });
1885
1856
  }).then(async () => {
1886
1857
  const fallbackPayload = await streamController.finalize().catch((err) => {
1887
- params.log.debug?.("stream finalize failed", { error: formatUnknownError(err) });
1858
+ params.log.debug?.("stream finalize failed", { error: require_setup_surface.formatUnknownError(err) });
1888
1859
  });
1889
1860
  if (fallbackPayload) {
1890
1861
  queueReplyPayload(fallbackPayload);
@@ -1894,8 +1865,8 @@ function createMSTeamsReplyDispatcher(params) {
1894
1865
  baseMarkDispatchIdle();
1895
1866
  });
1896
1867
  };
1897
- const previewToolProgressEnabled = resolveChannelStreamingPreviewToolProgress(msteamsCfg);
1898
- const suppressDefaultToolProgressMessages = resolveChannelStreamingSuppressDefaultToolProgressMessages(msteamsCfg);
1868
+ const previewToolProgressEnabled = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingPreviewToolProgress)(msteamsCfg);
1869
+ const suppressDefaultToolProgressMessages = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingSuppressDefaultToolProgressMessages)(msteamsCfg);
1899
1870
  const shouldSuppressDefaultToolProgressMessages = teamsStreamMode === "progress" && suppressDefaultToolProgressMessages && previewToolProgressEnabled;
1900
1871
  const progressCallbacks = streamController.hasStream() ? {
1901
1872
  onReasoningStream: async (payload) => {
@@ -1905,7 +1876,7 @@ function createMSTeamsReplyDispatcher(params) {
1905
1876
  await streamController.pushProgressLine(text);
1906
1877
  return;
1907
1878
  }
1908
- await streamController.pushProgressLine(buildChannelProgressDraftLine({
1879
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
1909
1880
  event: "item",
1910
1881
  itemId: "reasoning",
1911
1882
  itemKind: "analysis",
@@ -1916,7 +1887,7 @@ function createMSTeamsReplyDispatcher(params) {
1916
1887
  onToolStart: async (payload) => {
1917
1888
  const name = typeof payload?.name === "string" ? payload.name : void 0;
1918
1889
  const detailMode = typeof payload?.detailMode === "string" ? payload.detailMode : void 0;
1919
- await streamController.pushProgressLine(buildChannelProgressDraftLineForEntry(msteamsCfg, {
1890
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
1920
1891
  event: "tool",
1921
1892
  ...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
1922
1893
  ...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
@@ -1926,7 +1897,7 @@ function createMSTeamsReplyDispatcher(params) {
1926
1897
  }, detailMode === "explain" || detailMode === "raw" ? { detailMode } : void 0), name ? { toolName: name } : void 0);
1927
1898
  },
1928
1899
  onItemEvent: async (payload) => {
1929
- await streamController.pushProgressLine(buildChannelProgressDraftLineForEntry(msteamsCfg, {
1900
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
1930
1901
  event: "item",
1931
1902
  ...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
1932
1903
  ...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
@@ -1942,17 +1913,11 @@ function createMSTeamsReplyDispatcher(params) {
1942
1913
  },
1943
1914
  onPlanUpdate: async (payload) => {
1944
1915
  if (payload?.phase !== "update") return;
1945
- await streamController.pushProgressLine(buildChannelProgressDraftLine({
1946
- event: "plan",
1947
- phase: payload.phase,
1948
- ...typeof payload?.title === "string" ? { title: payload.title } : {},
1949
- ...typeof payload?.explanation === "string" ? { explanation: payload.explanation } : {},
1950
- ...Array.isArray(payload?.steps) && payload.steps.every((s) => typeof s === "string") ? { steps: payload.steps } : {}
1951
- }));
1916
+ await streamController.pushPlanProgress((0, openclaw_plugin_sdk_channel_outbound.normalizeAgentPlanSteps)(payload.planSteps), { explanation: typeof payload.explanation === "string" ? payload.explanation : void 0 });
1952
1917
  },
1953
1918
  onApprovalEvent: async (payload) => {
1954
1919
  if (payload?.phase !== "requested") return;
1955
- await streamController.pushProgressLine(buildChannelProgressDraftLine({
1920
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
1956
1921
  event: "approval",
1957
1922
  phase: payload.phase,
1958
1923
  ...typeof payload?.title === "string" ? { title: payload.title } : {},
@@ -1963,7 +1928,7 @@ function createMSTeamsReplyDispatcher(params) {
1963
1928
  },
1964
1929
  onCommandOutput: async (payload) => {
1965
1930
  if (payload?.phase !== "end") return;
1966
- await streamController.pushProgressLine(buildChannelProgressDraftLine({
1931
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
1967
1932
  event: "command-output",
1968
1933
  ...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
1969
1934
  ...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
@@ -1976,7 +1941,7 @@ function createMSTeamsReplyDispatcher(params) {
1976
1941
  },
1977
1942
  onPatchSummary: async (payload) => {
1978
1943
  if (payload?.phase !== "end") return;
1979
- await streamController.pushProgressLine(buildChannelProgressDraftLine({
1944
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
1980
1945
  event: "patch",
1981
1946
  ...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
1982
1947
  ...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
@@ -2005,17 +1970,17 @@ function createMSTeamsReplyDispatcher(params) {
2005
1970
  }
2006
1971
  //#endregion
2007
1972
  //#region extensions/msteams/src/sent-message-cache.ts
2008
- const sentMessages = createPersistentDedupeCache({
1973
+ const sentMessages = (0, openclaw_plugin_sdk_dedupe_runtime.createPersistentDedupeCache)({
2009
1974
  globalKey: Symbol.for("openclaw.msteamsSentMessages"),
2010
1975
  ttlMs: 1440 * 60 * 1e3,
2011
1976
  maxSize: 2e4,
2012
1977
  persistent: {
2013
1978
  namespace: "msteams.sent-messages",
2014
1979
  maxEntries: 1e3,
2015
- openStore: (options) => getOptionalMSTeamsRuntime()?.state.openKeyedStore(options),
1980
+ openStore: (options) => require_runtime.getOptionalMSTeamsRuntime()?.state.openKeyedStore(options),
2016
1981
  logError: (error) => {
2017
1982
  try {
2018
- getOptionalMSTeamsRuntime()?.logging.getChildLogger({
1983
+ require_runtime.getOptionalMSTeamsRuntime()?.logging.getChildLogger({
2019
1984
  plugin: "msteams",
2020
1985
  feature: "sent-message-state"
2021
1986
  }).warn("Microsoft Teams persistent sent-message state failed", { error: String(error) });
@@ -2037,18 +2002,50 @@ async function wasMSTeamsMessageSentWithPersistence(params) {
2037
2002
  return await sentMessages.lookup(makeKey(params.conversationId, params.messageId));
2038
2003
  }
2039
2004
  //#endregion
2005
+ //#region extensions/msteams/src/team-identity.ts
2006
+ const teamGroupIdCache = /* @__PURE__ */ new Map();
2007
+ const TEAM_GROUP_ID_CACHE_MAX_ENTRIES = 500;
2008
+ function cacheTeamGroupId(conversationTeamId, groupId) {
2009
+ teamGroupIdCache.set(conversationTeamId, groupId);
2010
+ (0, openclaw_plugin_sdk_collection_runtime.pruneMapToMaxSize)(teamGroupIdCache, TEAM_GROUP_ID_CACHE_MAX_ENTRIES);
2011
+ }
2012
+ /** Resolve the Graph team GUID without ever treating a Bot Framework team ID as equivalent. */
2013
+ async function resolveTeamGroupId(params) {
2014
+ const activityGroupId = params.aadGroupId?.trim();
2015
+ if (activityGroupId) {
2016
+ cacheTeamGroupId(params.conversationTeamId, activityGroupId);
2017
+ return activityGroupId;
2018
+ }
2019
+ const cached = teamGroupIdCache.get(params.conversationTeamId);
2020
+ if (cached) return cached;
2021
+ const getTeamDetails = params.getTeamDetails;
2022
+ if (!getTeamDetails) return;
2023
+ const groupId = (await require_resolve_allowlist.withMSTeamsRequestDeadline({
2024
+ deadline: params.deadline,
2025
+ label: "MS Teams team details",
2026
+ work: () => getTeamDetails(params.conversationTeamId)
2027
+ })).aadGroupId?.trim();
2028
+ if (!groupId) return;
2029
+ cacheTeamGroupId(params.conversationTeamId, groupId);
2030
+ return groupId;
2031
+ }
2032
+ //#endregion
2040
2033
  //#region extensions/msteams/src/monitor-handler/inbound-media.ts
2034
+ function shouldAttemptMSTeamsGraphMediaFallback(params) {
2035
+ const conversationType = params.conversationType.trim().toLowerCase();
2036
+ return params.graphMediaFallback === true && (conversationType === "channel" || conversationType === "groupchat") && (params.htmlSummary?.htmlAttachments ?? 0) > 0;
2037
+ }
2041
2038
  function resolveMSTeamsInboundMediaBody(params) {
2042
2039
  const unavailableCount = Math.max(0, params.expectedMediaCount - params.mediaCount);
2043
2040
  if (unavailableCount === 0) return params.body;
2044
- return formatInboundMediaUnavailableText({
2041
+ return (0, openclaw_plugin_sdk_channel_inbound.formatInboundMediaUnavailableText)({
2045
2042
  body: params.mediaCount > 0 && params.body === params.mediaPlaceholder ? params.materializedMediaPlaceholder : params.body,
2046
2043
  mediaPlaceholder: params.mediaCount === 0 ? params.mediaPlaceholder : void 0,
2047
2044
  notice: `[msteams ${unavailableCount > 1 ? `${unavailableCount} attachments` : "attachment"} unavailable]`
2048
2045
  });
2049
2046
  }
2050
2047
  async function resolveMSTeamsInboundMedia(params) {
2051
- const { attachments, htmlSummary, maxBytes, tokenProvider, allowHosts, conversationType, conversationId, conversationMessageId, serviceUrl, activity, log, preserveFilenames } = params;
2048
+ const { attachments, htmlSummary, maxBytes, tokenProvider, allowHosts, conversationType, conversationId, conversationMessageId, teamAadGroupId, serviceUrl, activity, log, preserveFilenames } = params;
2052
2049
  let mediaList = await downloadMSTeamsAttachments({
2053
2050
  attachments,
2054
2051
  maxBytes,
@@ -2056,12 +2053,20 @@ async function resolveMSTeamsInboundMedia(params) {
2056
2053
  allowHosts,
2057
2054
  authAllowHosts: params.authAllowHosts,
2058
2055
  preserveFilenames,
2056
+ deadline: params.deadline,
2059
2057
  logger: log
2060
2058
  });
2061
2059
  if (mediaList.length === 0) {
2062
2060
  const attachmentIds = extractMSTeamsHtmlAttachmentIds(attachments);
2063
2061
  const hasHtmlFileAttachment = attachmentIds.length > 0;
2064
- if (hasHtmlFileAttachment && isBotFrameworkPersonalChatId(conversationId)) if (!serviceUrl) log.debug?.("bot framework attachment skipped (missing serviceUrl)", {
2062
+ const hasChannelOrGroupHtml = shouldAttemptMSTeamsGraphMediaFallback({
2063
+ conversationType,
2064
+ htmlSummary,
2065
+ graphMediaFallback: params.graphMediaFallback
2066
+ });
2067
+ const shouldFetchGraphMessage = hasHtmlFileAttachment || hasChannelOrGroupHtml;
2068
+ const isBotFrameworkPersonalChat = isBotFrameworkPersonalChatId(conversationId);
2069
+ if (hasHtmlFileAttachment && isBotFrameworkPersonalChat) if (!serviceUrl) log.debug?.("bot framework attachment skipped (missing serviceUrl)", {
2065
2070
  conversationType,
2066
2071
  conversationId
2067
2072
  });
@@ -2073,7 +2078,8 @@ async function resolveMSTeamsInboundMedia(params) {
2073
2078
  maxBytes,
2074
2079
  allowHosts,
2075
2080
  authAllowHosts: params.authAllowHosts,
2076
- preserveFilenames
2081
+ preserveFilenames,
2082
+ deadline: params.deadline
2077
2083
  });
2078
2084
  if (bfMedia.media.length > 0) mediaList = bfMedia.media;
2079
2085
  else log.debug?.("bot framework attachments fetch empty", {
@@ -2081,50 +2087,41 @@ async function resolveMSTeamsInboundMedia(params) {
2081
2087
  attachmentCount: bfMedia.attachmentCount ?? attachmentIds.length
2082
2088
  });
2083
2089
  }
2084
- if (hasHtmlFileAttachment && mediaList.length === 0 && !isBotFrameworkPersonalChatId(conversationId)) {
2085
- const messageUrls = buildMSTeamsGraphMessageUrls({
2090
+ if (shouldFetchGraphMessage && mediaList.length === 0 && !isBotFrameworkPersonalChat) {
2091
+ const graphTeamAadGroupId = conversationType.trim().toLowerCase() === "channel" && !teamAadGroupId ? await params.resolveTeamAadGroupId?.() : teamAadGroupId;
2092
+ const messageUrl = buildMSTeamsGraphMessageUrl({
2086
2093
  conversationType,
2087
2094
  conversationId,
2088
2095
  messageId: activity.id ?? void 0,
2089
- replyToId: activity.replyToId ?? void 0,
2090
- conversationMessageId,
2091
- channelData: activity.channelData
2096
+ threadRootMessageId: conversationMessageId ?? activity.replyToId,
2097
+ teamAadGroupId: graphTeamAadGroupId,
2098
+ channelId: activity.channelData?.channel?.id
2092
2099
  });
2093
- if (messageUrls.length === 0) log.debug?.("graph message url unavailable", {
2100
+ if (!messageUrl) log.debug?.("graph message url unavailable", {
2094
2101
  conversationType,
2095
2102
  hasChannelData: Boolean(activity.channelData),
2096
2103
  messageId: activity.id ?? void 0,
2097
2104
  replyToId: activity.replyToId ?? void 0
2098
2105
  });
2099
2106
  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
- }
2107
+ const graphMedia = await downloadMSTeamsGraphMedia({
2108
+ messageUrl,
2109
+ tokenProvider,
2110
+ maxBytes,
2111
+ allowHosts,
2112
+ authAllowHosts: params.authAllowHosts,
2113
+ preserveFilenames,
2114
+ deadline: params.deadline,
2115
+ logger: log
2116
+ });
2117
+ if (graphMedia.media.length > 0) mediaList = graphMedia.media;
2126
2118
  if (mediaList.length === 0) log.debug?.("graph media fetch empty", {
2127
- attempts,
2119
+ messageUrl,
2120
+ hostedStatus: graphMedia.hostedStatus,
2121
+ attachmentStatus: graphMedia.attachmentStatus,
2122
+ hostedCount: graphMedia.hostedCount,
2123
+ attachmentCount: graphMedia.attachmentCount,
2124
+ tokenError: graphMedia.tokenError,
2128
2125
  attachmentIdCount: attachmentIds.length
2129
2126
  });
2130
2127
  }
@@ -2143,9 +2140,7 @@ async function resolveMSTeamsInboundMedia(params) {
2143
2140
  //#region extensions/msteams/src/monitor-handler/message-handler.ts
2144
2141
  function extractTextFromHtmlAttachments(attachments) {
2145
2142
  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 : "";
2143
+ const raw = require_resolve_allowlist.extractHtmlFromAttachment(attachment);
2149
2144
  if (!raw) continue;
2150
2145
  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
2146
  if (text) return text;
@@ -2176,7 +2171,7 @@ function buildStoredConversationReference(params) {
2176
2171
  const clientInfo = activity.entities?.find((e) => e.type === "clientInfo");
2177
2172
  const tenantId = activity.channelData?.tenant?.id ?? conversation?.tenantId;
2178
2173
  const aadObjectId = from?.aadObjectId;
2179
- const serviceUrl = tryNormalizeBotFrameworkServiceUrl(activity.serviceUrl);
2174
+ const serviceUrl = require_resolve_allowlist.tryNormalizeBotFrameworkServiceUrl(activity.serviceUrl);
2180
2175
  return {
2181
2176
  activityId: activity.id,
2182
2177
  user: from ? {
@@ -2185,10 +2180,6 @@ function buildStoredConversationReference(params) {
2185
2180
  aadObjectId: from.aadObjectId
2186
2181
  } : void 0,
2187
2182
  agent,
2188
- bot: agent ? {
2189
- id: agent.id,
2190
- name: agent.name
2191
- } : void 0,
2192
2183
  conversation: {
2193
2184
  id: conversationId,
2194
2185
  conversationType,
@@ -2206,16 +2197,16 @@ function buildStoredConversationReference(params) {
2206
2197
  }
2207
2198
  function createMSTeamsMessageHandler(deps) {
2208
2199
  const { cfg, runtime, appId, app, tokenProvider, textLimit, mediaMaxBytes, conversationStore, pollStore, log } = deps;
2209
- const core = getMSTeamsRuntime();
2200
+ const core = require_runtime.getMSTeamsRuntime();
2210
2201
  const logVerboseMessage = (message) => {
2211
2202
  if (core.logging.shouldLogVerbose()) log.debug?.(message);
2212
2203
  };
2213
2204
  const msteamsCfg = cfg.channels?.msteams;
2214
- const contextVisibilityMode = resolveChannelContextVisibilityMode({
2205
+ const contextVisibilityMode = (0, openclaw_plugin_sdk_context_visibility_runtime.resolveChannelContextVisibilityMode)({
2215
2206
  cfg,
2216
2207
  channel: "msteams"
2217
2208
  });
2218
- const historyLimit = Math.max(0, msteamsCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT);
2209
+ const historyLimit = Math.max(0, msteamsCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? openclaw_plugin_sdk_reply_history.DEFAULT_GROUP_HISTORY_LIMIT);
2219
2210
  const conversationHistories = /* @__PURE__ */ new Map();
2220
2211
  const inboundDebounceMs = core.channel.debounce.resolveInboundDebounceMs({
2221
2212
  cfg,
@@ -2233,7 +2224,7 @@ function createMSTeamsMessageHandler(deps) {
2233
2224
  });
2234
2225
  const attachmentPlaceholder = attachmentPresentation.placeholder;
2235
2226
  const rawBody = text || attachmentPlaceholder;
2236
- const quoteInfo = extractMSTeamsQuoteInfo(attachments);
2227
+ const quoteInfo = require_channel.extractMSTeamsQuoteInfo(attachments);
2237
2228
  let quoteSenderId;
2238
2229
  let quoteSenderName;
2239
2230
  const from = activity.from;
@@ -2241,8 +2232,8 @@ function createMSTeamsMessageHandler(deps) {
2241
2232
  const attachmentTypes = attachments.map((att) => typeof att.contentType === "string" ? att.contentType : void 0).filter(Boolean).slice(0, 3);
2242
2233
  const htmlSummary = summarizeMSTeamsHtmlAttachments(attachments);
2243
2234
  log.info("received message", {
2244
- rawText: truncateUtf16Safe(rawText, 50),
2245
- text: truncateUtf16Safe(text, 50),
2235
+ rawText: (0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(rawText, 50),
2236
+ text: (0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(text, 50),
2246
2237
  attachments: attachments.length,
2247
2238
  attachmentTypes,
2248
2239
  from: from?.id,
@@ -2254,10 +2245,11 @@ function createMSTeamsMessageHandler(deps) {
2254
2245
  return;
2255
2246
  }
2256
2247
  const rawConversationId = conversation?.id ?? "";
2257
- const conversationId = normalizeMSTeamsConversationId(rawConversationId);
2258
- const conversationMessageId = extractMSTeamsConversationMessageId(rawConversationId);
2248
+ const conversationId = require_channel.normalizeMSTeamsConversationId(rawConversationId);
2249
+ const conversationMessageId = require_channel.extractMSTeamsConversationMessageId(rawConversationId);
2259
2250
  const conversationType = conversation?.conversationType ?? "personal";
2260
2251
  const teamId = activity.channelData?.team?.id;
2252
+ const graphChannelId = activity.channelData?.channel?.id?.trim() || conversationId;
2261
2253
  const conversationRef = buildStoredConversationReference({
2262
2254
  activity,
2263
2255
  conversationId,
@@ -2288,7 +2280,7 @@ function createMSTeamsMessageHandler(deps) {
2288
2280
  log.debug?.("dropping dm (dms disabled)");
2289
2281
  return;
2290
2282
  }
2291
- const allowMatch = resolveMSTeamsAllowlistMatch({
2283
+ const allowMatch = require_channel.resolveMSTeamsAllowlistMatch({
2292
2284
  allowFrom: effectiveDmAllowFrom,
2293
2285
  senderId,
2294
2286
  senderName,
@@ -2296,7 +2288,7 @@ function createMSTeamsMessageHandler(deps) {
2296
2288
  });
2297
2289
  if (senderAccess.decision === "pairing") {
2298
2290
  conversationStore.upsert(conversationId, conversationRef).catch((err) => {
2299
- log.debug?.("failed to save conversation reference", { error: formatUnknownError(err) });
2291
+ log.debug?.("failed to save conversation reference", { error: require_setup_surface.formatUnknownError(err) });
2300
2292
  });
2301
2293
  if (await pairing.upsertPairingRequest({
2302
2294
  id: senderId,
@@ -2309,7 +2301,7 @@ function createMSTeamsMessageHandler(deps) {
2309
2301
  log.debug?.("dropping dm (not allowlisted)", {
2310
2302
  sender: senderId,
2311
2303
  label: senderName,
2312
- allowlistMatch: formatAllowlistMatchMeta(allowMatch)
2304
+ allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
2313
2305
  });
2314
2306
  log.info("dropping dm (not allowlisted)", {
2315
2307
  sender: senderId,
@@ -2320,7 +2312,7 @@ function createMSTeamsMessageHandler(deps) {
2320
2312
  dmPolicy,
2321
2313
  groupPolicy
2322
2314
  }),
2323
- allowlistMatch: formatAllowlistMatchMeta(allowMatch)
2315
+ allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
2324
2316
  });
2325
2317
  return;
2326
2318
  }
@@ -2353,7 +2345,7 @@ function createMSTeamsMessageHandler(deps) {
2353
2345
  return;
2354
2346
  }
2355
2347
  if (!senderAccess.allowed && senderAccess.reasonCode === "group_policy_not_allowlisted") {
2356
- const allowMatch = resolveMSTeamsAllowlistMatch({
2348
+ const allowMatch = require_channel.resolveMSTeamsAllowlistMatch({
2357
2349
  allowFrom: effectiveGroupAllowFrom,
2358
2350
  senderId,
2359
2351
  senderName,
@@ -2362,18 +2354,18 @@ function createMSTeamsMessageHandler(deps) {
2362
2354
  log.debug?.("dropping group message (not in groupAllowFrom)", {
2363
2355
  sender: senderId,
2364
2356
  label: senderName,
2365
- allowlistMatch: formatAllowlistMatchMeta(allowMatch)
2357
+ allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
2366
2358
  });
2367
2359
  log.info("dropping group message (not in groupAllowFrom)", {
2368
2360
  sender: senderId,
2369
2361
  label: senderName,
2370
- allowlistMatch: formatAllowlistMatchMeta(allowMatch)
2362
+ allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
2371
2363
  });
2372
2364
  return;
2373
2365
  }
2374
2366
  }
2375
2367
  if (commandAccess.shouldBlockControlCommand) {
2376
- logInboundDrop({
2368
+ (0, openclaw_plugin_sdk_channel_inbound.logInboundDrop)({
2377
2369
  log: logVerboseMessage,
2378
2370
  channel: "msteams",
2379
2371
  reason: "control command (unauthorized)",
@@ -2382,9 +2374,9 @@ function createMSTeamsMessageHandler(deps) {
2382
2374
  return;
2383
2375
  }
2384
2376
  conversationStore.upsert(conversationId, conversationRef).catch((err) => {
2385
- log.debug?.("failed to save conversation reference", { error: formatUnknownError(err) });
2377
+ log.debug?.("failed to save conversation reference", { error: require_setup_surface.formatUnknownError(err) });
2386
2378
  });
2387
- const pollVote = extractMSTeamsPollVote(activity);
2379
+ const pollVote = require_polls.extractMSTeamsPollVote(activity);
2388
2380
  if (pollVote) {
2389
2381
  try {
2390
2382
  if (!await pollStore.recordVote({
@@ -2400,12 +2392,17 @@ function createMSTeamsMessageHandler(deps) {
2400
2392
  } catch (err) {
2401
2393
  log.error("failed to record poll vote", {
2402
2394
  pollId: pollVote.pollId,
2403
- error: formatUnknownError(err)
2395
+ error: require_setup_surface.formatUnknownError(err)
2404
2396
  });
2405
2397
  }
2406
2398
  return;
2407
2399
  }
2408
- if (!rawBody) {
2400
+ const mayRecoverGraphMedia = Boolean(htmlSummary?.attachmentIds.length) || shouldAttemptMSTeamsGraphMediaFallback({
2401
+ conversationType,
2402
+ htmlSummary: htmlSummary ?? void 0,
2403
+ graphMediaFallback: msteamsCfg?.graphMediaFallback
2404
+ });
2405
+ if (!rawBody && !mayRecoverGraphMedia) {
2409
2406
  log.debug?.("skipping empty message after stripping mentions");
2410
2407
  return;
2411
2408
  }
@@ -2420,13 +2417,13 @@ function createMSTeamsMessageHandler(deps) {
2420
2417
  id: isDirectMessage ? senderId : conversationId
2421
2418
  }
2422
2419
  });
2423
- route.sessionKey = resolveMSTeamsRouteSessionKey({
2420
+ route.sessionKey = require_channel.resolveMSTeamsRouteSessionKey({
2424
2421
  baseSessionKey: route.sessionKey,
2425
2422
  isChannel,
2426
2423
  conversationMessageId,
2427
2424
  replyToId: activity.replyToId
2428
2425
  });
2429
- const preview = sliceUtf16Safe(rawBody.replace(/\s+/g, " "), 0, 160);
2426
+ const preview = (0, openclaw_plugin_sdk_text_utility_runtime.sliceUtf16Safe)(rawBody.replace(/\s+/g, " "), 0, 160);
2430
2427
  const inboundLabel = isDirectMessage ? `Teams DM from ${senderName}` : `Teams message in ${conversationType} from ${senderName}`;
2431
2428
  const enqueuePrimaryMessageSystemEvent = () => core.system.enqueueSystemEvent(inboundLabel, {
2432
2429
  sessionKey: route.sessionKey,
@@ -2434,14 +2431,14 @@ function createMSTeamsMessageHandler(deps) {
2434
2431
  });
2435
2432
  const channelId = conversationId;
2436
2433
  const { teamConfig, channelConfig } = channelGate;
2437
- const { requireMention, replyStyle } = resolveMSTeamsReplyPolicy({
2434
+ const { requireMention, replyStyle } = require_channel.resolveMSTeamsReplyPolicy({
2438
2435
  isDirectMessage,
2439
2436
  globalConfig: msteamsCfg,
2440
2437
  teamConfig,
2441
2438
  channelConfig
2442
2439
  });
2443
- const timestamp = parseMSTeamsActivityTimestamp(activity.timestamp);
2444
- const mentionDecision = resolveInboundMentionDecision({
2440
+ const timestamp = require_channel.parseMSTeamsActivityTimestamp(activity.timestamp);
2441
+ const mentionDecision = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundMentionDecision)({
2445
2442
  facts: {
2446
2443
  canDetectMention: true,
2447
2444
  wasMentioned: params.wasMentioned,
@@ -2464,124 +2461,163 @@ function createMSTeamsMessageHandler(deps) {
2464
2461
  requireMention,
2465
2462
  mentioned
2466
2463
  });
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
- });
2464
+ if (rawBody) {
2465
+ enqueuePrimaryMessageSystemEvent();
2466
+ (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).record({
2467
+ historyKey: conversationId,
2468
+ limit: historyLimit,
2469
+ entry: {
2470
+ sender: senderName,
2471
+ body: rawBody,
2472
+ timestamp: timestamp?.getTime(),
2473
+ messageId: activity.id ?? void 0
2474
+ }
2475
+ });
2476
+ }
2478
2477
  return;
2479
2478
  }
2480
2479
  }
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
2480
+ const preprocessingDeadline = require_resolve_allowlist.createMSTeamsInboundDeadline();
2481
+ let teamAadGroupId = activity.channelData?.team?.aadGroupId?.trim() || void 0;
2482
+ const conversationTeamId = isChannel ? teamId : void 0;
2483
+ let teamGroupIdPromise;
2484
+ const resolveChannelTeamGroupId = async () => {
2485
+ if (!conversationTeamId) return;
2486
+ teamGroupIdPromise ??= resolveTeamGroupId({
2487
+ conversationTeamId,
2488
+ aadGroupId: teamAadGroupId,
2489
+ getTeamDetails: context.getTeamDetails,
2490
+ deadline: preprocessingDeadline
2491
+ }).catch((err) => {
2492
+ log.debug?.("failed to resolve Teams AAD group ID", {
2493
+ teamId: conversationTeamId,
2494
+ error: require_setup_surface.formatUnknownError(err)
2496
2495
  });
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
- }
2496
+ });
2497
+ teamAadGroupId = await teamGroupIdPromise;
2498
+ return teamAadGroupId;
2499
+ };
2500
+ let mediaList = [];
2501
+ try {
2502
+ mediaList = await require_resolve_allowlist.withMSTeamsRequestDeadline({
2503
+ deadline: preprocessingDeadline,
2504
+ label: "MS Teams inbound media",
2505
+ work: () => resolveMSTeamsInboundMedia({
2506
+ attachments,
2507
+ htmlSummary: htmlSummary ?? void 0,
2508
+ maxBytes: mediaMaxBytes,
2509
+ tokenProvider,
2510
+ allowHosts: msteamsCfg?.mediaAllowHosts,
2511
+ authAllowHosts: msteamsCfg?.mediaAuthAllowHosts,
2512
+ graphMediaFallback: msteamsCfg?.graphMediaFallback,
2513
+ conversationType,
2514
+ conversationId,
2515
+ conversationMessageId: conversationMessageId ?? void 0,
2516
+ teamAadGroupId,
2517
+ resolveTeamAadGroupId: resolveChannelTeamGroupId,
2518
+ serviceUrl: activity.serviceUrl,
2519
+ activity: {
2520
+ id: activity.id,
2521
+ replyToId: activity.replyToId,
2522
+ channelData: activity.channelData
2523
+ },
2524
+ log,
2525
+ deadline: preprocessingDeadline,
2526
+ preserveFilenames: cfg.media?.preserveFilenames
2527
+ })
2528
+ });
2529
+ } catch (err) {
2530
+ log.debug?.("failed to resolve inbound Teams media", { error: require_setup_surface.formatUnknownError(err) });
2507
2531
  }
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
2532
  const mediaPayload = buildMSTeamsMediaPayload(mediaList);
2528
2533
  const materializedMediaPlaceholder = resolveMSTeamsInboundAttachmentPresentation(mediaList.map((media) => ({
2529
2534
  contentType: media.contentType,
2530
2535
  name: media.path
2531
2536
  }))).placeholder;
2532
2537
  const agentBody = resolveMSTeamsInboundMediaBody({
2533
- body: rawBody,
2538
+ body: rawBody || materializedMediaPlaceholder,
2534
2539
  mediaPlaceholder: attachmentPlaceholder,
2535
2540
  materializedMediaPlaceholder,
2536
2541
  expectedMediaCount: attachmentPresentation.expectedMediaCount,
2537
2542
  mediaCount: mediaList.length
2538
2543
  });
2544
+ if (!agentBody) {
2545
+ log.debug?.("skipping empty message after Graph media recovery");
2546
+ return;
2547
+ }
2548
+ enqueuePrimaryMessageSystemEvent();
2549
+ teamAadGroupId = await resolveChannelTeamGroupId();
2550
+ let quoteBodyFull;
2551
+ const quoteMessageId = quoteInfo?.id;
2552
+ if (quoteMessageId && isDirectMessage && conversationId.startsWith("19:")) try {
2553
+ const graphToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
2554
+ deadline: preprocessingDeadline,
2555
+ label: "MS Teams quote token",
2556
+ work: () => tokenProvider.getAccessToken("https://graph.microsoft.com")
2557
+ });
2558
+ quoteBodyFull = await require_resolve_allowlist.withMSTeamsRequestDeadline({
2559
+ deadline: preprocessingDeadline,
2560
+ label: "MS Teams quote lookup",
2561
+ work: () => require_messenger.fetchChatMessageText(graphToken, conversationId, quoteMessageId, preprocessingDeadline)
2562
+ });
2563
+ } catch (err) {
2564
+ log.debug?.("failed to fetch full quoted message text", { error: require_setup_surface.formatUnknownError(err) });
2565
+ }
2539
2566
  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)]);
2567
+ const threadParentId = activity.replyToId;
2568
+ const channelGroupId = teamAadGroupId;
2569
+ if (threadParentId && isChannel && channelGroupId) try {
2570
+ const graphToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
2571
+ deadline: preprocessingDeadline,
2572
+ label: "MS Teams thread token",
2573
+ work: () => tokenProvider.getAccessToken("https://graph.microsoft.com")
2574
+ });
2575
+ const [parentResult, repliesResult] = await require_resolve_allowlist.withMSTeamsRequestDeadline({
2576
+ deadline: preprocessingDeadline,
2577
+ label: "MS Teams thread history",
2578
+ 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)])
2579
+ });
2544
2580
  const parentMsg = parentResult.status === "fulfilled" ? parentResult.value : void 0;
2545
2581
  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({
2582
+ if (parentResult.status === "rejected") log.debug?.("failed to fetch parent message", { error: require_setup_surface.formatUnknownError(parentResult.reason) });
2583
+ if (repliesResult.status === "rejected") log.debug?.("failed to fetch thread replies", { error: require_setup_surface.formatUnknownError(repliesResult.reason) });
2584
+ const isThreadSenderAllowed = (msg) => groupPolicy === "allowlist" ? require_channel.resolveMSTeamsAllowlistMatch({
2549
2585
  allowFrom: effectiveGroupAllowFrom,
2550
2586
  senderId: msg.from?.user?.id ?? "",
2551
2587
  senderName: msg.from?.user?.displayName,
2552
2588
  allowNameMatching
2553
2589
  }).allowed : true;
2554
2590
  const parentSummary = summarizeParentMessage(parentMsg);
2555
- const visibleParentMessages = parentMsg ? filterSupplementalContextItems({
2591
+ const visibleParentMessages = parentMsg ? (0, openclaw_plugin_sdk_context_visibility_runtime.filterSupplementalContextItems)({
2556
2592
  items: [parentMsg],
2557
2593
  mode: contextVisibilityMode,
2558
2594
  kind: "thread",
2559
2595
  isSenderAllowed: isThreadSenderAllowed
2560
2596
  }).items : [];
2561
- if (parentSummary && visibleParentMessages.length > 0 && shouldInjectParentContext(route.sessionKey, activity.replyToId)) {
2597
+ if (parentSummary && visibleParentMessages.length > 0 && shouldInjectParentContext(route.sessionKey, threadParentId)) {
2562
2598
  core.system.enqueueSystemEvent(formatParentContextEvent(parentSummary), {
2563
2599
  sessionKey: route.sessionKey,
2564
- contextKey: `msteams:thread-parent:${conversationId}:${activity.replyToId}`
2600
+ contextKey: `msteams:thread-parent:${conversationId}:${threadParentId}`
2565
2601
  });
2566
- markParentContextInjected(route.sessionKey, activity.replyToId);
2602
+ markParentContextInjected(route.sessionKey, threadParentId);
2567
2603
  }
2568
2604
  const allMessages = parentMsg ? [parentMsg, ...replies] : replies;
2569
2605
  quoteSenderId = parentMsg?.from?.user?.id ?? parentMsg?.from?.application?.id ?? void 0;
2570
2606
  quoteSenderName = parentMsg?.from?.user?.displayName ?? parentMsg?.from?.application?.displayName ?? quoteInfo?.sender;
2571
- const { items: threadMessages } = filterSupplementalContextItems({
2607
+ const { items: threadMessages } = (0, openclaw_plugin_sdk_context_visibility_runtime.filterSupplementalContextItems)({
2572
2608
  items: allMessages,
2573
2609
  mode: contextVisibilityMode,
2574
2610
  kind: "thread",
2575
2611
  isSenderAllowed: isThreadSenderAllowed
2576
2612
  });
2577
- const formatted = formatThreadContext(threadMessages, activity.id);
2613
+ const formatted = require_messenger.formatThreadContext(threadMessages, activity.id);
2578
2614
  if (formatted) threadContext = formatted;
2579
2615
  } catch (err) {
2580
- log.debug?.("failed to fetch thread history", { error: formatUnknownError(err) });
2616
+ log.debug?.("failed to fetch thread history", { error: require_setup_surface.formatUnknownError(err) });
2581
2617
  }
2582
2618
  quoteSenderName ??= quoteInfo?.sender;
2583
2619
  const envelopeFrom = isDirectMessage ? senderName : conversationType;
2584
- const { storePath, envelopeOptions, previousTimestamp } = resolveInboundSessionEnvelopeContext({
2620
+ const { storePath, envelopeOptions, previousTimestamp } = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundSessionEnvelopeContext)({
2585
2621
  cfg,
2586
2622
  agentId: route.agentId,
2587
2623
  sessionKey: route.sessionKey
@@ -2596,7 +2632,7 @@ function createMSTeamsMessageHandler(deps) {
2596
2632
  });
2597
2633
  const isRoomish = !isDirectMessage;
2598
2634
  const historyKey = isRoomish ? conversationId : void 0;
2599
- if (isRoomish && historyKey) combinedBody = createChannelHistoryWindow({ historyMap: conversationHistories }).buildPendingContext({
2635
+ if (isRoomish && historyKey) combinedBody = (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).buildPendingContext({
2600
2636
  historyKey,
2601
2637
  limit: historyLimit,
2602
2638
  currentMessage: combinedBody,
@@ -2608,26 +2644,26 @@ function createMSTeamsMessageHandler(deps) {
2608
2644
  envelope: envelopeOptions
2609
2645
  })
2610
2646
  });
2611
- const inboundHistory = isRoomish && historyKey && historyLimit > 0 ? createChannelHistoryWindow({ historyMap: conversationHistories }).buildInboundHistory({
2647
+ const inboundHistory = isRoomish && historyKey && historyLimit > 0 ? (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).buildInboundHistory({
2612
2648
  historyKey,
2613
2649
  limit: historyLimit
2614
2650
  }) : void 0;
2615
2651
  const commandBody = text.trim();
2616
- const quoteSenderAllowed = quoteInfo && quoteInfo.sender ? !isChannel || groupPolicy !== "allowlist" ? true : resolveMSTeamsAllowlistMatch({
2652
+ const quoteSenderAllowed = quoteInfo && quoteInfo.sender ? !isChannel || groupPolicy !== "allowlist" ? true : require_channel.resolveMSTeamsAllowlistMatch({
2617
2653
  allowFrom: effectiveGroupAllowFrom,
2618
2654
  senderId: quoteSenderId ?? "",
2619
2655
  senderName: quoteSenderName,
2620
2656
  allowNameMatching
2621
2657
  }).allowed : true;
2622
2658
  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({
2659
+ const nativeChannelId = isChannel && teamAadGroupId ? `${teamAadGroupId}/${graphChannelId}` : void 0;
2660
+ const ctxPayload = (0, openclaw_plugin_sdk_channel_inbound.buildChannelInboundEventContext)({
2625
2661
  channel: "msteams",
2626
2662
  finalize: core.channel.reply.finalizeInboundContext,
2627
2663
  contextVisibility: contextVisibilityMode,
2628
2664
  supplemental: { quote: quoteInfo ? {
2629
- id: activity.replyToId ?? void 0,
2630
- body: quoteInfo.body,
2665
+ id: quoteInfo.id ?? activity.replyToId ?? void 0,
2666
+ body: quoteBodyFull ?? quoteInfo.body,
2631
2667
  sender: quoteInfo.sender,
2632
2668
  senderAllowed: quoteSenderAllowed,
2633
2669
  isQuote: true
@@ -2668,7 +2704,7 @@ function createMSTeamsMessageHandler(deps) {
2668
2704
  canDetectMention: !isDirectMessage,
2669
2705
  wasMentioned: isDirectMessage || mentionDecision.effectiveWasMentioned
2670
2706
  },
2671
- commands: { authorized: commandAuthorized }
2707
+ commands: { authorized: commandAuthorized === true }
2672
2708
  },
2673
2709
  extra: {
2674
2710
  GroupSubject: !isDirectMessage ? conversationType : void 0,
@@ -2725,7 +2761,7 @@ function createMSTeamsMessageHandler(deps) {
2725
2761
  ctxPayload,
2726
2762
  recordInboundSession: core.channel.session.recordInboundSession,
2727
2763
  record: { onRecordError: (err) => {
2728
- logVerboseMessage(`msteams: failed updating session meta: ${formatUnknownError(err)}`);
2764
+ logVerboseMessage(`msteams: failed updating session meta: ${require_setup_surface.formatUnknownError(err)}`);
2729
2765
  } },
2730
2766
  history: {
2731
2767
  isGroup: isRoomish,
@@ -2737,7 +2773,7 @@ function createMSTeamsMessageHandler(deps) {
2737
2773
  dispatcher,
2738
2774
  onSettled: () => markDispatchIdle()
2739
2775
  }),
2740
- runDispatch: () => dispatchReplyFromConfigWithSettledDispatcher({
2776
+ runDispatch: () => (0, openclaw_plugin_sdk_channel_inbound.dispatchReplyFromConfigWithSettledDispatcher)({
2741
2777
  cfg,
2742
2778
  ctxPayload,
2743
2779
  dispatcher,
@@ -2750,8 +2786,8 @@ function createMSTeamsMessageHandler(deps) {
2750
2786
  });
2751
2787
  const dispatchResult = turnResult.dispatched ? turnResult.dispatchResult : void 0;
2752
2788
  const queuedFinal = dispatchResult?.queuedFinal ?? false;
2753
- const counts = resolveInboundReplyDispatchCounts(dispatchResult);
2754
- const hasFinalResponse = hasFinalInboundReplyDispatch(dispatchResult);
2789
+ const counts = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundReplyDispatchCounts)(dispatchResult);
2790
+ const hasFinalResponse = (0, openclaw_plugin_sdk_channel_inbound.hasFinalInboundReplyDispatch)(dispatchResult);
2755
2791
  log.info("dispatch complete", {
2756
2792
  queuedFinal,
2757
2793
  counts
@@ -2760,8 +2796,8 @@ function createMSTeamsMessageHandler(deps) {
2760
2796
  const finalCount = counts.final;
2761
2797
  logVerboseMessage(`msteams: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${teamsTo}`);
2762
2798
  } catch (err) {
2763
- log.error("dispatch failed", { error: formatUnknownError(err) });
2764
- runtime.error(`msteams dispatch failed: ${formatUnknownError(err)}`);
2799
+ log.error("dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
2800
+ runtime.error(`msteams dispatch failed: ${require_setup_surface.formatUnknownError(err)}`);
2765
2801
  try {
2766
2802
  await context.sendActivity("⚠️ Something went wrong. Please try again.");
2767
2803
  } catch {}
@@ -2770,7 +2806,7 @@ function createMSTeamsMessageHandler(deps) {
2770
2806
  const inboundDebouncer = core.channel.debounce.createInboundDebouncer({
2771
2807
  debounceMs: inboundDebounceMs,
2772
2808
  buildKey: (entry) => {
2773
- const conversationId = normalizeMSTeamsConversationId(entry.context.activity.conversation?.id ?? "");
2809
+ const conversationId = require_channel.normalizeMSTeamsConversationId(entry.context.activity.conversation?.id ?? "");
2774
2810
  const senderId = entry.context.activity.from?.aadObjectId ?? entry.context.activity.from?.id ?? "";
2775
2811
  if (!senderId || !conversationId) return null;
2776
2812
  return `msteams:${appId}:${conversationId}:${senderId}`;
@@ -2802,7 +2838,7 @@ function createMSTeamsMessageHandler(deps) {
2802
2838
  });
2803
2839
  },
2804
2840
  onError: (err) => {
2805
- runtime.error(`msteams debounce flush failed: ${formatUnknownError(err)}`);
2841
+ runtime.error(`msteams debounce flush failed: ${require_setup_surface.formatUnknownError(err)}`);
2806
2842
  }
2807
2843
  });
2808
2844
  return async function handleTeamsMessage(context) {
@@ -2811,9 +2847,9 @@ function createMSTeamsMessageHandler(deps) {
2811
2847
  const rawText = activity.text?.trim() ?? "";
2812
2848
  const htmlText = extractTextFromHtmlAttachments(attachments);
2813
2849
  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 ?? "");
2850
+ const text = require_channel.stripMSTeamsMentionTags(rawText || htmlText || valueText || "");
2851
+ const wasMentioned = require_channel.wasMSTeamsBotMentioned(activity);
2852
+ const conversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
2817
2853
  const replyToId = activity.replyToId ?? void 0;
2818
2854
  const implicitMentionKinds = conversationId && replyToId && await wasMSTeamsMessageSentWithPersistence({
2819
2855
  conversationId,
@@ -2831,29 +2867,13 @@ function createMSTeamsMessageHandler(deps) {
2831
2867
  }
2832
2868
  //#endregion
2833
2869
  //#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
2870
  /**
2851
2871
  * Create a handler for MS Teams reaction activities (reactionsAdded / reactionsRemoved).
2852
2872
  * The returned function accepts a turn context and a direction string.
2853
2873
  */
2854
2874
  function createMSTeamsReactionHandler(deps) {
2855
2875
  const { cfg, log } = deps;
2856
- const core = getMSTeamsRuntime();
2876
+ const core = require_runtime.getMSTeamsRuntime();
2857
2877
  const msteamsCfg = cfg.channels?.msteams;
2858
2878
  return async function handleReaction(context, direction) {
2859
2879
  const activity = context.activity;
@@ -2867,7 +2887,7 @@ function createMSTeamsReactionHandler(deps) {
2867
2887
  log.debug?.("reaction activity missing from.id; skipping");
2868
2888
  return;
2869
2889
  }
2870
- const conversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
2890
+ const conversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
2871
2891
  const conversationType = activity.conversation?.conversationType ?? "personal";
2872
2892
  const isGroupChat = conversationType === "groupChat" || activity.conversation?.isGroup === true;
2873
2893
  const isChannel = conversationType === "channel";
@@ -2900,7 +2920,7 @@ function createMSTeamsReactionHandler(deps) {
2900
2920
  const targetMessageId = activity.replyToId ?? "unknown";
2901
2921
  for (const reaction of reactions) {
2902
2922
  const reactionType = reaction.type ?? "unknown";
2903
- const emoji = mapReactionEmoji(reactionType);
2923
+ const emoji = require_messenger.resolveMSTeamsReactionEmoji(reactionType);
2904
2924
  const label = direction === "added" ? `Teams reaction ${emoji} added by ${senderName} on message ${targetMessageId}` : `Teams reaction ${emoji} removed by ${senderName} from message ${targetMessageId}`;
2905
2925
  log.info(`reaction ${direction}`, {
2906
2926
  sender: senderId,
@@ -3059,7 +3079,7 @@ function registerMSTeamsHandlers(handler, deps) {
3059
3079
  try {
3060
3080
  await handleTeamsMessage(context);
3061
3081
  } catch (err) {
3062
- deps.runtime.error(`msteams handler failed: ${formatUnknownError(err)}`);
3082
+ deps.runtime.error(`msteams handler failed: ${require_setup_surface.formatUnknownError(err)}`);
3063
3083
  }
3064
3084
  await next();
3065
3085
  });
@@ -3069,7 +3089,7 @@ function registerMSTeamsHandlers(handler, deps) {
3069
3089
  const botId = ctx.activity?.recipient?.id;
3070
3090
  const msteamsCfg = deps.cfg.channels?.msteams;
3071
3091
  for (const member of membersAdded) if (member.id === botId) {
3072
- const isPersonal = (normalizeOptionalLowercaseString(ctx.activity?.conversation?.conversationType) ?? "personal") === "personal";
3092
+ const isPersonal = ((0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(ctx.activity?.conversation?.conversationType) ?? "personal") === "personal";
3073
3093
  if (isPersonal && msteamsCfg?.welcomeCard !== false) {
3074
3094
  const card = buildWelcomeCard({
3075
3095
  botName: ctx.activity?.recipient?.name ?? void 0,
@@ -3085,7 +3105,7 @@ function registerMSTeamsHandlers(handler, deps) {
3085
3105
  });
3086
3106
  deps.log.info("sent welcome card");
3087
3107
  } catch (err) {
3088
- deps.log.debug?.("failed to send welcome card", { error: formatUnknownError(err) });
3108
+ deps.log.debug?.("failed to send welcome card", { error: require_setup_surface.formatUnknownError(err) });
3089
3109
  }
3090
3110
  } else if (!isPersonal && msteamsCfg?.groupWelcomeCard === true) {
3091
3111
  const botName = ctx.activity?.recipient?.name ?? void 0;
@@ -3093,7 +3113,7 @@ function registerMSTeamsHandlers(handler, deps) {
3093
3113
  await ctx.sendActivity(buildGroupWelcomeText(botName));
3094
3114
  deps.log.info("sent group welcome message");
3095
3115
  } catch (err) {
3096
- deps.log.debug?.("failed to send group welcome", { error: formatUnknownError(err) });
3116
+ deps.log.debug?.("failed to send group welcome", { error: require_setup_surface.formatUnknownError(err) });
3097
3117
  }
3098
3118
  } else deps.log.debug?.("skipping welcome (disabled by config or conversation type)");
3099
3119
  } else deps.log.debug?.("member added", { member: member.id });
@@ -3155,14 +3175,14 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3155
3175
  userComment = JSON.parse(value.actionValue.feedback).feedbackText || void 0;
3156
3176
  } catch {}
3157
3177
  const rawConversationId = activity.conversation?.id ?? "unknown";
3158
- const conversationId = normalizeMSTeamsConversationId(rawConversationId);
3178
+ const conversationId = require_channel.normalizeMSTeamsConversationId(rawConversationId);
3159
3179
  const senderId = activity.from?.aadObjectId ?? activity.from?.id ?? "unknown";
3160
3180
  const messageId = value.replyToId ?? activity.replyToId ?? "unknown";
3161
3181
  const isNegative = reaction === "dislike";
3162
- const convType = normalizeOptionalLowercaseString(activity.conversation?.conversationType);
3182
+ const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(activity.conversation?.conversationType);
3163
3183
  const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
3164
3184
  const isChannel = convType === "channel";
3165
- const core = getMSTeamsRuntime();
3185
+ const core = require_runtime.getMSTeamsRuntime();
3166
3186
  const route = core.channel.routing.resolveAgentRoute({
3167
3187
  cfg: deps.cfg,
3168
3188
  channel: "msteams",
@@ -3171,8 +3191,8 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3171
3191
  id: isDirectMessage ? senderId : conversationId
3172
3192
  }
3173
3193
  });
3174
- const feedbackThreadId = isChannel ? extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId ?? void 0 : void 0;
3175
- if (feedbackThreadId) route.sessionKey = resolveThreadSessionKeys({
3194
+ const feedbackThreadId = isChannel ? require_channel.extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId ?? void 0 : void 0;
3195
+ if (feedbackThreadId) route.sessionKey = (0, openclaw_plugin_sdk_routing.resolveThreadSessionKeys)({
3176
3196
  baseSessionKey: route.sessionKey,
3177
3197
  threadId: feedbackThreadId,
3178
3198
  parentSessionKey: route.sessionKey
@@ -3194,8 +3214,8 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3194
3214
  try {
3195
3215
  const storePath = core.channel.session.resolveStorePath(deps.cfg.session?.store, { agentId: route.agentId });
3196
3216
  const safeKey = route.sessionKey.replace(/[^a-zA-Z0-9_-]/g, "_");
3197
- await appendRegularFile({
3198
- filePath: path.join(storePath, `${safeKey}.jsonl`),
3217
+ await (0, openclaw_plugin_sdk_security_runtime.appendRegularFile)({
3218
+ filePath: node_path.default.join(storePath, `${safeKey}.jsonl`),
3199
3219
  content: `${JSON.stringify(feedbackEvent)}\n`,
3200
3220
  rejectSymlinkParents: true
3201
3221
  }).catch(() => {});
@@ -3211,10 +3231,6 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3211
3231
  id: activity.recipient.id,
3212
3232
  name: activity.recipient.name
3213
3233
  } : void 0,
3214
- bot: activity.recipient ? {
3215
- id: activity.recipient.id,
3216
- name: activity.recipient.name
3217
- } : void 0,
3218
3234
  conversation: {
3219
3235
  id: conversationId,
3220
3236
  conversationType: activity.conversation?.conversationType,
@@ -3236,7 +3252,7 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3236
3252
  userComment,
3237
3253
  log: deps.log
3238
3254
  }).catch((err) => {
3239
- deps.log.error("feedback reflection failed", { error: formatUnknownError(err) });
3255
+ deps.log.error("feedback reflection failed", { error: require_setup_surface.formatUnknownError(err) });
3240
3256
  });
3241
3257
  return true;
3242
3258
  }
@@ -3249,18 +3265,18 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3249
3265
  const expiredUploadMessage = "The file upload request has expired. Please try sending the file again.";
3250
3266
  const activity = context.activity;
3251
3267
  if (activity.type !== "invoke" || activity.name !== "fileConsent/invoke") return false;
3252
- const consentResponse = parseFileConsentInvoke(activity);
3268
+ const consentResponse = require_messenger.parseFileConsentInvoke(activity);
3253
3269
  if (!consentResponse) {
3254
3270
  log.debug?.("invalid file consent invoke", { value: activity.value });
3255
3271
  return false;
3256
3272
  }
3257
3273
  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);
3274
+ const inMemoryFile = require_messenger.getPendingUpload(uploadId);
3275
+ const fsFile = inMemoryFile ? void 0 : await require_messenger.getPendingUploadFs(uploadId);
3260
3276
  const pendingFile = inMemoryFile ?? fsFile;
3261
3277
  if (pendingFile) {
3262
- const pendingConversationId = normalizeMSTeamsConversationId(pendingFile.conversationId);
3263
- const invokeConversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
3278
+ const pendingConversationId = require_channel.normalizeMSTeamsConversationId(pendingFile.conversationId);
3279
+ const invokeConversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
3264
3280
  if (!invokeConversationId || pendingConversationId !== invokeConversationId) {
3265
3281
  log.info("file consent conversation mismatch", {
3266
3282
  uploadId,
@@ -3278,12 +3294,12 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3278
3294
  size: pendingFile.buffer.length
3279
3295
  });
3280
3296
  try {
3281
- await uploadToConsentUrl({
3297
+ await require_messenger.uploadToConsentUrl({
3282
3298
  url: consentResponse.uploadInfo.uploadUrl,
3283
3299
  buffer: pendingFile.buffer,
3284
3300
  contentType: pendingFile.contentType
3285
3301
  });
3286
- const fileInfoCard = buildFileInfoCard({
3302
+ const fileInfoCard = require_messenger.buildFileInfoCard({
3287
3303
  filename: consentResponse.uploadInfo.name,
3288
3304
  contentUrl: consentResponse.uploadInfo.contentUrl,
3289
3305
  uniqueId: consentResponse.uploadInfo.uniqueId,
@@ -3313,12 +3329,12 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3313
3329
  } catch (err) {
3314
3330
  log.error("file upload failed", {
3315
3331
  uploadId,
3316
- error: formatUnknownError(err)
3332
+ error: require_setup_surface.formatUnknownError(err)
3317
3333
  });
3318
3334
  await context.sendActivity("File upload failed. Please try again.");
3319
3335
  } finally {
3320
- removePendingUpload(uploadId);
3321
- await removePendingUploadFs(uploadId);
3336
+ require_messenger.removePendingUpload(uploadId);
3337
+ await require_messenger.removePendingUploadFs(uploadId);
3322
3338
  }
3323
3339
  } else {
3324
3340
  log.debug?.("pending file not found for consent", { uploadId });
@@ -3326,8 +3342,8 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3326
3342
  }
3327
3343
  else {
3328
3344
  log.debug?.("user declined file consent", { uploadId });
3329
- removePendingUpload(uploadId);
3330
- await removePendingUploadFs(uploadId);
3345
+ require_messenger.removePendingUpload(uploadId);
3346
+ await require_messenger.removePendingUploadFs(uploadId);
3331
3347
  }
3332
3348
  return true;
3333
3349
  }
@@ -3338,7 +3354,7 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3338
3354
  */
3339
3355
  async function runMSTeamsFileConsentInvokeHandler(context, log) {
3340
3356
  try {
3341
- await withRevokedProxyFallback({
3357
+ await require_messenger.withRevokedProxyFallback({
3342
3358
  run: async () => await handleMSTeamsFileConsentInvoke(context, log),
3343
3359
  onRevoked: async () => true,
3344
3360
  onRevokedLog: () => {
@@ -3346,7 +3362,7 @@ async function runMSTeamsFileConsentInvokeHandler(context, log) {
3346
3362
  }
3347
3363
  });
3348
3364
  } catch (err) {
3349
- log.debug?.("file consent handler error", { error: formatUnknownError(err) });
3365
+ log.debug?.("file consent handler error", { error: require_setup_surface.formatUnknownError(err) });
3350
3366
  }
3351
3367
  }
3352
3368
  //#endregion
@@ -3365,7 +3381,7 @@ function applyMSTeamsWebhookTimeouts(httpServer, opts) {
3365
3381
  //#endregion
3366
3382
  //#region extensions/msteams/src/monitor.ts
3367
3383
  async function monitorMSTeamsProvider(opts) {
3368
- const core = getMSTeamsRuntime();
3384
+ const core = require_runtime.getMSTeamsRuntime();
3369
3385
  const log = core.logging.getChildLogger({ name: "msteams" });
3370
3386
  let cfg = opts.cfg;
3371
3387
  let msteamsCfg = cfg.channels?.msteams;
@@ -3376,7 +3392,7 @@ async function monitorMSTeamsProvider(opts) {
3376
3392
  shutdown: async () => {}
3377
3393
  };
3378
3394
  }
3379
- const creds = resolveMSTeamsCredentials(msteamsCfg);
3395
+ const creds = require_resolve_allowlist.resolveMSTeamsCredentials(msteamsCfg);
3380
3396
  if (!creds) {
3381
3397
  log.error("msteams credentials not configured");
3382
3398
  return {
@@ -3392,26 +3408,22 @@ async function monitorMSTeamsProvider(opts) {
3392
3408
  throw new Error(`exit ${code}`);
3393
3409
  }
3394
3410
  };
3395
- let allowFrom = msteamsCfg.allowFrom;
3396
- let groupAllowFrom = msteamsCfg.groupAllowFrom;
3397
- let teamsConfig = msteamsCfg.teams;
3398
- const allowNameMatching = isDangerousNameMatchingEnabled(msteamsCfg);
3411
+ const configuredAllowFrom = msteamsCfg.allowFrom;
3412
+ const configuredGroupAllowFrom = msteamsCfg.groupAllowFrom;
3413
+ let allowFrom = require_resolve_allowlist.projectStableMSTeamsUserAllowlist(configuredAllowFrom);
3414
+ let groupAllowFrom = require_resolve_allowlist.projectStableMSTeamsUserAllowlist(configuredGroupAllowFrom);
3415
+ let teamsConfig = require_resolve_allowlist.projectStableMSTeamsTeamsConfig(msteamsCfg.teams);
3416
+ const allowNameMatching = (0, openclaw_plugin_sdk_dangerous_name_runtime.isDangerousNameMatchingEnabled)(msteamsCfg);
3399
3417
  const cleanAllowEntry = (entry) => entry.replace(/^(msteams|teams):/i, "").replace(/^user:/i, "").trim();
3400
3418
  const isStableUserId = (entry) => /^[0-9a-fA-F-]{16,}$/.test(entry);
3401
3419
  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
- };
3420
+ const isMutableUserEntry = (entry) => !isStableUserId(entry) && !/^accessGroup:/i.test(entry);
3409
3421
  const resolveAllowlistUsers = async (label, entries) => {
3410
3422
  if (entries.length === 0) return {
3411
3423
  additions: [],
3412
3424
  unresolved: []
3413
3425
  };
3414
- const resolved = await resolveMSTeamsUserAllowlist({
3426
+ const resolved = await require_resolve_allowlist.resolveMSTeamsUserAllowlist({
3415
3427
  cfg,
3416
3428
  entries
3417
3429
  });
@@ -3419,102 +3431,44 @@ async function monitorMSTeamsProvider(opts) {
3419
3431
  const unresolved = [];
3420
3432
  for (const entry of resolved) if (entry.resolved && entry.id) additions.push(entry.id);
3421
3433
  else unresolved.push(entry.input);
3422
- summarizeMapping(label, resolved.filter((entry) => entry.resolved && entry.id).map((entry) => `${entry.input}→${entry.id}`), unresolved, runtime);
3434
+ (0, openclaw_plugin_sdk_allow_from.summarizeMapping)(label, resolved.filter((entry) => entry.resolved && entry.id).map((entry) => `${entry.input}→${entry.id}`), unresolved, runtime);
3423
3435
  return {
3424
3436
  additions,
3425
3437
  unresolved
3426
3438
  };
3427
3439
  };
3428
3440
  try {
3429
- allowFrom = mergeStableUserIds(allowFrom);
3430
- if (Array.isArray(groupAllowFrom) && groupAllowFrom.length > 0) groupAllowFrom = mergeStableUserIds(groupAllowFrom);
3431
3441
  if (allowNameMatching) {
3432
- const allowEntries = cleanAllowEntries(allowFrom).filter((entry) => !isStableUserId(entry));
3442
+ const allowEntries = cleanAllowEntries(configuredAllowFrom).filter(isMutableUserEntry);
3433
3443
  if (allowEntries.length > 0) {
3434
3444
  const { additions } = await resolveAllowlistUsers("msteams users", allowEntries);
3435
- allowFrom = mergeAllowlist({
3445
+ allowFrom = (0, openclaw_plugin_sdk_allow_from.mergeAllowlist)({
3436
3446
  existing: allowFrom,
3437
3447
  additions
3438
3448
  });
3439
3449
  }
3440
- if (Array.isArray(groupAllowFrom) && groupAllowFrom.length > 0) {
3441
- const groupEntries = cleanAllowEntries(groupAllowFrom).filter((entry) => !isStableUserId(entry));
3450
+ if (Array.isArray(configuredGroupAllowFrom) && configuredGroupAllowFrom.length > 0) {
3451
+ const groupEntries = cleanAllowEntries(configuredGroupAllowFrom).filter(isMutableUserEntry);
3442
3452
  if (groupEntries.length > 0) {
3443
3453
  const { additions } = await resolveAllowlistUsers("msteams group users", groupEntries);
3444
- groupAllowFrom = mergeAllowlist({
3454
+ groupAllowFrom = (0, openclaw_plugin_sdk_allow_from.mergeAllowlist)({
3445
3455
  existing: groupAllowFrom,
3446
3456
  additions
3447
3457
  });
3448
3458
  }
3449
3459
  }
3450
3460
  }
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
- }
3461
+ if (msteamsCfg.teams && Object.keys(msteamsCfg.teams).length > 0) {
3462
+ const resolved = await require_resolve_allowlist.resolveMSTeamsTeamsConfig({
3463
+ cfg,
3464
+ teamIdMode: "bot-framework",
3465
+ teams: msteamsCfg.teams
3466
+ });
3467
+ teamsConfig = resolved.teams;
3468
+ (0, openclaw_plugin_sdk_allow_from.summarizeMapping)("msteams channels", resolved.mapping, resolved.unresolved, runtime);
3515
3469
  }
3516
3470
  } catch (err) {
3517
- runtime.error?.(`msteams resolve failed; falling back to raw config entries — allowlist members resolved via Graph may be missing. ${formatUnknownError(err)}`);
3471
+ runtime.error?.(`msteams resolve failed; mutable allowlist entries are disabled. ${require_setup_surface.formatUnknownError(err)}`);
3518
3472
  }
3519
3473
  msteamsCfg = {
3520
3474
  ...msteamsCfg,
@@ -3534,8 +3488,8 @@ async function monitorMSTeamsProvider(opts) {
3534
3488
  const MB = 1024 * 1024;
3535
3489
  const agentDefaults = cfg.agents?.defaults;
3536
3490
  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();
3491
+ const conversationStore = opts.conversationStore ?? require_polls.createMSTeamsConversationStoreState();
3492
+ const pollStore = opts.pollStore ?? require_polls.createMSTeamsPollStoreState();
3539
3493
  log.info(`starting provider (port ${port})`);
3540
3494
  const express = await import("express");
3541
3495
  const expressApp = express.default();
@@ -3547,7 +3501,7 @@ async function monitorMSTeamsProvider(opts) {
3547
3501
  }
3548
3502
  next();
3549
3503
  });
3550
- expressApp.use(express.json({ limit: DEFAULT_WEBHOOK_MAX_BODY_BYTES }));
3504
+ expressApp.use(express.json({ limit: openclaw_plugin_sdk_webhook_ingress.DEFAULT_WEBHOOK_MAX_BODY_BYTES }));
3551
3505
  expressApp.use((err, _req, res, next) => {
3552
3506
  if (err && typeof err === "object" && "status" in err && err.status === 413) {
3553
3507
  res.status(413).json({ error: "Payload too large" });
@@ -3556,11 +3510,12 @@ async function monitorMSTeamsProvider(opts) {
3556
3510
  next(err);
3557
3511
  });
3558
3512
  const configuredPath = msteamsCfg.webhook?.path ?? "/api/messages";
3559
- const { app } = await loadMSTeamsSdkWithAuth(creds, {
3560
- ...resolveMSTeamsSdkCloudOptions(msteamsCfg),
3561
- httpServerAdapter: await createMSTeamsExpressAdapter(expressApp),
3513
+ const ssoConnectionName = msteamsCfg.sso?.enabled && msteamsCfg.sso.connectionName ? msteamsCfg.sso.connectionName : void 0;
3514
+ const { app } = await require_resolve_allowlist.loadMSTeamsSdkWithAuth(creds, {
3515
+ ...require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg),
3516
+ httpServerAdapter: await require_resolve_allowlist.createMSTeamsExpressAdapter(expressApp),
3562
3517
  messagingEndpoint: configuredPath,
3563
- ...msteamsCfg.sso?.enabled && msteamsCfg.sso.connectionName ? { oauthDefaultConnectionName: msteamsCfg.sso.connectionName } : {}
3518
+ ...ssoConnectionName ? { oauthDefaultConnectionName: ssoConnectionName } : {}
3564
3519
  });
3565
3520
  if (configuredPath !== "/api/messages") {
3566
3521
  let warnedLegacyMessagesRoute = false;
@@ -3573,16 +3528,12 @@ async function monitorMSTeamsProvider(opts) {
3573
3528
  expressApp(req, res, next);
3574
3529
  });
3575
3530
  }
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
- }
3531
+ const tokenProvider = require_resolve_allowlist.createMSTeamsTokenProvider(app);
3532
+ const ssoDeps = ssoConnectionName ? {
3533
+ tokenStore: require_sso_token_store.createMSTeamsSsoTokenStoreFs(),
3534
+ connectionName: ssoConnectionName
3535
+ } : void 0;
3536
+ if (ssoDeps) log.debug?.("msteams sso enabled", { connectionName: ssoDeps.connectionName });
3586
3537
  const handler = buildActivityHandler();
3587
3538
  const handlerDeps = {
3588
3539
  cfg,
@@ -3594,15 +3545,14 @@ async function monitorMSTeamsProvider(opts) {
3594
3545
  mediaMaxBytes,
3595
3546
  conversationStore,
3596
3547
  pollStore,
3597
- log,
3598
- sso: ssoDeps
3548
+ log
3599
3549
  };
3600
3550
  registerMSTeamsHandlers(handler, handlerDeps);
3601
3551
  app.on("card.action", async (ctx) => {
3602
3552
  const adaptedCtx = adaptSdkContext(ctx, app);
3603
3553
  try {
3604
3554
  const activity = adaptedCtx.activity;
3605
- const vote = extractMSTeamsPollVote(activity);
3555
+ const vote = require_polls.extractMSTeamsPollVote(activity);
3606
3556
  if (vote) {
3607
3557
  const voterId = activity?.from?.aadObjectId ?? activity?.from?.id ?? "unknown";
3608
3558
  try {
@@ -3620,8 +3570,8 @@ async function monitorMSTeamsProvider(opts) {
3620
3570
  value: "Poll not found."
3621
3571
  };
3622
3572
  }
3623
- const pollConversationId = existingPoll.conversationId ? normalizeMSTeamsConversationId(existingPoll.conversationId) : void 0;
3624
- const activityConversationId = normalizeMSTeamsConversationId(activity?.conversation?.id ?? "");
3573
+ const pollConversationId = existingPoll.conversationId ? require_channel.normalizeMSTeamsConversationId(existingPoll.conversationId) : void 0;
3574
+ const activityConversationId = require_channel.normalizeMSTeamsConversationId(activity?.conversation?.id ?? "");
3625
3575
  if (pollConversationId && pollConversationId !== activityConversationId) {
3626
3576
  log.info("poll vote ignored (conversation mismatch)", {
3627
3577
  pollId: vote.pollId,
@@ -3658,7 +3608,7 @@ async function monitorMSTeamsProvider(opts) {
3658
3608
  } catch (err) {
3659
3609
  log.error("failed to record poll vote", {
3660
3610
  pollId: vote.pollId,
3661
- error: formatUnknownError(err)
3611
+ error: require_setup_surface.formatUnknownError(err)
3662
3612
  });
3663
3613
  return {
3664
3614
  statusCode: 500,
@@ -3675,7 +3625,7 @@ async function monitorMSTeamsProvider(opts) {
3675
3625
  }
3676
3626
  }
3677
3627
  handler.run(adaptedCtx).catch((err) => {
3678
- log.error("msteams card.action dispatch failed", { error: formatUnknownError(err) });
3628
+ log.error("msteams card.action dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
3679
3629
  });
3680
3630
  return {
3681
3631
  statusCode: 200,
@@ -3683,7 +3633,7 @@ async function monitorMSTeamsProvider(opts) {
3683
3633
  value: "OK"
3684
3634
  };
3685
3635
  } catch (err) {
3686
- log.error("msteams card.action failed", { error: formatUnknownError(err) });
3636
+ log.error("msteams card.action failed", { error: require_setup_surface.formatUnknownError(err) });
3687
3637
  return {
3688
3638
  statusCode: 500,
3689
3639
  type: "application/vnd.microsoft.error",
@@ -3751,7 +3701,7 @@ async function monitorMSTeamsProvider(opts) {
3751
3701
  hasExpiry: Boolean(ctx.token.expiration)
3752
3702
  });
3753
3703
  })().catch((err) => {
3754
- log.error("msteams sso token persistence failed", { error: formatUnknownError(err) });
3704
+ log.error("msteams sso token persistence failed", { error: require_setup_surface.formatUnknownError(err) });
3755
3705
  });
3756
3706
  });
3757
3707
  app.on("message.submit", async (ctx) => {
@@ -3768,39 +3718,31 @@ async function monitorMSTeamsProvider(opts) {
3768
3718
  }
3769
3719
  await handler.run(adaptedCtx);
3770
3720
  } catch (err) {
3771
- log.error("msteams webhook failed", { error: formatUnknownError(err) });
3721
+ log.error("msteams webhook failed", { error: require_setup_surface.formatUnknownError(err) });
3772
3722
  }
3773
3723
  });
3774
3724
  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);
3725
+ const httpServer = await new Promise((resolve, reject) => {
3726
+ const server = expressApp.listen(port, (err) => err ? reject(err) : resolve(server));
3727
+ }).catch((err) => {
3728
+ log.error("msteams server error", { error: require_setup_surface.formatUnknownError(err) });
3729
+ throw err;
3789
3730
  });
3731
+ log.info(`msteams provider started on port ${port}`);
3790
3732
  applyMSTeamsWebhookTimeouts(httpServer);
3791
3733
  httpServer.on("error", (err) => {
3792
- log.error("msteams server error", { error: formatUnknownError(err) });
3734
+ log.error("msteams server error", { error: require_setup_surface.formatUnknownError(err) });
3793
3735
  });
3794
3736
  const shutdown = async () => {
3795
3737
  log.info("shutting down msteams provider");
3796
3738
  return new Promise((resolve) => {
3797
3739
  httpServer.close((err) => {
3798
- if (err) log.debug?.("msteams server close error", { error: formatUnknownError(err) });
3740
+ if (err) log.debug?.("msteams server close error", { error: require_setup_surface.formatUnknownError(err) });
3799
3741
  resolve();
3800
3742
  });
3801
3743
  });
3802
3744
  };
3803
- await keepHttpServerTaskAlive({
3745
+ await (0, openclaw_plugin_sdk_channel_outbound.keepHttpServerTaskAlive)({
3804
3746
  server: httpServer,
3805
3747
  abortSignal: opts.abortSignal,
3806
3748
  onAbort: shutdown
@@ -3810,13 +3752,6 @@ async function monitorMSTeamsProvider(opts) {
3810
3752
  shutdown
3811
3753
  };
3812
3754
  }
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
3755
  /**
3821
3756
  * Build a minimal ActivityHandler-compatible object that supports
3822
3757
  * onMessage / onMembersAdded registration and a run() method.
@@ -3866,7 +3801,9 @@ function adaptSdkContext(ctx, app) {
3866
3801
  const sdkCtx = ctx ?? {};
3867
3802
  if (typeof sdkCtx.reply !== "function" && typeof sdkCtx.send !== "function") return ctx;
3868
3803
  const conversationId = sdkCtx.activity?.conversation?.id ?? "";
3869
- const activityApi = sdkCtx.api ?? app.api;
3804
+ const inboundApi = sdkCtx.api;
3805
+ const activityApi = inboundApi ?? app.api;
3806
+ const getTeamDetails = inboundApi ? (teamId) => inboundApi.teams.getById(teamId) : void 0;
3870
3807
  const conversationType = (sdkCtx.activity?.conversation?.conversationType ?? "").toLowerCase();
3871
3808
  const isThreadable = conversationType === "channel" || conversationType === "groupchat";
3872
3809
  const sendActivity = (activity) => isThreadable ? sdkCtx.reply(activity) : sdkCtx.send(activity);
@@ -3884,8 +3821,9 @@ function adaptSdkContext(ctx, app) {
3884
3821
  deleteActivity: async (activityId) => {
3885
3822
  return activityApi.conversations.activities(conversationId).delete(activityId);
3886
3823
  },
3824
+ getTeamDetails,
3887
3825
  stream: sdkCtx.stream
3888
3826
  });
3889
3827
  }
3890
3828
  //#endregion
3891
- export { monitorMSTeamsProvider };
3829
+ exports.monitorMSTeamsProvider = monitorMSTeamsProvider;