@openclaw/msteams 2026.5.12 → 2026.5.14-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.js CHANGED
@@ -1,3 +1,3 @@
1
- import { t as msteamsPlugin } from "./channel-BApPsQGS.js";
2
- import { i as msteamsSetupAdapter, n as openDelegatedOAuthUrl, r as createMSTeamsSetupWizardBase, t as msteamsSetupWizard } from "./setup-surface-Ce463w3t.js";
1
+ import { t as msteamsPlugin } from "./channel-C7J7iUD1.js";
2
+ import { i as msteamsSetupAdapter, n as openDelegatedOAuthUrl, r as createMSTeamsSetupWizardBase, t as msteamsSetupWizard } from "./setup-surface-D9_2tf9Q.js";
3
3
  export { createMSTeamsSetupWizardBase, msteamsPlugin, msteamsSetupAdapter, msteamsSetupWizard, openDelegatedOAuthUrl };
@@ -1,9 +1,9 @@
1
1
  import { o as buildProbeChannelStatusSummary, r as PAIRING_APPROVED_MESSAGE, s as chunkTextForOutbound, t as DEFAULT_ACCOUNT_ID, u as createDefaultChannelRuntimeState } from "./runtime-api-C3EIaIpt.js";
2
- import { h as resolveMSTeamsCredentials } from "./graph-users-BQJvcsX8.js";
3
- import { a as parseMSTeamsTeamChannelInput, c as resolveMSTeamsUserAllowlist, i as parseMSTeamsConversationId, n as normalizeMSTeamsMessagingTarget, r as normalizeMSTeamsUserInput, s as resolveMSTeamsChannelAllowlist, t as looksLikeMSTeamsTargetId } from "./resolve-allowlist-DPCTpYxi.js";
2
+ import { h as resolveMSTeamsCredentials } from "./graph-users-OieoCBxW.js";
3
+ import { a as parseMSTeamsTeamChannelInput, c as resolveMSTeamsUserAllowlist, i as parseMSTeamsConversationId, n as normalizeMSTeamsMessagingTarget, r as normalizeMSTeamsUserInput, s as resolveMSTeamsChannelAllowlist, t as looksLikeMSTeamsTargetId } from "./resolve-allowlist-DWp32o4l.js";
4
4
  import { t as MSTeamsChannelConfigSchema } from "./config-schema-DwOEthCC.js";
5
5
  import { n as resolveMSTeamsGroupToolPolicy } from "./policy-bM71GXRd.js";
6
- import { i as msteamsSetupAdapter, t as msteamsSetupWizard } from "./setup-surface-Ce463w3t.js";
6
+ import { i as msteamsSetupAdapter, t as msteamsSetupWizard } from "./setup-surface-D9_2tf9Q.js";
7
7
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
8
8
  import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
9
9
  import { createTopLevelChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
@@ -183,7 +183,7 @@ const collectMSTeamsSecurityWarnings = createAllowlistProviderGroupPolicyWarning
183
183
  resolveGroupPolicy: ({ cfg }) => cfg.channels?.msteams?.groupPolicy,
184
184
  collect: ({ groupPolicy }) => groupPolicy === "open" ? ["- MS Teams groups: groupPolicy=\"open\" allows any member to trigger (mention-gated). Set channels.msteams.groupPolicy=\"allowlist\" + channels.msteams.groupAllowFrom to restrict senders."] : []
185
185
  });
186
- const loadMSTeamsChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-CnDHBWml.js"), "msTeamsChannelRuntime");
186
+ const loadMSTeamsChannelRuntime = createLazyRuntimeNamedExport(() => import("./channel.runtime-B14HY4Hr.js"), "msTeamsChannelRuntime");
187
187
  const resolveMSTeamsChannelConfig = (cfg) => ({
188
188
  allowFrom: cfg.channels?.msteams?.allowFrom,
189
189
  defaultTo: cfg.channels?.msteams?.defaultTo
@@ -965,7 +965,7 @@ const msteamsPlugin = createChatChannelPlugin({
965
965
  })
966
966
  }),
967
967
  gateway: { startAccount: async (ctx) => {
968
- const { monitorMSTeamsProvider } = await import("./src-BEhrsfns.js");
968
+ const { monitorMSTeamsProvider } = await import("./src-CBRN2aXw.js");
969
969
  const port = ctx.cfg.channels?.msteams?.webhook?.port ?? 3978;
970
970
  ctx.setStatus({
971
971
  accountId: ctx.accountId,
@@ -1,2 +1,2 @@
1
- import { t as msteamsPlugin } from "./channel-BApPsQGS.js";
1
+ import { t as msteamsPlugin } from "./channel-C7J7iUD1.js";
2
2
  export { msteamsPlugin };
@@ -1,6 +1,6 @@
1
1
  import { s as chunkTextForOutbound } from "./runtime-api-C3EIaIpt.js";
2
- import { a as fetchGraphJson, c as normalizeQuery, d as postGraphJson, f as resolveGraphToken, i as fetchGraphAbsoluteUrl, l as patchGraphJson, n as deleteGraphRequest, o as listChannelsForTeam, r as escapeOData, s as listTeamsByName, t as searchGraphUsers, u as postGraphBetaJson } from "./graph-users-BQJvcsX8.js";
3
- import { S as createMSTeamsConversationStoreFs, a as sendMessageMSTeams, b as createMSTeamsPollStoreFs, i as sendAdaptiveCardMSTeams, n as deleteMessageMSTeams, o as sendPollMSTeams, r as editMessageMSTeams, t as probeMSTeams } from "./probe-4kXMWuAw.js";
2
+ import { a as fetchGraphJson, c as normalizeQuery, d as postGraphJson, f as resolveGraphToken, i as fetchGraphAbsoluteUrl, l as patchGraphJson, n as deleteGraphRequest, o as listChannelsForTeam, r as escapeOData, s as listTeamsByName, t as searchGraphUsers, u as postGraphBetaJson } from "./graph-users-OieoCBxW.js";
3
+ import { S as createMSTeamsConversationStoreFs, a as sendMessageMSTeams, b as createMSTeamsPollStoreFs, i as sendAdaptiveCardMSTeams, n as deleteMessageMSTeams, o as sendPollMSTeams, r as editMessageMSTeams, t as probeMSTeams } from "./probe-D3LwpZwU.js";
4
4
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
5
5
  import { createAttachedChannelResultAdapter } from "openclaw/plugin-sdk/channel-send-result";
6
6
  import { resolveOutboundSendDep } from "openclaw/plugin-sdk/outbound-send-deps";
@@ -180,8 +180,24 @@ function extractHtmlFromAttachment(att) {
180
180
  if (!isRecord$2(att.content)) return;
181
181
  return typeof att.content.text === "string" ? att.content.text : typeof att.content.body === "string" ? att.content.body : typeof att.content.content === "string" ? att.content.content : void 0;
182
182
  }
183
- function isLikelyBase64Payload(value) {
184
- return /^[A-Za-z0-9+/=\r\n]+$/.test(value);
183
+ function canonicalizeInlineBase64Payload(value) {
184
+ let cleaned = "";
185
+ let padding = 0;
186
+ let sawPadding = false;
187
+ for (let index = 0; index < value.length; index += 1) {
188
+ const code = value.charCodeAt(index);
189
+ if (code <= 32) continue;
190
+ if (code === 61) {
191
+ padding += 1;
192
+ if (padding > 2) return;
193
+ sawPadding = true;
194
+ cleaned += "=";
195
+ continue;
196
+ }
197
+ if (sawPadding || !(code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 43 || code === 47)) return;
198
+ cleaned += value[index];
199
+ }
200
+ return cleaned && cleaned.length % 4 === 0 ? cleaned : void 0;
185
201
  }
186
202
  function decodeDataImageWithLimits(src, opts) {
187
203
  const match = /^data:(image\/[a-z0-9.+-]+)?(;base64)?,(.*)$/i.exec(src);
@@ -194,12 +210,12 @@ function decodeDataImageWithLimits(src, opts) {
194
210
  candidate: null,
195
211
  estimatedBytes: 0
196
212
  };
197
- const payload = match[3] ?? "";
198
- if (!payload || !isLikelyBase64Payload(payload)) return {
213
+ const canonicalPayload = canonicalizeInlineBase64Payload(match[3] ?? "");
214
+ if (!canonicalPayload) return {
199
215
  candidate: null,
200
216
  estimatedBytes: 0
201
217
  };
202
- const estimatedBytes = estimateBase64DecodedBytes(payload);
218
+ const estimatedBytes = estimateBase64DecodedBytes(canonicalPayload);
203
219
  if (estimatedBytes <= 0) return {
204
220
  candidate: null,
205
221
  estimatedBytes: 0
@@ -212,7 +228,7 @@ function decodeDataImageWithLimits(src, opts) {
212
228
  return {
213
229
  candidate: {
214
230
  kind: "data",
215
- data: Buffer.from(payload, "base64"),
231
+ data: Buffer.from(canonicalPayload, "base64"),
216
232
  contentType,
217
233
  placeholder: "<media:image>"
218
234
  },
@@ -1,5 +1,5 @@
1
1
  import { C as normalizeStringEntries, E as resolveChannelMediaMaxBytes, M as getMSTeamsRuntime, d as detectMime, g as getFileExtension, m as extractOriginalFilename, p as extensionForMime, y as loadOutboundMediaFromUrl } from "./runtime-api-C3EIaIpt.js";
2
- import { C as loadMSTeamsSdkWithAuth, D as formatMSTeamsSendErrorHint, E as classifyMSTeamsSendError, O as formatUnknownError, S as createMSTeamsTokenProvider, _ as resolveMSTeamsStorePath, h as resolveMSTeamsCredentials, k as isRevokedProxyError, m as loadDelegatedTokens, w as buildUserAgent, x as createMSTeamsAdapter, y as readAccessToken } from "./graph-users-BQJvcsX8.js";
2
+ import { C as loadMSTeamsSdkWithAuth, D as formatMSTeamsSendErrorHint, E as classifyMSTeamsSendError, O as formatUnknownError, S as createMSTeamsTokenProvider, _ as resolveMSTeamsStorePath, h as resolveMSTeamsCredentials, k as isRevokedProxyError, m as loadDelegatedTokens, w as buildUserAgent, x as createMSTeamsAdapter, y as readAccessToken } from "./graph-users-OieoCBxW.js";
3
3
  import { i as resolveMSTeamsRouteConfig, r as resolveMSTeamsReplyPolicy } from "./policy-bM71GXRd.js";
4
4
  import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-message";
5
5
  import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -1,4 +1,4 @@
1
- import { c as normalizeQuery, f as resolveGraphToken, o as listChannelsForTeam, s as listTeamsByName, t as searchGraphUsers } from "./graph-users-BQJvcsX8.js";
1
+ import { c as normalizeQuery, f as resolveGraphToken, o as listChannelsForTeam, s as listTeamsByName, t as searchGraphUsers } from "./graph-users-OieoCBxW.js";
2
2
  import { mapAllowlistResolutionInputs } from "openclaw/plugin-sdk/allow-from";
3
3
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  //#region extensions/msteams/src/resolve-allowlist.ts
@@ -1,6 +1,6 @@
1
- import { h as resolveMSTeamsCredentials } from "./graph-users-BQJvcsX8.js";
1
+ import { h as resolveMSTeamsCredentials } from "./graph-users-OieoCBxW.js";
2
2
  import { t as MSTeamsChannelConfigSchema } from "./config-schema-DwOEthCC.js";
3
- import { i as msteamsSetupAdapter, t as msteamsSetupWizard } from "./setup-surface-Ce463w3t.js";
3
+ import { i as msteamsSetupAdapter, t as msteamsSetupWizard } from "./setup-surface-D9_2tf9Q.js";
4
4
  import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";
5
5
  import { formatAllowFromLowercase } from "openclaw/plugin-sdk/allow-from";
6
6
  import { createTopLevelChannelConfigAdapter } from "openclaw/plugin-sdk/channel-config-helpers";
@@ -1,5 +1,5 @@
1
- import { O as formatUnknownError, g as saveDelegatedTokens, h as resolveMSTeamsCredentials, p as hasConfiguredMSTeamsCredentials, v as normalizeSecretInputString } from "./graph-users-BQJvcsX8.js";
2
- import { c as resolveMSTeamsUserAllowlist, o as parseMSTeamsTeamEntry, s as resolveMSTeamsChannelAllowlist } from "./resolve-allowlist-DPCTpYxi.js";
1
+ import { O as formatUnknownError, g as saveDelegatedTokens, h as resolveMSTeamsCredentials, p as hasConfiguredMSTeamsCredentials, v as normalizeSecretInputString } from "./graph-users-OieoCBxW.js";
2
+ import { c as resolveMSTeamsUserAllowlist, o as parseMSTeamsTeamEntry, s as resolveMSTeamsChannelAllowlist } from "./resolve-allowlist-DWp32o4l.js";
3
3
  import { DEFAULT_ACCOUNT_ID, createStandardChannelSetupStatus, createTopLevelChannelAllowFromSetter, createTopLevelChannelDmPolicy, createTopLevelChannelGroupPolicySetter, mergeAllowFromEntries, splitSetupEntries } from "openclaw/plugin-sdk/setup";
4
4
  import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
5
5
  //#region extensions/msteams/src/setup-core.ts
@@ -1,16 +1,15 @@
1
1
  import { A as summarizeMapping, D as resolveDefaultGroupPolicy, E as resolveChannelMediaMaxBytes, M as getMSTeamsRuntime, N as getOptionalMSTeamsRuntime, _ as isDangerousNameMatchingEnabled, a as buildMediaPayload, b as logTypingFailure, c as createChannelMessageReplyPipeline, f as dispatchReplyFromConfigWithSettledDispatcher$1, l as createChannelPairingController, n as DEFAULT_WEBHOOK_MAX_BODY_BYTES, t as DEFAULT_ACCOUNT_ID, v as keepHttpServerTaskAlive, x as mergeAllowlist } from "./runtime-api-C3EIaIpt.js";
2
- import { A as ATTACHMENT_TAG_RE, B as isLikelyImageAttachment, C as loadMSTeamsSdkWithAuth, D as formatMSTeamsSendErrorHint, E as classifyMSTeamsSendError, F as estimateBase64DecodedBytes, G as resolveAttachmentFetchPolicy, H as isUrlAllowed, I as extractHtmlFromAttachment, J as safeFetchWithPolicy, K as resolveMediaSsrfPolicy, L as extractInlineImageCandidates, M as IMG_SRC_RE, N as applyAuthorizationHeaderForUrl, O as formatUnknownError, P as encodeGraphShareId, R as inferPlaceholder, S as createMSTeamsTokenProvider, T as ensureUserAgentHeader, U as normalizeContentType, V as isRecord$1, W as readNestedString, X as tryBuildGraphSharesUrlForSharedLink, Y as safeHostForUrl, _ as resolveMSTeamsStorePath, a as fetchGraphJson, b as createBotFrameworkJwtValidator, h as resolveMSTeamsCredentials, j as GRAPH_ROOT, q as resolveRequestUrl, w as buildUserAgent, x as createMSTeamsAdapter, z as isDownloadableAttachment } from "./graph-users-BQJvcsX8.js";
3
- import { c as resolveMSTeamsUserAllowlist, s as resolveMSTeamsChannelAllowlist } from "./resolve-allowlist-DPCTpYxi.js";
2
+ import { A as ATTACHMENT_TAG_RE, B as isLikelyImageAttachment, C as loadMSTeamsSdkWithAuth, D as formatMSTeamsSendErrorHint, E as classifyMSTeamsSendError, F as estimateBase64DecodedBytes, G as resolveAttachmentFetchPolicy, H as isUrlAllowed, I as extractHtmlFromAttachment, J as safeFetchWithPolicy, K as resolveMediaSsrfPolicy, L as extractInlineImageCandidates, M as IMG_SRC_RE, N as applyAuthorizationHeaderForUrl, O as formatUnknownError, P as encodeGraphShareId, R as inferPlaceholder, S as createMSTeamsTokenProvider, T as ensureUserAgentHeader, U as normalizeContentType, V as isRecord$1, W as readNestedString, X as tryBuildGraphSharesUrlForSharedLink, Y as safeHostForUrl, _ as resolveMSTeamsStorePath, a as fetchGraphJson, b as createBotFrameworkJwtValidator, h as resolveMSTeamsCredentials, j as GRAPH_ROOT, q as resolveRequestUrl, w as buildUserAgent, x as createMSTeamsAdapter, z as isDownloadableAttachment } from "./graph-users-OieoCBxW.js";
3
+ import { c as resolveMSTeamsUserAllowlist, s as resolveMSTeamsChannelAllowlist } from "./resolve-allowlist-DWp32o4l.js";
4
4
  import { i as resolveMSTeamsRouteConfig, r as resolveMSTeamsReplyPolicy, t as resolveMSTeamsAllowlistMatch } from "./policy-bM71GXRd.js";
5
- import { C as readJsonFile, S as createMSTeamsConversationStoreFs, T as writeJsonFile, _ as buildFileInfoCard, b as createMSTeamsPollStoreFs, 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 AI_GENERATED_ENTITY, v as parseFileConsentInvoke, w as withFileLock, x as extractMSTeamsPollVote, y as uploadToConsentUrl } from "./probe-4kXMWuAw.js";
5
+ import { C as readJsonFile, S as createMSTeamsConversationStoreFs, T as writeJsonFile, _ as buildFileInfoCard, b as createMSTeamsPollStoreFs, 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 AI_GENERATED_ENTITY, v as parseFileConsentInvoke, w as withFileLock, x as extractMSTeamsPollVote, y as uploadToConsentUrl } from "./probe-D3LwpZwU.js";
6
6
  import { formatAllowlistMatchMeta } from "openclaw/plugin-sdk/allow-from";
7
7
  import { createLiveMessageState, createPreviewMessageReceipt, defineFinalizableLivePreviewAdapter, deliverWithFinalizableLivePreviewAdapter, markLiveMessageFinalized } from "openclaw/plugin-sdk/channel-message";
8
8
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
9
9
  import { createDraftStreamLoop } from "openclaw/plugin-sdk/channel-lifecycle";
10
- import { readResponseWithLimit } from "openclaw/plugin-sdk/media-runtime";
10
+ import { saveResponseMedia } from "openclaw/plugin-sdk/media-runtime";
11
11
  import { dispatchReplyFromConfigWithSettledDispatcher, hasFinalInboundReplyDispatch, resolveInboundReplyDispatchCounts } from "openclaw/plugin-sdk/inbound-reply-dispatch";
12
12
  import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
13
- import { Buffer as Buffer$1 } from "node:buffer";
14
13
  import path from "node:path";
15
14
  import { appendRegularFile } from "openclaw/plugin-sdk/security-runtime";
16
15
  import { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
@@ -20,7 +19,7 @@ import { channelIngressRoutes, resolveStableChannelMessageIngress } from "opencl
20
19
  import { logInboundDrop, resolveInboundMentionDecision, resolveInboundSessionEnvelopeContext } from "openclaw/plugin-sdk/channel-inbound";
21
20
  import { filterSupplementalContextItems, resolveChannelContextVisibilityMode, shouldIncludeSupplementalContext } from "openclaw/plugin-sdk/context-visibility-runtime";
22
21
  import { DEFAULT_GROUP_HISTORY_LIMIT, buildPendingHistoryContextFromMap, recordPendingHistoryEntryIfEnabled } from "openclaw/plugin-sdk/reply-history";
23
- import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, resolveChannelPreviewStreamMode, resolveChannelProgressDraftMaxLines, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress } from "openclaw/plugin-sdk/channel-streaming";
22
+ import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, normalizeChannelProgressDraftLineIdentity, resolveChannelPreviewStreamMode, resolveChannelProgressDraftMaxLines, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress } from "openclaw/plugin-sdk/channel-streaming";
24
23
  //#region extensions/msteams/src/feedback-reflection-prompt.ts
25
24
  /** Max chars of the thumbed-down response to include in the reflection prompt. */
26
25
  const MAX_RESPONSE_CHARS = 500;
@@ -624,7 +623,7 @@ async function fetchBotFrameworkAttachmentInfo(params) {
624
623
  return;
625
624
  }
626
625
  }
627
- async function fetchBotFrameworkAttachmentView(params) {
626
+ async function saveBotFrameworkAttachmentView(params) {
628
627
  const url = `${normalizeServiceUrl(params.serviceUrl)}/v3/attachments/${encodeURIComponent(params.attachmentId)}/views/${encodeURIComponent(params.viewId)}`;
629
628
  let response;
630
629
  try {
@@ -646,12 +645,16 @@ async function fetchBotFrameworkAttachmentView(params) {
646
645
  const contentLength = response.headers.get("content-length");
647
646
  if (contentLength && Number(contentLength) > params.maxBytes) return;
648
647
  try {
649
- const arrayBuffer = await response.arrayBuffer();
650
- const buffer = Buffer$1.from(arrayBuffer);
651
- if (buffer.byteLength > params.maxBytes) return;
652
- return buffer;
648
+ return await getMSTeamsRuntime().channel.media.saveResponseMedia(response, {
649
+ sourceUrl: url,
650
+ filePathHint: params.fileNameHint,
651
+ maxBytes: params.maxBytes,
652
+ fallbackContentType: params.contentTypeHint,
653
+ subdir: "inbound",
654
+ originalFilename: params.preserveFilenames ? params.fileNameHint : void 0
655
+ });
653
656
  } catch (err) {
654
- params.logger?.warn?.("msteams botFramework attachmentView body read failed", { error: err instanceof Error ? err.message : String(err) });
657
+ params.logger?.warn?.("msteams botFramework attachmentView save failed", { error: err instanceof Error ? err.message : String(err) });
655
658
  return;
656
659
  }
657
660
  }
@@ -691,40 +694,31 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
691
694
  const viewId = typeof candidateView?.viewId === "string" && candidateView.viewId ? candidateView.viewId : void 0;
692
695
  if (!viewId) return;
693
696
  if (typeof candidateView?.size === "number" && candidateView.size > 0 && candidateView.size > params.maxBytes) return;
694
- const buffer = await fetchBotFrameworkAttachmentView({
697
+ const fileNameHint = typeof params.fileNameHint === "string" && params.fileNameHint || typeof info.name === "string" && info.name || void 0;
698
+ const contentTypeHint = typeof params.contentTypeHint === "string" && params.contentTypeHint || typeof info.type === "string" && info.type || void 0;
699
+ const saved = await saveBotFrameworkAttachmentView({
695
700
  serviceUrl: params.serviceUrl,
696
701
  attachmentId: params.attachmentId,
697
702
  viewId,
698
703
  accessToken,
699
704
  maxBytes: params.maxBytes,
705
+ fileNameHint,
706
+ contentTypeHint,
707
+ preserveFilenames: params.preserveFilenames,
700
708
  policy,
701
709
  fetchFn: params.fetchFn,
702
710
  resolveFn: params.resolveFn,
703
711
  logger: params.logger
704
712
  });
705
- if (!buffer) return;
706
- const fileNameHint = typeof params.fileNameHint === "string" && params.fileNameHint || typeof info.name === "string" && info.name || void 0;
707
- const contentTypeHint = typeof params.contentTypeHint === "string" && params.contentTypeHint || typeof info.type === "string" && info.type || void 0;
708
- const mime = await getMSTeamsRuntime().media.detectMime({
709
- buffer,
710
- headerMime: contentTypeHint,
711
- filePath: fileNameHint
712
- });
713
- try {
714
- const originalFilename = params.preserveFilenames ? fileNameHint : void 0;
715
- const saved = await getMSTeamsRuntime().channel.media.saveMediaBuffer(buffer, mime ?? contentTypeHint, "inbound", params.maxBytes, originalFilename);
716
- return {
717
- path: saved.path,
713
+ if (!saved) return;
714
+ return {
715
+ path: saved.path,
716
+ contentType: saved.contentType,
717
+ placeholder: inferPlaceholder({
718
718
  contentType: saved.contentType,
719
- placeholder: inferPlaceholder({
720
- contentType: saved.contentType,
721
- fileName: fileNameHint
722
- })
723
- };
724
- } catch (err) {
725
- params.logger?.warn?.("msteams botFramework save failed", { error: err instanceof Error ? err.message : String(err) });
726
- return;
727
- }
719
+ fileName: fileNameHint
720
+ })
721
+ };
728
722
  }
729
723
  /**
730
724
  * Download media for every attachment referenced by a Bot Framework personal
@@ -780,8 +774,8 @@ async function downloadMSTeamsBotFrameworkAttachments(params) {
780
774
  * Direct fetch path used when the caller's `fetchImpl` has already validated
781
775
  * the URL against a hostname allowlist (for example `safeFetchWithPolicy`).
782
776
  *
783
- * Bypasses the strict SSRF dispatcher on `fetchRemoteMedia` because:
784
- * 1. The pinned undici dispatcher used by `fetchRemoteMedia` is incompatible
777
+ * Bypasses the strict SSRF dispatcher on `readRemoteMediaBuffer` because:
778
+ * 1. The pinned undici dispatcher used by `readRemoteMediaBuffer` is incompatible
785
779
  * with Node 24+'s built-in undici v7 (fails with "invalid onRequestStart
786
780
  * method"), which silently breaks SharePoint/OneDrive downloads. See
787
781
  * issue #63396.
@@ -789,43 +783,35 @@ async function downloadMSTeamsBotFrameworkAttachments(params) {
789
783
  * (`safeFetch` validates every redirect hop against the hostname
790
784
  * allowlist before following).
791
785
  */
792
- async function fetchRemoteMediaDirect(params) {
793
- const response = await params.fetchImpl(params.url, { redirect: "follow" });
794
- if (!response.ok) {
795
- const statusText = response.statusText ? ` ${response.statusText}` : "";
796
- throw new Error(`HTTP ${response.status}${statusText}`);
797
- }
798
- const contentLength = response.headers.get("content-length");
799
- if (contentLength) {
800
- const length = Number(contentLength);
801
- if (Number.isFinite(length) && length > params.maxBytes) throw new Error(`content length ${length} exceeds maxBytes ${params.maxBytes}`);
802
- }
803
- return {
804
- buffer: await readResponseWithLimit(response, params.maxBytes, { onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`payload size ${size} exceeds maxBytes ${maxBytes}`) }),
805
- contentType: response.headers.get("content-type") ?? void 0
806
- };
786
+ async function saveRemoteMediaDirect(params) {
787
+ return await saveResponseMedia(await params.fetchImpl(params.url, { redirect: "follow" }), {
788
+ sourceUrl: params.url,
789
+ filePathHint: params.filePathHint,
790
+ maxBytes: params.maxBytes,
791
+ fallbackContentType: params.contentTypeHint,
792
+ originalFilename: params.originalFilename
793
+ });
807
794
  }
808
795
  async function downloadAndStoreMSTeamsRemoteMedia(params) {
809
- let fetched;
810
- if (params.useDirectFetch && params.fetchImpl) fetched = await fetchRemoteMediaDirect({
796
+ const originalFilename = params.preserveFilenames ? params.filePathHint : void 0;
797
+ let saved;
798
+ if (params.useDirectFetch && params.fetchImpl) saved = await saveRemoteMediaDirect({
811
799
  url: params.url,
800
+ filePathHint: params.filePathHint,
812
801
  fetchImpl: params.fetchImpl,
813
- maxBytes: params.maxBytes
802
+ maxBytes: params.maxBytes,
803
+ contentTypeHint: params.contentTypeHint,
804
+ originalFilename
814
805
  });
815
- else fetched = await getMSTeamsRuntime().channel.media.fetchRemoteMedia({
806
+ else saved = await getMSTeamsRuntime().channel.media.saveRemoteMedia({
816
807
  url: params.url,
817
808
  fetchImpl: params.fetchImpl,
818
809
  filePathHint: params.filePathHint,
819
810
  maxBytes: params.maxBytes,
820
- ssrfPolicy: params.ssrfPolicy
811
+ ssrfPolicy: params.ssrfPolicy,
812
+ fallbackContentType: params.contentTypeHint,
813
+ originalFilename
821
814
  });
822
- const mime = await getMSTeamsRuntime().media.detectMime({
823
- buffer: fetched.buffer,
824
- headerMime: fetched.contentType ?? params.contentTypeHint,
825
- filePath: params.filePathHint
826
- });
827
- const originalFilename = params.preserveFilenames ? params.filePathHint : void 0;
828
- const saved = await getMSTeamsRuntime().channel.media.saveMediaBuffer(fetched.buffer, mime ?? params.contentTypeHint, "inbound", params.maxBytes, originalFilename);
829
815
  return {
830
816
  path: saved.path,
831
817
  contentType: saved.contentType,
@@ -1106,28 +1092,38 @@ async function downloadGraphHostedContent(params) {
1106
1092
  params.logger?.warn?.("msteams graph hostedContent base64 decode failed", { error: err instanceof Error ? err.message : String(err) });
1107
1093
  continue;
1108
1094
  }
1109
- } else if (item.id) try {
1110
- const { response: valRes, release } = await fetchWithSsrFGuard({
1111
- url: `${params.messageUrl}/hostedContents/${encodeURIComponent(item.id)}/$value`,
1112
- fetchImpl: params.fetchFn ?? fetch,
1113
- init: { headers: ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
1114
- policy: params.ssrfPolicy,
1115
- auditContext: "msteams.graph.hostedContent.value"
1116
- });
1095
+ } else if (item.id) {
1117
1096
  try {
1118
- if (!valRes.ok) continue;
1119
- const cl = valRes.headers.get("content-length");
1120
- if (cl && Number(cl) > params.maxBytes) continue;
1121
- const ab = await valRes.arrayBuffer();
1122
- buffer = Buffer.from(ab);
1123
- } finally {
1124
- await release();
1097
+ const valueUrl = `${params.messageUrl}/hostedContents/${encodeURIComponent(item.id)}/$value`;
1098
+ const { response: valRes, release } = await fetchWithSsrFGuard({
1099
+ url: valueUrl,
1100
+ fetchImpl: params.fetchFn ?? fetch,
1101
+ init: { headers: ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
1102
+ policy: params.ssrfPolicy,
1103
+ auditContext: "msteams.graph.hostedContent.value"
1104
+ });
1105
+ try {
1106
+ if (!valRes.ok) continue;
1107
+ const saved = await getMSTeamsRuntime().channel.media.saveResponseMedia(valRes, {
1108
+ sourceUrl: valueUrl,
1109
+ maxBytes: params.maxBytes,
1110
+ fallbackContentType: item.contentType ?? void 0,
1111
+ subdir: "inbound"
1112
+ });
1113
+ out.push({
1114
+ path: saved.path,
1115
+ contentType: saved.contentType,
1116
+ placeholder: inferPlaceholder({ contentType: saved.contentType })
1117
+ });
1118
+ } finally {
1119
+ await release();
1120
+ }
1121
+ } catch (err) {
1122
+ params.logger?.warn?.("msteams graph hostedContent value fetch failed", { error: err instanceof Error ? err.message : String(err) });
1123
+ continue;
1125
1124
  }
1126
- } catch (err) {
1127
- params.logger?.warn?.("msteams graph hostedContent value fetch failed", { error: err instanceof Error ? err.message : String(err) });
1128
1125
  continue;
1129
- }
1130
- else continue;
1126
+ } else continue;
1131
1127
  if (buffer.byteLength > params.maxBytes) continue;
1132
1128
  const mime = await getMSTeamsRuntime().media.detectMime({
1133
1129
  buffer,
@@ -1824,13 +1820,8 @@ function createTeamsReplyStreamController(params) {
1824
1820
  if (!stream || streamMode !== "progress") return;
1825
1821
  if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
1826
1822
  if (shouldStreamPreviewToolProgress) {
1827
- const normalized = normalizeProgressLineIdentity(line);
1828
- if (normalized) {
1829
- if (normalizeProgressLineIdentity(progressLines.at(-1)) !== normalized) {
1830
- const progressLine = typeof line === "object" && line !== void 0 ? line : normalized;
1831
- progressLines = [...progressLines, progressLine].slice(-resolveChannelProgressDraftMaxLines(params.msteamsConfig));
1832
- }
1833
- }
1823
+ const normalized = normalizeChannelProgressDraftLineIdentity(line);
1824
+ if (normalized) progressLines = mergeChannelProgressDraftLine(progressLines, typeof line === "object" && line !== void 0 ? line : normalized, { maxLines: resolveChannelProgressDraftMaxLines(params.msteamsConfig) });
1834
1825
  }
1835
1826
  await noteProgressWork();
1836
1827
  };
@@ -1961,9 +1952,6 @@ function createTeamsReplyStreamController(params) {
1961
1952
  }
1962
1953
  };
1963
1954
  }
1964
- function normalizeProgressLineIdentity(line) {
1965
- return (typeof line === "string" ? line : line?.text)?.replace(/\s+/g, " ").trim() ?? "";
1966
- }
1967
1955
  //#endregion
1968
1956
  //#region extensions/msteams/src/reply-dispatcher.ts
1969
1957
  function createMSTeamsReplyDispatcher(params) {
@@ -2214,6 +2202,7 @@ function createMSTeamsReplyDispatcher(params) {
2214
2202
  onItemEvent: async (payload) => {
2215
2203
  await streamController.pushProgressLine(buildChannelProgressDraftLineForEntry(msteamsCfg, {
2216
2204
  event: "item",
2205
+ itemId: payload.itemId,
2217
2206
  itemKind: payload.kind,
2218
2207
  title: payload.title,
2219
2208
  name: payload.name,
package/dist/test-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as msteamsPlugin } from "./channel-BApPsQGS.js";
1
+ import { t as msteamsPlugin } from "./channel-C7J7iUD1.js";
2
2
  export { msteamsPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/msteams",
3
- "version": "2026.5.12",
3
+ "version": "2026.5.14-beta.1",
4
4
  "description": "OpenClaw Microsoft Teams channel plugin",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,7 +22,7 @@
22
22
  "openclaw": "workspace:*"
23
23
  },
24
24
  "peerDependencies": {
25
- "openclaw": ">=2026.5.12"
25
+ "openclaw": ">=2026.5.14-beta.1"
26
26
  },
27
27
  "peerDependenciesMeta": {
28
28
  "openclaw": {
@@ -58,10 +58,10 @@
58
58
  "minHostVersion": ">=2026.4.10"
59
59
  },
60
60
  "compat": {
61
- "pluginApi": ">=2026.5.12"
61
+ "pluginApi": ">=2026.5.14-beta.1"
62
62
  },
63
63
  "build": {
64
- "openclawVersion": "2026.5.12"
64
+ "openclawVersion": "2026.5.14-beta.1"
65
65
  },
66
66
  "release": {
67
67
  "publishToClawHub": true,