@openclaw/msteams 2026.7.2-beta.7 → 2026.8.1-beta.3

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.
@@ -1,22 +1,23 @@
1
- const require_polls = require("./polls-BWKNpBSg.cjs");
1
+ const require_rolldown_runtime = require("./rolldown-runtime-D6vf50IK.cjs");
2
2
  const require_runtime = require("./runtime-gZTAuFux.cjs");
3
3
  require("./runtime-api.cjs");
4
- const require_resolve_allowlist = require("./resolve-allowlist-CEj9sABO.cjs");
5
- const require_channel = require("./channel-C7QxRjBW.cjs");
6
- const require_setup_surface = require("./setup-surface-ByWjaXEn.cjs");
7
- const require_sso_token_store = require("./sso-token-store-TKYk_Ms7.cjs");
8
- const require_messenger = require("./messenger-DTiaLTDO.cjs");
4
+ const require_resolve_allowlist = require("./resolve-allowlist-CIfWUwuc.cjs");
5
+ const require_channel = require("./channel-KpWENcgK.cjs");
6
+ const require_channel_setup = require("./channel.setup-mOFtsfYz.cjs");
7
+ const require_polls = require("./polls-DvuJvaDG.cjs");
8
+ const require_sso_token_store = require("./sso-token-store-ChhO1f4i.cjs");
9
+ const require_messenger = require("./messenger-IISPSOvj.cjs");
9
10
  let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
10
11
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
11
12
  let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
13
+ let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-http");
12
14
  let openclaw_plugin_sdk_media_runtime = require("openclaw/plugin-sdk/media-runtime");
13
15
  let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
14
- let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-http");
15
16
  let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-runtime");
16
17
  let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text-utility-runtime");
17
18
  let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
18
19
  let node_crypto = require("node:crypto");
19
- node_crypto = require_polls.__toESM(node_crypto, 1);
20
+ node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
20
21
  let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
21
22
  let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
22
23
  let openclaw_plugin_sdk_channel_ingress_runtime = require("openclaw/plugin-sdk/channel-ingress-runtime");
@@ -26,7 +27,9 @@ let openclaw_plugin_sdk_agent_runtime = require("openclaw/plugin-sdk/agent-runti
26
27
  let openclaw_plugin_sdk_extension_shared = require("openclaw/plugin-sdk/extension-shared");
27
28
  let openclaw_plugin_sdk_plugin_runtime = require("openclaw/plugin-sdk/plugin-runtime");
28
29
  let openclaw_plugin_sdk_dedupe_runtime = require("openclaw/plugin-sdk/dedupe-runtime");
30
+ let openclaw_plugin_sdk_plugin_state_runtime = require("openclaw/plugin-sdk/plugin-state-runtime");
29
31
  let openclaw_plugin_sdk_collection_runtime = require("openclaw/plugin-sdk/collection-runtime");
32
+ let openclaw_plugin_sdk_gateway_runtime = require("openclaw/plugin-sdk/gateway-runtime");
30
33
  let openclaw_plugin_sdk_channel_pairing = require("openclaw/plugin-sdk/channel-pairing");
31
34
  let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
32
35
  let openclaw_plugin_sdk_runtime_group_policy = require("openclaw/plugin-sdk/runtime-group-policy");
@@ -86,11 +89,11 @@ async function runFeedbackReflection(params) {
86
89
  thumbedDownResponse: params.thumbedDownResponse,
87
90
  userComment: params.userComment,
88
91
  cooldownMs,
89
- onRecordError: (err) => log.debug?.("reflection session record failed", { error: require_setup_surface.formatUnknownError(err) }),
90
- onDispatchError: (err) => log.debug?.("reflection reply error", { error: require_setup_surface.formatUnknownError(err) })
92
+ onRecordError: (err) => log.debug?.("reflection session record failed", { error: require_channel_setup.formatUnknownError(err) }),
93
+ onDispatchError: (err) => log.debug?.("reflection reply error", { error: require_channel_setup.formatUnknownError(err) })
91
94
  });
92
95
  } catch (err) {
93
- log.error("reflection dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
96
+ log.error("reflection dispatch failed", { error: require_channel_setup.formatUnknownError(err) });
94
97
  return;
95
98
  }
96
99
  if (reflection.status === "cooldown") {
@@ -113,7 +116,7 @@ async function runFeedbackReflection(params) {
113
116
  learning: reflection.learning
114
117
  });
115
118
  } catch (err) {
116
- log.debug?.("failed to store reflection learning", { error: require_setup_surface.formatUnknownError(err) });
119
+ log.debug?.("failed to store reflection learning", { error: require_channel_setup.formatUnknownError(err) });
117
120
  }
118
121
  const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationRef.conversation?.conversationType);
119
122
  if (!(conversationType === "personal" && reflection.followUp && Boolean(reflection.userMessage))) {
@@ -130,7 +133,7 @@ async function runFeedbackReflection(params) {
130
133
  }, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(cfg.channels?.msteams) });
131
134
  log.info("sent reflection follow-up", { sessionKey });
132
135
  } catch (err) {
133
- log.debug?.("failed to send reflection follow-up", { error: require_setup_surface.formatUnknownError(err) });
136
+ log.debug?.("failed to send reflection follow-up", { error: require_channel_setup.formatUnknownError(err) });
134
137
  }
135
138
  }
136
139
  //#endregion
@@ -237,50 +240,53 @@ async function resolveMSTeamsSenderAccess(params) {
237
240
  channelName: activity.channelData?.channel?.name,
238
241
  allowNameMatching
239
242
  });
240
- return {
241
- ...await (0, openclaw_plugin_sdk_channel_ingress_runtime.resolveStableChannelMessageIngress)({
242
- channelId: "msteams",
243
- accountId: pairing.accountId,
244
- identity: msteamsIngressIdentity,
245
- cfg: params.cfg,
246
- readStoreAllowFrom: pairing.readAllowFromStore,
247
- subject: {
248
- stableId: senderId,
249
- aliases: {
250
- "sender-name": senderName,
251
- ...!isDirectMessage ? { "conversation-id": conversationId } : {}
252
- }
253
- },
254
- conversation: {
255
- kind: isDirectMessage ? "direct" : convType === "channel" ? "channel" : "group",
256
- id: conversationId,
257
- parentId: activity.channelData?.team?.id
258
- },
259
- route: (0, openclaw_plugin_sdk_channel_ingress_runtime.channelIngressRoutes)(!isDirectMessage && channelGate.allowlistConfigured && {
260
- id: "msteams:team-channel",
261
- kind: "nestedAllowlist",
262
- allowed: channelGate.allowed,
263
- precedence: 0,
264
- matchId: "msteams-route",
265
- ...channelGate.allowed && groupPolicy === "allowlist" ? {
266
- senderPolicy: "deny-when-empty",
267
- senderAllowFromSource: "effective-group"
268
- } : {}
269
- }),
270
- dmPolicy,
271
- groupPolicy,
272
- policy: {
273
- groupAllowFromFallbackToAllowFrom: true,
274
- mutableIdentifierMatching: allowNameMatching ? "enabled" : "disabled"
275
- },
276
- allowFrom: configuredDmAllowFrom,
277
- groupAllowFrom,
278
- command: {
279
- allowTextCommands: true,
280
- hasControlCommand: params.hasControlCommand === true,
281
- directGroupAllowFrom: isDirectMessage ? "effective" : "none"
243
+ const resolved = await (0, openclaw_plugin_sdk_channel_ingress_runtime.resolveStableChannelMessageIngress)({
244
+ channelId: "msteams",
245
+ accountId: pairing.accountId,
246
+ identity: msteamsIngressIdentity,
247
+ cfg: params.cfg,
248
+ readStoreAllowFrom: pairing.readAllowFromStore,
249
+ subject: {
250
+ stableId: senderId,
251
+ aliases: {
252
+ "sender-name": senderName,
253
+ ...!isDirectMessage ? { "conversation-id": conversationId } : {}
282
254
  }
255
+ },
256
+ conversation: {
257
+ kind: isDirectMessage ? "direct" : convType === "channel" ? "channel" : "group",
258
+ id: conversationId,
259
+ threadId: params.conversationThreadId
260
+ },
261
+ ...params.contextBinding ? { contextBinding: params.contextBinding } : {},
262
+ route: (0, openclaw_plugin_sdk_channel_ingress_runtime.channelIngressRoutes)(!isDirectMessage && channelGate.allowlistConfigured && {
263
+ id: "msteams:team-channel",
264
+ kind: "nestedAllowlist",
265
+ allowed: channelGate.allowed,
266
+ precedence: 0,
267
+ matchId: "msteams-route",
268
+ ...channelGate.allowed && groupPolicy === "allowlist" ? {
269
+ senderPolicy: "deny-when-empty",
270
+ senderAllowFromSource: "effective-group"
271
+ } : {}
283
272
  }),
273
+ dmPolicy,
274
+ groupPolicy,
275
+ policy: {
276
+ groupAllowFromFallbackToAllowFrom: true,
277
+ mutableIdentifierMatching: allowNameMatching ? "enabled" : "disabled"
278
+ },
279
+ allowFrom: configuredDmAllowFrom,
280
+ groupAllowFrom,
281
+ command: {
282
+ allowTextCommands: true,
283
+ hasControlCommand: params.hasControlCommand === true,
284
+ directGroupAllowFrom: isDirectMessage ? "effective" : "none"
285
+ }
286
+ });
287
+ return {
288
+ ...resolved,
289
+ channelIngress: resolved,
284
290
  pairing,
285
291
  isDirectMessage,
286
292
  conversationId,
@@ -325,7 +331,7 @@ async function admitMSTeamsMessage(params) {
325
331
  });
326
332
  if (senderAccess.decision === "pairing") {
327
333
  params.conversationStore.upsert(params.conversationId, params.conversationRef).catch((err) => {
328
- params.log.debug?.("failed to save conversation reference", { error: require_setup_surface.formatUnknownError(err) });
334
+ params.log.debug?.("failed to save conversation reference", { error: require_channel_setup.formatUnknownError(err) });
329
335
  });
330
336
  if (await pairing.upsertPairingRequest({
331
337
  id: senderId,
@@ -381,7 +387,7 @@ async function admitMSTeamsMessage(params) {
381
387
  params.log.debug?.("dropping group message (groupPolicy: allowlist, no allowlist)", { conversationId: params.conversationId });
382
388
  return null;
383
389
  }
384
- if (!senderAccess.allowed && senderAccess.reasonCode === "group_policy_not_allowlisted") {
390
+ if (!senderAccess.allowed) {
385
391
  const allowMatch = require_channel.resolveMSTeamsAllowlistMatch({
386
392
  allowFrom: effectiveGroupAllowFrom,
387
393
  senderId,
@@ -411,7 +417,7 @@ async function admitMSTeamsMessage(params) {
411
417
  return null;
412
418
  }
413
419
  params.conversationStore.upsert(params.conversationId, params.conversationRef).catch((err) => {
414
- params.log.debug?.("failed to save conversation reference", { error: require_setup_surface.formatUnknownError(err) });
420
+ params.log.debug?.("failed to save conversation reference", { error: require_channel_setup.formatUnknownError(err) });
415
421
  });
416
422
  return {
417
423
  ...access,
@@ -473,7 +479,7 @@ async function fetchBotFrameworkAttachmentInfo(params) {
473
479
  timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
474
480
  });
475
481
  } catch (err) {
476
- params.logger?.warn?.("msteams botFramework attachmentInfo fetch failed", { error: err instanceof Error ? err.message : String(err) });
482
+ params.logger?.warn?.("msteams botFramework attachmentInfo fetch failed", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
477
483
  return;
478
484
  }
479
485
  if (!response.ok) {
@@ -484,7 +490,7 @@ async function fetchBotFrameworkAttachmentInfo(params) {
484
490
  try {
485
491
  return await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(response, "msteams botFramework attachmentInfo");
486
492
  } catch (err) {
487
- params.logger?.warn?.("msteams botFramework attachmentInfo parse failed", { error: err instanceof Error ? err.message : String(err) });
493
+ params.logger?.warn?.("msteams botFramework attachmentInfo parse failed", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
488
494
  return;
489
495
  }
490
496
  }
@@ -506,7 +512,7 @@ async function saveBotFrameworkAttachmentView(params) {
506
512
  timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
507
513
  });
508
514
  } catch (err) {
509
- params.logger?.warn?.("msteams botFramework attachmentView fetch failed", { error: err instanceof Error ? err.message : String(err) });
515
+ params.logger?.warn?.("msteams botFramework attachmentView fetch failed", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
510
516
  return;
511
517
  }
512
518
  if (!response.ok) {
@@ -519,7 +525,7 @@ async function saveBotFrameworkAttachmentView(params) {
519
525
  contentLength = (0, openclaw_plugin_sdk_media_runtime.parseMediaContentLength)(response.headers.get("content-length"));
520
526
  } catch (err) {
521
527
  await response.body?.cancel().catch(() => void 0);
522
- params.logger?.warn?.("msteams botFramework attachmentView invalid content-length", { error: err instanceof Error ? err.message : String(err) });
528
+ params.logger?.warn?.("msteams botFramework attachmentView invalid content-length", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
523
529
  return;
524
530
  }
525
531
  if (contentLength !== null && contentLength > params.maxBytes) {
@@ -536,7 +542,7 @@ async function saveBotFrameworkAttachmentView(params) {
536
542
  originalFilename: params.preserveFilenames ? params.fileNameHint : void 0
537
543
  });
538
544
  } catch (err) {
539
- params.logger?.warn?.("msteams botFramework attachmentView save failed", { error: err instanceof Error ? err.message : String(err) });
545
+ params.logger?.warn?.("msteams botFramework attachmentView save failed", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
540
546
  return;
541
547
  } finally {
542
548
  await response.body?.cancel().catch(() => void 0);
@@ -564,7 +570,7 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
564
570
  work: () => tokenProvider.getAccessToken(BOT_FRAMEWORK_SCOPE)
565
571
  });
566
572
  } catch (err) {
567
- params.logger?.warn?.("msteams botFramework token acquisition failed", { error: err instanceof Error ? err.message : String(err) });
573
+ params.logger?.warn?.("msteams botFramework token acquisition failed", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
568
574
  return;
569
575
  }
570
576
  if (!accessToken) return;
@@ -665,7 +671,7 @@ async function downloadMSTeamsBotFrameworkAttachments(params) {
665
671
  sourceId: attachmentId
666
672
  });
667
673
  params.logger?.warn?.("msteams botFramework attachment download failed", {
668
- error: err instanceof Error ? err.message : String(err),
674
+ error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err),
669
675
  attachmentId
670
676
  });
671
677
  }
@@ -878,9 +884,6 @@ function scopeCandidatesForUrl(url) {
878
884
  return ["https://api.botframework.com", "https://graph.microsoft.com"];
879
885
  }
880
886
  }
881
- function isRedirectStatus(status) {
882
- return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
883
- }
884
887
  async function resolveInlineDataImageMime(inline) {
885
888
  const mime = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(await require_runtime.getMSTeamsRuntime().media.detectMime({
886
889
  buffer: inline.data,
@@ -903,7 +906,7 @@ async function fetchWithAuthFallback(params) {
903
906
  const tokenProvider = params.tokenProvider;
904
907
  if (firstAttempt.status !== 401 && firstAttempt.status !== 403) return firstAttempt;
905
908
  if (!require_resolve_allowlist.isUrlAllowed(params.url, params.policy.authAllowHosts)) return firstAttempt;
906
- await firstAttempt.body?.cancel();
909
+ let fallbackAttempt = firstAttempt;
907
910
  const scopes = scopeCandidatesForUrl(params.url);
908
911
  const fetchFn = params.fetchFn ?? fetch;
909
912
  for (const scope of scopes) try {
@@ -926,15 +929,11 @@ async function fetchWithAuthFallback(params) {
926
929
  resolveFn: params.resolveFn,
927
930
  timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
928
931
  });
929
- if (authAttempt.ok) return authAttempt;
930
- if (isRedirectStatus(authAttempt.status)) return authAttempt;
931
- if (authAttempt.status !== 401 && authAttempt.status !== 403) {
932
- await authAttempt.body?.cancel();
933
- continue;
934
- }
935
- await authAttempt.body?.cancel();
932
+ await fallbackAttempt.body?.cancel().catch(() => void 0);
933
+ if (authAttempt.ok || require_resolve_allowlist.isRedirectStatus(authAttempt.status)) return authAttempt;
934
+ fallbackAttempt = authAttempt;
936
935
  } catch {}
937
- return firstAttempt;
936
+ return fallbackAttempt;
938
937
  }
939
938
  /**
940
939
  * Download all file attachments from a Teams message (images, documents, etc.).
@@ -1015,7 +1014,7 @@ async function downloadMSTeamsAttachments(params) {
1015
1014
  }, candidate.sourceId));
1016
1015
  } catch (err) {
1017
1016
  out.push(withSourceId({ kind: candidate.mediaKind }, candidate.sourceId));
1018
- params.logger?.warn?.("msteams inline attachment decode failed", { error: err instanceof Error ? err.message : String(err) });
1017
+ params.logger?.warn?.("msteams inline attachment decode failed", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
1019
1018
  }
1020
1019
  continue;
1021
1020
  }
@@ -1047,7 +1046,7 @@ async function downloadMSTeamsAttachments(params) {
1047
1046
  out.push(withSourceId(media, candidate.sourceId));
1048
1047
  } catch (err) {
1049
1048
  out.push(withSourceId({ kind: candidate.mediaKind }, candidate.sourceId));
1050
- const msg = err instanceof Error ? err.message : String(err);
1049
+ const msg = (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err);
1051
1050
  params.logger?.warn?.(`msteams attachment download failed host=${safeHostForLog(candidate.url)} error=${msg}`);
1052
1051
  }
1053
1052
  }
@@ -1086,6 +1085,10 @@ function buildMSTeamsGraphMessageUrl(params) {
1086
1085
  if (!chatId) return;
1087
1086
  return `${require_resolve_allowlist.GRAPH_ROOT}/chats/${encodeURIComponent(chatId)}/messages/${encodeURIComponent(messageId)}`;
1088
1087
  }
1088
+ async function releaseGraphResponse(response, release) {
1089
+ if (!response.bodyUsed) response.body?.cancel().catch(() => void 0);
1090
+ await release();
1091
+ }
1089
1092
  async function fetchGraphCollection(params) {
1090
1093
  const fetchFn = params.fetchFn ?? fetch;
1091
1094
  const { response, release } = await (0, openclaw_plugin_sdk_ssrf_runtime.fetchWithSsrFGuard)({
@@ -1098,13 +1101,10 @@ async function fetchGraphCollection(params) {
1098
1101
  });
1099
1102
  try {
1100
1103
  const status = response.status;
1101
- if (!response.ok) {
1102
- await response.body?.cancel().catch(() => void 0);
1103
- return {
1104
- status,
1105
- items: []
1106
- };
1107
- }
1104
+ if (!response.ok) return {
1105
+ status,
1106
+ items: []
1107
+ };
1108
1108
  try {
1109
1109
  return {
1110
1110
  status,
@@ -1117,7 +1117,7 @@ async function fetchGraphCollection(params) {
1117
1117
  };
1118
1118
  }
1119
1119
  } finally {
1120
- await release();
1120
+ await releaseGraphResponse(response, release);
1121
1121
  }
1122
1122
  }
1123
1123
  function normalizeGraphAttachment(att) {
@@ -1149,7 +1149,7 @@ async function downloadGraphHostedContent(params) {
1149
1149
  deadline: params.deadline
1150
1150
  });
1151
1151
  } catch (err) {
1152
- params.logger?.warn?.("msteams graph hostedContents fetch failed", { error: err instanceof Error ? err.message : String(err) });
1152
+ params.logger?.warn?.("msteams graph hostedContents fetch failed", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
1153
1153
  return {
1154
1154
  media: [],
1155
1155
  count: 0
@@ -1194,11 +1194,11 @@ async function downloadGraphHostedContent(params) {
1194
1194
  sourceId: item.id
1195
1195
  });
1196
1196
  } finally {
1197
- await release();
1197
+ await releaseGraphResponse(valRes, release);
1198
1198
  }
1199
1199
  } catch (err) {
1200
1200
  out.push(createGraphHostedContentFact(item));
1201
- params.logger?.warn?.("msteams graph hostedContent value fetch failed", { error: err instanceof Error ? err.message : String(err) });
1201
+ params.logger?.warn?.("msteams graph hostedContent value fetch failed", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
1202
1202
  continue;
1203
1203
  }
1204
1204
  }
@@ -1227,9 +1227,9 @@ async function downloadMSTeamsGraphMedia(params) {
1227
1227
  } catch (err) {
1228
1228
  params.logger?.debug?.("graph media token acquisition failed", {
1229
1229
  messageUrl,
1230
- error: err instanceof Error ? err.message : String(err)
1230
+ error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)
1231
1231
  });
1232
- params.logger?.warn?.("msteams graph token acquisition failed", { error: err instanceof Error ? err.message : String(err) });
1232
+ params.logger?.warn?.("msteams graph token acquisition failed", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
1233
1233
  return {
1234
1234
  media: [],
1235
1235
  messageUrl,
@@ -1260,10 +1260,10 @@ async function downloadMSTeamsGraphMedia(params) {
1260
1260
  } catch (err) {
1261
1261
  params.logger?.debug?.("graph media message parse failed", {
1262
1262
  messageUrl,
1263
- error: err instanceof Error ? err.message : String(err)
1263
+ error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)
1264
1264
  });
1265
1265
  params.logger?.warn?.("msteams graph message parse failed", {
1266
- error: err instanceof Error ? err.message : String(err),
1266
+ error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err),
1267
1267
  messageUrl
1268
1268
  });
1269
1269
  msgData = {};
@@ -1275,14 +1275,14 @@ async function downloadMSTeamsGraphMedia(params) {
1275
1275
  status: messageStatus
1276
1276
  });
1277
1277
  } finally {
1278
- await release();
1278
+ await releaseGraphResponse(msgRes, release);
1279
1279
  }
1280
1280
  } catch (err) {
1281
1281
  params.logger?.debug?.("graph media message fetch failed", {
1282
1282
  messageUrl,
1283
- error: err instanceof Error ? err.message : String(err)
1283
+ error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)
1284
1284
  });
1285
- params.logger?.warn?.("msteams graph message fetch failed", { error: err instanceof Error ? err.message : String(err) });
1285
+ params.logger?.warn?.("msteams graph message fetch failed", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
1286
1286
  }
1287
1287
  for (const att of referenceAttachments) {
1288
1288
  const name = att.name ?? "file";
@@ -1348,7 +1348,7 @@ async function downloadMSTeamsGraphMedia(params) {
1348
1348
  } catch (err) {
1349
1349
  sharePointMedia.push(unavailableMedia);
1350
1350
  params.logger?.warn?.("msteams SharePoint reference download failed", {
1351
- error: err instanceof Error ? err.message : String(err),
1351
+ error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err),
1352
1352
  name
1353
1353
  });
1354
1354
  }
@@ -1387,7 +1387,7 @@ async function downloadMSTeamsGraphMedia(params) {
1387
1387
  });
1388
1388
  } catch (err) {
1389
1389
  params.logger?.warn?.("msteams graph attachment download failed", {
1390
- error: err instanceof Error ? err.message : String(err),
1390
+ error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err),
1391
1391
  messageUrl
1392
1392
  });
1393
1393
  }
@@ -1593,7 +1593,7 @@ async function prepareMSTeamsInboundContent(params) {
1593
1593
  })
1594
1594
  });
1595
1595
  } catch (err) {
1596
- params.log.debug?.("failed to resolve inbound Teams media", { error: require_setup_surface.formatUnknownError(err) });
1596
+ params.log.debug?.("failed to resolve inbound Teams media", { error: require_channel_setup.formatUnknownError(err) });
1597
1597
  }
1598
1598
  const inboundMedia = mergeMSTeamsMediaFacts(params.advertisedMedia, mediaList);
1599
1599
  const nativeMediaForComparison = [...params.advertisedMedia, ...mediaList.slice(params.advertisedMedia.length).map((media) => ({
@@ -1654,12 +1654,22 @@ function createTeamsReplyStreamController(params) {
1654
1654
  let emittedText = "";
1655
1655
  let acknowledgedText = "";
1656
1656
  let acknowledgedStreamId;
1657
+ let replacementFinalPending = false;
1658
+ let replacementEmitFailed = false;
1659
+ let replacementSettlementPending = false;
1660
+ let replacementTextAwaitingAcknowledgement;
1661
+ let deferredReplacementEntries = [];
1662
+ let finalMetadataQueued = false;
1663
+ let failedSegmentFallbackPrepared = false;
1657
1664
  const streamEvents = stream?.events;
1658
1665
  let streamChunkSubscription;
1659
1666
  if (typeof streamEvents?.on === "function" && typeof streamEvents.off === "function") streamChunkSubscription = streamEvents.on("chunk", (activity) => {
1660
- if (activity.type !== "typing" || activity.channelData?.streamType !== "streaming" || !activity.id || !activity.text || acknowledgedStreamId !== void 0 && activity.id !== acknowledgedStreamId || !activity.text.startsWith(acknowledgedText) || !emittedText.startsWith(activity.text)) return;
1667
+ const replacementAcknowledgementPending = replacementTextAwaitingAcknowledgement !== void 0;
1668
+ const replacementAcknowledgement = typeof activity.text === "string" && replacementAcknowledgementPending && activity.text === replacementTextAwaitingAcknowledgement;
1669
+ if (activity.type !== "typing" || activity.channelData?.streamType !== "streaming" || !activity.id || !activity.text || acknowledgedStreamId !== void 0 && activity.id !== acknowledgedStreamId || (replacementAcknowledgementPending ? !replacementAcknowledgement : !activity.text.startsWith(acknowledgedText)) || !emittedText.startsWith(activity.text)) return;
1661
1670
  acknowledgedStreamId = activity.id;
1662
1671
  acknowledgedText = activity.text;
1672
+ if (activity.text === replacementTextAwaitingAcknowledgement) replacementTextAwaitingAcknowledgement = void 0;
1663
1673
  });
1664
1674
  const wasCanceled = () => canceledLocally || Boolean(stream?.canceled);
1665
1675
  const releaseStreamChunkSubscription = () => {
@@ -1667,6 +1677,14 @@ function createTeamsReplyStreamController(params) {
1667
1677
  streamEvents?.off(streamChunkSubscription);
1668
1678
  streamChunkSubscription = void 0;
1669
1679
  };
1680
+ const acknowledgedNativeDelivery = () => {
1681
+ if (!acknowledgedStreamId || !acknowledgedText) return { visibleReplySent: false };
1682
+ return {
1683
+ visibleReplySent: true,
1684
+ content: acknowledgedText,
1685
+ messageId: acknowledgedStreamId
1686
+ };
1687
+ };
1670
1688
  const fallbackPayloadAfterAcknowledgedText = (payload) => {
1671
1689
  if (!acknowledgedText || typeof payload.text !== "string" || !payload.text.startsWith(acknowledgedText)) return payload;
1672
1690
  const remainingText = payload.text.slice(acknowledgedText.length);
@@ -1684,6 +1702,35 @@ function createTeamsReplyStreamController(params) {
1684
1702
  mediaUrls: void 0
1685
1703
  } : payload;
1686
1704
  };
1705
+ const finalStreamActivity = (text) => ({
1706
+ type: "message",
1707
+ ...text ? { text } : {},
1708
+ entities: [{
1709
+ type: "https://schema.org/Message",
1710
+ "@type": "Message",
1711
+ "@context": "https://schema.org",
1712
+ "@id": "",
1713
+ additionalType: ["AIGeneratedContent"]
1714
+ }],
1715
+ channelData: params.feedbackLoopEnabled ? { feedbackLoopEnabled: true } : {}
1716
+ });
1717
+ const deferredReplacementLogicalContent = () => {
1718
+ return deferredReplacementEntries.map((entry) => entry.payload.text).filter((text) => Boolean(text)).join("\n") || void 0;
1719
+ };
1720
+ const takeDeferredReplacementPayloads = (replacementFallback) => {
1721
+ const payloads = deferredReplacementEntries.flatMap((entry) => {
1722
+ if (entry.kind === "payload") return [entry.payload];
1723
+ const hasMedia = Boolean(entry.payload.mediaUrl || entry.payload.mediaUrls?.length);
1724
+ const text = replacementFallback?.text;
1725
+ if (!text && !hasMedia) return [];
1726
+ return [{
1727
+ ...entry.payload,
1728
+ text: text || void 0
1729
+ }];
1730
+ });
1731
+ deferredReplacementEntries = [];
1732
+ return payloads;
1733
+ };
1687
1734
  /**
1688
1735
  * Render the current informative status line into the streaming card. Pulls
1689
1736
  * the rotating "Thinking..." label from msteams config (or the plugin-sdk
@@ -1708,14 +1755,19 @@ function createTeamsReplyStreamController(params) {
1708
1755
  canceledLocally = true;
1709
1756
  return;
1710
1757
  }
1711
- params.log?.debug?.(`stream informative update failed: ${err instanceof Error ? err.message : String(err)}`);
1758
+ params.log?.debug?.(`stream informative update failed: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
1712
1759
  }
1713
1760
  };
1714
1761
  const progressDraftGate = (0, openclaw_plugin_sdk_channel_outbound.createChannelProgressDraftGate)({ onStart: renderInformativeUpdate });
1715
1762
  return {
1716
1763
  async onReplyStart() {},
1717
1764
  onPartialReply(payload) {
1718
- if (!stream || !payload.text || wasCanceled() || streamMode !== "partial" || streamFinalizationPending) return;
1765
+ if (!stream || !payload.text || wasCanceled() || streamMode !== "partial") return;
1766
+ if (replacementSettlementPending && replacementFinalPending) {
1767
+ pendingFinalPayload = { text: payload.text };
1768
+ return;
1769
+ }
1770
+ if (streamFinalizationPending) return;
1719
1771
  const fullText = payload.text;
1720
1772
  let prefixLength = 0;
1721
1773
  while (prefixLength < emittedText.length && prefixLength < fullText.length && emittedText[prefixLength] === fullText[prefixLength]) prefixLength += 1;
@@ -1724,7 +1776,13 @@ function createTeamsReplyStreamController(params) {
1724
1776
  if (!delta) return;
1725
1777
  if (previousRemainder.trim()) {
1726
1778
  stream.clearText();
1727
- streamFailed = true;
1779
+ if (streamFailed) {
1780
+ streamFinalizationPending = true;
1781
+ return;
1782
+ }
1783
+ replacementFinalPending = true;
1784
+ replacementSettlementPending = true;
1785
+ pendingFinalPayload = { text: fullText };
1728
1786
  streamFinalizationPending = true;
1729
1787
  return;
1730
1788
  }
@@ -1739,7 +1797,7 @@ function createTeamsReplyStreamController(params) {
1739
1797
  return;
1740
1798
  }
1741
1799
  streamFailed = true;
1742
- params.log?.warn?.(`msteams stream emit failed, falling back to block delivery: ${err instanceof Error ? err.message : String(err)}`);
1800
+ params.log?.warn?.(`msteams stream emit failed, falling back to block delivery: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
1743
1801
  }
1744
1802
  },
1745
1803
  /**
@@ -1781,6 +1839,49 @@ function createTeamsReplyStreamController(params) {
1781
1839
  preparePayload(payload) {
1782
1840
  if (!stream) return payload;
1783
1841
  if (wasCanceled()) return;
1842
+ if (replacementSettlementPending) {
1843
+ if (!replacementFinalPending) {
1844
+ deferredReplacementEntries.push({
1845
+ kind: "payload",
1846
+ payload
1847
+ });
1848
+ return;
1849
+ }
1850
+ if (!payload.text) {
1851
+ deferredReplacementEntries.push({
1852
+ kind: "payload",
1853
+ payload
1854
+ });
1855
+ return;
1856
+ }
1857
+ replacementFinalPending = false;
1858
+ deferredReplacementEntries.push({
1859
+ kind: "replacement",
1860
+ payload
1861
+ });
1862
+ pendingFinalPayload = fallbackPayloadForSuppressedFinal(payload);
1863
+ try {
1864
+ replacementTextAwaitingAcknowledgement = payload.text;
1865
+ stream.emit(finalStreamActivity(payload.text));
1866
+ finalMetadataQueued = true;
1867
+ emittedText = payload.text;
1868
+ tokensEmitted = false;
1869
+ return;
1870
+ } catch (err) {
1871
+ replacementTextAwaitingAcknowledgement = void 0;
1872
+ tokensEmitted = false;
1873
+ if (isStreamCancelledError(err)) {
1874
+ canceledLocally = true;
1875
+ pendingFinalPayload = void 0;
1876
+ deferredReplacementEntries = [];
1877
+ return;
1878
+ }
1879
+ streamFailed = true;
1880
+ replacementEmitFailed = true;
1881
+ params.log?.warn?.(`msteams stream replacement failed, falling back to block delivery: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
1882
+ return;
1883
+ }
1884
+ }
1784
1885
  if (tokensEmitted && !streamFailed) {
1785
1886
  const hasMedia = Boolean(payload.mediaUrl || payload.mediaUrls?.length);
1786
1887
  pendingFinalPayload = fallbackPayloadForSuppressedFinal(payload);
@@ -1792,11 +1893,9 @@ function createTeamsReplyStreamController(params) {
1792
1893
  } : void 0;
1793
1894
  }
1794
1895
  if (streamFailed) {
1795
- const fallback = fallbackPayloadAfterAcknowledgedText(payload);
1896
+ const fallback = failedSegmentFallbackPrepared ? payload : fallbackPayloadAfterAcknowledgedText(payload);
1897
+ failedSegmentFallbackPrepared = true;
1796
1898
  pendingFinalPayload = void 0;
1797
- acknowledgedText = "";
1798
- acknowledgedStreamId = void 0;
1799
- releaseStreamChunkSubscription();
1800
1899
  return fallback;
1801
1900
  }
1802
1901
  if (streamMode === "progress" && payload.text) try {
@@ -1813,7 +1912,7 @@ function createTeamsReplyStreamController(params) {
1813
1912
  canceledLocally = true;
1814
1913
  return;
1815
1914
  }
1816
- params.log?.debug?.(`progress-mode finalize failed: ${err instanceof Error ? err.message : String(err)}`);
1915
+ params.log?.debug?.(`progress-mode finalize failed: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
1817
1916
  }
1818
1917
  return payload;
1819
1918
  },
@@ -1828,74 +1927,88 @@ function createTeamsReplyStreamController(params) {
1828
1927
  releaseStreamChunkSubscription();
1829
1928
  return { visibleReplySent: false };
1830
1929
  }
1831
- const content = wasCanceled() ? acknowledgedText || void 0 : pendingFinalPayload?.text ?? (emittedText || void 0);
1930
+ let logicalContent;
1832
1931
  try {
1833
1932
  if (wasCanceled()) {
1834
1933
  pendingFinalPayload = void 0;
1934
+ deferredReplacementEntries = [];
1835
1935
  streamFinalizationPending = false;
1836
- return {
1837
- visibleReplySent: content !== void 0,
1838
- ...content === void 0 ? {} : { content }
1839
- };
1936
+ return acknowledgedNativeDelivery();
1840
1937
  }
1841
- if (!streamFinalizationPending) return {
1842
- visibleReplySent: true,
1843
- ...content === void 0 ? {} : { content }
1844
- };
1845
- const finalEntities = [{
1846
- type: "https://schema.org/Message",
1847
- "@type": "Message",
1848
- "@context": "https://schema.org",
1849
- "@id": "",
1850
- additionalType: ["AIGeneratedContent"]
1851
- }];
1852
- const finalChannelData = params.feedbackLoopEnabled ? { feedbackLoopEnabled: true } : {};
1853
- stream.emit({
1854
- type: "message",
1855
- entities: finalEntities,
1856
- channelData: finalChannelData
1857
- });
1938
+ if (!streamFinalizationPending) return acknowledgedNativeDelivery();
1939
+ if (replacementSettlementPending && replacementFinalPending && pendingFinalPayload?.text) {
1940
+ replacementFinalPending = false;
1941
+ deferredReplacementEntries.push({
1942
+ kind: "replacement",
1943
+ payload: pendingFinalPayload
1944
+ });
1945
+ replacementTextAwaitingAcknowledgement = pendingFinalPayload.text;
1946
+ logicalContent = deferredReplacementLogicalContent();
1947
+ stream.emit(finalStreamActivity(pendingFinalPayload.text));
1948
+ finalMetadataQueued = true;
1949
+ emittedText = pendingFinalPayload.text;
1950
+ }
1951
+ logicalContent ??= replacementSettlementPending ? deferredReplacementLogicalContent() : void 0;
1952
+ const content = pendingFinalPayload?.text ?? (emittedText || void 0);
1953
+ if (!finalMetadataQueued) stream.emit(finalStreamActivity());
1858
1954
  const result = await stream.close();
1859
1955
  streamFinalizationPending = false;
1860
1956
  if (!result) {
1861
1957
  const fallback = pendingFinalPayload;
1862
1958
  pendingFinalPayload = void 0;
1863
- const fallbackPayload = fallback && !wasCanceled() ? fallbackPayloadAfterAcknowledgedText(fallback) : void 0;
1959
+ const canceled = wasCanceled();
1960
+ const replacementFallback = replacementSettlementPending && fallback && !canceled ? fallbackPayloadAfterAcknowledgedText(fallback) : void 0;
1961
+ const fallbackPayload = !replacementSettlementPending && fallback && !canceled ? fallbackPayloadAfterAcknowledgedText(fallback) : void 0;
1962
+ const postNativePayloads = replacementSettlementPending && !canceled ? takeDeferredReplacementPayloads(replacementFallback) : [];
1963
+ if (canceled) deferredReplacementEntries = [];
1864
1964
  return {
1865
- visibleReplySent: true,
1866
- ...content === void 0 ? {} : { content },
1867
- ...fallbackPayload ? { fallbackPayload } : {}
1965
+ ...acknowledgedNativeDelivery(),
1966
+ ...!canceled && logicalContent ? { logicalContent } : {},
1967
+ ...fallbackPayload ? { fallbackPayload } : {},
1968
+ ...postNativePayloads.length > 0 ? { postNativePayloads } : {}
1868
1969
  };
1869
1970
  }
1971
+ const replacementFallback = replacementSettlementPending && replacementEmitFailed && pendingFinalPayload ? fallbackPayloadAfterAcknowledgedText(pendingFinalPayload) : void 0;
1870
1972
  pendingFinalPayload = void 0;
1871
- const messageId = require_messenger.extractMessageId(result) ?? void 0;
1973
+ const messageId = require_messenger.extractMessageId(result) ?? acknowledgedStreamId;
1974
+ const postNativePayloads = replacementSettlementPending ? takeDeferredReplacementPayloads(replacementFallback) : [];
1975
+ const nativeContent = replacementEmitFailed ? acknowledgedText || void 0 : content;
1872
1976
  return {
1873
- visibleReplySent: true,
1874
- ...content === void 0 ? {} : { content },
1875
- ...messageId ? { messageId } : {}
1977
+ visibleReplySent: replacementEmitFailed ? Boolean(nativeContent) : true,
1978
+ ...nativeContent === void 0 ? {} : { content: nativeContent },
1979
+ ...logicalContent ? { logicalContent } : {},
1980
+ ...messageId && (!replacementEmitFailed || nativeContent) ? { messageId } : {},
1981
+ ...postNativePayloads.length > 0 ? { postNativePayloads } : {}
1876
1982
  };
1877
1983
  } catch (err) {
1878
1984
  if (isStreamCancelledError(err)) {
1879
1985
  canceledLocally = true;
1880
1986
  pendingFinalPayload = void 0;
1987
+ deferredReplacementEntries = [];
1881
1988
  streamFinalizationPending = false;
1882
- return {
1883
- visibleReplySent: true,
1884
- ...content === void 0 ? {} : { content }
1885
- };
1989
+ return acknowledgedNativeDelivery();
1886
1990
  }
1887
1991
  streamFailed = true;
1888
1992
  streamFinalizationPending = false;
1889
- params.log?.warn?.(`msteams stream finalize failed: ${err instanceof Error ? err.message : String(err)}`);
1993
+ params.log?.warn?.(`msteams stream finalize failed: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
1890
1994
  const fallback = pendingFinalPayload;
1891
1995
  pendingFinalPayload = void 0;
1892
- const fallbackPayload = fallback ? fallbackPayloadAfterAcknowledgedText(fallback) : void 0;
1996
+ const replacementFallback = replacementSettlementPending && fallback ? fallbackPayloadAfterAcknowledgedText(fallback) : void 0;
1997
+ const fallbackPayload = !replacementSettlementPending && fallback ? fallbackPayloadAfterAcknowledgedText(fallback) : void 0;
1998
+ const postNativePayloads = replacementSettlementPending ? takeDeferredReplacementPayloads(replacementFallback) : [];
1893
1999
  return {
1894
- visibleReplySent: true,
1895
- ...content === void 0 ? {} : { content },
1896
- ...fallbackPayload ? { fallbackPayload } : {}
2000
+ ...acknowledgedNativeDelivery(),
2001
+ ...logicalContent ? { logicalContent } : {},
2002
+ ...fallbackPayload ? { fallbackPayload } : {},
2003
+ ...postNativePayloads.length > 0 ? { postNativePayloads } : {}
1897
2004
  };
1898
2005
  } finally {
2006
+ finalMetadataQueued = false;
2007
+ replacementEmitFailed = false;
2008
+ replacementFinalPending = false;
2009
+ replacementSettlementPending = false;
2010
+ replacementTextAwaitingAcknowledgement = void 0;
2011
+ deferredReplacementEntries = [];
1899
2012
  releaseStreamChunkSubscription();
1900
2013
  }
1901
2014
  },
@@ -1998,6 +2111,7 @@ function createMSTeamsReplyDispatcher(params) {
1998
2111
  const blockStreamingEnabled = blockStreamingResolved ?? false;
1999
2112
  const typingIndicatorEnabled = typeof msteamsCfg?.typingIndicator === "boolean" ? msteamsCfg.typingIndicator : true;
2000
2113
  const pendingDeliveries = [];
2114
+ const joinAcceptedContents = (contents) => contents.filter((content) => Boolean(content)).join("\n");
2001
2115
  const sendMessages = async (messages) => {
2002
2116
  return require_messenger.sendMSTeamsMessages({
2003
2117
  replyStyle: params.replyStyle,
@@ -2021,15 +2135,16 @@ function createMSTeamsReplyDispatcher(params) {
2021
2135
  });
2022
2136
  };
2023
2137
  const queueDeliveryFailureSystemEvent = (failure) => {
2024
- const classification = require_setup_surface.classifyMSTeamsSendError(failure.error);
2025
- const errorText = require_setup_surface.formatUnknownError(failure.error);
2138
+ const classification = require_channel_setup.classifyMSTeamsSendError(failure.error);
2139
+ const errorText = require_channel_setup.formatUnknownError(failure.error);
2026
2140
  const failedAll = failure.failed >= failure.total;
2141
+ const ambiguous = classification.kind === "ambiguous";
2027
2142
  const sentences = [
2028
- `Microsoft Teams delivery failed: ${failedAll ? "the previous reply was not delivered" : `${failure.failed} of ${failure.total} message blocks were not delivered`}.`,
2029
- `The user may not have received ${failedAll ? "that reply" : "the full reply"}.`,
2143
+ `Microsoft Teams delivery failed: ${ambiguous ? failedAll ? "the delivery outcome is unknown for the previous reply" : `the delivery outcome is unknown for ${failure.failed} of ${failure.total} message blocks` : failedAll ? "the previous reply was not delivered" : `${failure.failed} of ${failure.total} message blocks were not delivered`}.`,
2144
+ ambiguous ? void 0 : `The user may not have received ${failedAll ? "that reply" : "the full reply"}.`,
2030
2145
  `Error: ${errorText}.`,
2031
2146
  classification.statusCode != null ? `Status: ${classification.statusCode}.` : void 0,
2032
- classification.kind === "transient" || classification.kind === "throttled" ? "Retrying later may succeed." : void 0
2147
+ require_channel_setup.formatMSTeamsDeliveryFailureGuidance(classification)
2033
2148
  ].filter(Boolean);
2034
2149
  core.system.enqueueSystemEvent(sentences.join(" "), {
2035
2150
  sessionKey: params.sessionKey,
@@ -2046,11 +2161,13 @@ function createMSTeamsReplyDispatcher(params) {
2046
2161
  });
2047
2162
  };
2048
2163
  const deliveryOutcome = (delivery) => {
2049
- const messageIds = [...delivery.nativeMessageId ? [delivery.nativeMessageId] : [], ...delivery.messageIds];
2164
+ const acceptedParts = [...delivery.nativeResult ? [delivery.nativeResult] : [], ...delivery.blockResults];
2165
+ const messageIds = acceptedParts.flatMap((part) => part.messageIds);
2166
+ const content = delivery.errors.length > 0 ? joinAcceptedContents(acceptedParts.map((part) => part.content)) : delivery.content;
2050
2167
  return {
2051
- visibleReplySent: delivery.visibleReplySent,
2168
+ visibleReplySent: acceptedParts.length > 0,
2052
2169
  ...messageIds.length > 0 ? { messageIds } : {},
2053
- ...delivery.visibleReplySent && delivery.content !== void 0 ? { content: delivery.content } : {}
2170
+ ...acceptedParts.length > 0 && content !== void 0 ? { content } : {}
2054
2171
  };
2055
2172
  };
2056
2173
  const settlePendingDelivery = (delivery) => {
@@ -2062,7 +2179,7 @@ function createMSTeamsReplyDispatcher(params) {
2062
2179
  return;
2063
2180
  }
2064
2181
  const error = delivery.errors.find((candidate) => !(candidate instanceof openclaw_plugin_sdk_error_runtime.PlatformMessageNotDispatchedError)) ?? delivery.errors[0];
2065
- delivery.finalization.reject(delivery.visibleReplySent ? (0, openclaw_plugin_sdk_channel_inbound.createChannelPartialDeliveryError)(error, {
2182
+ delivery.finalization.reject(outcome.visibleReplySent ? (0, openclaw_plugin_sdk_channel_inbound.createChannelPartialDeliveryError)(error, {
2066
2183
  ...outcome,
2067
2184
  visibleReplySent: true
2068
2185
  }) : error);
@@ -2072,12 +2189,11 @@ function createMSTeamsReplyDispatcher(params) {
2072
2189
  messages,
2073
2190
  finalization: (0, openclaw_plugin_sdk_extension_shared.createDeferred)(),
2074
2191
  content: payload.text,
2192
+ blockResults: [],
2075
2193
  native,
2076
2194
  nativeSettled: !native,
2077
2195
  blockSettled: messages.length === 0,
2078
2196
  settled: false,
2079
- visibleReplySent: false,
2080
- messageIds: [],
2081
2197
  errors: []
2082
2198
  };
2083
2199
  pendingDeliveries.push(delivery);
@@ -2093,9 +2209,11 @@ function createMSTeamsReplyDispatcher(params) {
2093
2209
  const sentIds = [];
2094
2210
  for (const msg of toSend) try {
2095
2211
  const msgIds = await sendMessages([msg]);
2096
- delivery.visibleReplySent ||= msgIds.length > 0;
2097
2212
  const validIds = msgIds.filter((id) => id.trim() && id !== "unknown");
2098
- delivery.messageIds.push(...validIds);
2213
+ if (msgIds.length > 0) delivery.blockResults.push({
2214
+ messageIds: validIds,
2215
+ ...msg.text ? { content: msg.text } : {}
2216
+ });
2099
2217
  sentIds.push(...validIds);
2100
2218
  } catch (msgError) {
2101
2219
  failed += 1;
@@ -2119,7 +2237,7 @@ function createMSTeamsReplyDispatcher(params) {
2119
2237
  if (sentIds.length > 0) try {
2120
2238
  params.onSentMessageIds?.(sentIds);
2121
2239
  } catch (error) {
2122
- params.log.warn?.("failed to record sent Teams message ids", { error: require_setup_surface.formatUnknownError(error) });
2240
+ params.log.warn?.("failed to record sent Teams message ids", { error: require_channel_setup.formatUnknownError(error) });
2123
2241
  }
2124
2242
  }
2125
2243
  };
@@ -2151,9 +2269,9 @@ function createMSTeamsReplyDispatcher(params) {
2151
2269
  };
2152
2270
  },
2153
2271
  onError: (err, info) => {
2154
- const errMsg = require_setup_surface.formatUnknownError(err);
2155
- const classification = require_setup_surface.classifyMSTeamsSendError(err);
2156
- const hint = require_setup_surface.formatMSTeamsSendErrorHint(classification);
2272
+ const errMsg = require_channel_setup.formatUnknownError(err);
2273
+ const classification = require_channel_setup.classifyMSTeamsSendError(err);
2274
+ const hint = require_channel_setup.formatMSTeamsSendErrorHint(classification);
2157
2275
  params.runtime.error?.(`msteams ${info.kind} reply failed: ${errMsg}${hint ? ` (${hint})` : ""}`);
2158
2276
  params.log.error("reply failed", {
2159
2277
  kind: info.kind,
@@ -2179,11 +2297,16 @@ function createMSTeamsReplyDispatcher(params) {
2179
2297
  settlePendingDelivery(nativeDelivery);
2180
2298
  return;
2181
2299
  }
2182
- nativeDelivery.visibleReplySent ||= nativeResult.visibleReplySent;
2183
- nativeDelivery.nativeMessageId = nativeResult.messageId;
2184
- if (nativeResult.content !== void 0) nativeDelivery.content = nativeResult.content;
2185
- if (nativeResult.fallbackPayload) {
2186
- nativeDelivery.messages.push(...renderReplyPayload(nativeResult.fallbackPayload));
2300
+ if (nativeResult.visibleReplySent) nativeDelivery.nativeResult = {
2301
+ messageIds: nativeResult.messageId ? [nativeResult.messageId] : [],
2302
+ ...nativeResult.content !== void 0 ? { content: nativeResult.content } : {}
2303
+ };
2304
+ const hasPostNativePayloads = Boolean(nativeResult.postNativePayloads?.length);
2305
+ if (nativeResult.logicalContent !== void 0) nativeDelivery.content = nativeResult.logicalContent;
2306
+ else if (nativeResult.content !== void 0 && (!nativeResult.fallbackPayload && !hasPostNativePayloads || nativeDelivery.content === void 0)) nativeDelivery.content = nativeResult.content;
2307
+ const afterNativePayloads = [...nativeResult.fallbackPayload ? [nativeResult.fallbackPayload] : [], ...nativeResult.postNativePayloads ?? []];
2308
+ if (afterNativePayloads.length > 0) {
2309
+ nativeDelivery.messages.push(...afterNativePayloads.flatMap((payload) => renderReplyPayload(payload)));
2187
2310
  nativeDelivery.blockSettled = nativeDelivery.messages.length === 0;
2188
2311
  }
2189
2312
  nativeDelivery.nativeSettled = true;
@@ -2192,7 +2315,7 @@ function createMSTeamsReplyDispatcher(params) {
2192
2315
  if (nativeResult.messageId) try {
2193
2316
  params.onSentMessageIds?.([nativeResult.messageId]);
2194
2317
  } catch (error) {
2195
- params.log.warn?.("failed to record sent Teams message id", { error: require_setup_surface.formatUnknownError(error) });
2318
+ params.log.warn?.("failed to record sent Teams message id", { error: require_channel_setup.formatUnknownError(error) });
2196
2319
  }
2197
2320
  };
2198
2321
  const previewToolProgressEnabled = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingPreviewToolProgress)(msteamsCfg, true, teamsStreamMode);
@@ -2201,10 +2324,10 @@ function createMSTeamsReplyDispatcher(params) {
2201
2324
  const progressCallbacks = streamController.hasStream() ? {
2202
2325
  onReasoningStream: async (payload) => {
2203
2326
  const text = typeof payload?.text === "string" ? payload.text : void 0;
2204
- if (!text) return;
2327
+ if (!text) return false;
2205
2328
  if (payload?.isReasoningSnapshot !== true) {
2206
2329
  await streamController.pushProgressLine(text);
2207
- return;
2330
+ return false;
2208
2331
  }
2209
2332
  await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
2210
2333
  event: "item",
@@ -2213,6 +2336,7 @@ function createMSTeamsReplyDispatcher(params) {
2213
2336
  title: "Reasoning",
2214
2337
  progressText: text
2215
2338
  }));
2339
+ return false;
2216
2340
  },
2217
2341
  onToolStart: async (payload) => {
2218
2342
  const name = typeof payload?.name === "string" ? payload.name : void 0;
@@ -2225,6 +2349,7 @@ function createMSTeamsReplyDispatcher(params) {
2225
2349
  ...typeof payload?.phase === "string" ? { phase: payload.phase } : {},
2226
2350
  ...payload?.args && typeof payload.args === "object" ? { args: payload.args } : {}
2227
2351
  }, detailMode === "explain" || detailMode === "raw" ? { detailMode } : void 0), name ? { toolName: name } : void 0);
2352
+ return false;
2228
2353
  },
2229
2354
  onItemEvent: async (payload) => {
2230
2355
  await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
@@ -2240,13 +2365,15 @@ function createMSTeamsReplyDispatcher(params) {
2240
2365
  ...typeof payload?.progressText === "string" ? { progressText: payload.progressText } : {},
2241
2366
  ...typeof payload?.meta === "string" ? { meta: payload.meta } : {}
2242
2367
  }));
2368
+ return false;
2243
2369
  },
2244
2370
  onPlanUpdate: async (payload) => {
2245
- if (payload?.phase !== "update") return;
2371
+ if (payload?.phase !== "update") return false;
2246
2372
  await streamController.pushPlanProgress((0, openclaw_plugin_sdk_channel_outbound.normalizeAgentPlanSteps)(payload.steps), { explanation: typeof payload.explanation === "string" ? payload.explanation : void 0 });
2373
+ return false;
2247
2374
  },
2248
2375
  onApprovalEvent: async (payload) => {
2249
- if (payload?.phase !== "requested") return;
2376
+ if (payload?.phase !== "requested") return false;
2250
2377
  await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
2251
2378
  event: "approval",
2252
2379
  phase: payload.phase,
@@ -2255,10 +2382,11 @@ function createMSTeamsReplyDispatcher(params) {
2255
2382
  ...typeof payload?.reason === "string" ? { reason: payload.reason } : {},
2256
2383
  ...typeof payload?.message === "string" ? { message: payload.message } : {}
2257
2384
  }));
2385
+ return false;
2258
2386
  },
2259
2387
  onCommandOutput: async (payload) => {
2260
- if (payload?.phase !== "end") return;
2261
- await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
2388
+ if (payload?.phase !== "end") return false;
2389
+ await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
2262
2390
  event: "command-output",
2263
2391
  ...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
2264
2392
  ...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
@@ -2268,9 +2396,10 @@ function createMSTeamsReplyDispatcher(params) {
2268
2396
  ...typeof payload?.status === "string" ? { status: payload.status } : {},
2269
2397
  ...typeof payload?.exitCode === "number" ? { exitCode: payload.exitCode } : {}
2270
2398
  }));
2399
+ return false;
2271
2400
  },
2272
2401
  onPatchSummary: async (payload) => {
2273
- if (payload?.phase !== "end") return;
2402
+ if (payload?.phase !== "end") return false;
2274
2403
  await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
2275
2404
  event: "patch",
2276
2405
  ...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
@@ -2283,6 +2412,7 @@ function createMSTeamsReplyDispatcher(params) {
2283
2412
  ...Array.isArray(payload?.deleted) && payload.deleted.every((s) => typeof s === "string") ? { deleted: payload.deleted } : {},
2284
2413
  ...typeof payload?.summary === "string" ? { summary: payload.summary } : {}
2285
2414
  }));
2415
+ return false;
2286
2416
  }
2287
2417
  } : {};
2288
2418
  return {
@@ -2292,7 +2422,10 @@ function createMSTeamsReplyDispatcher(params) {
2292
2422
  },
2293
2423
  delivery,
2294
2424
  replyOptions: {
2295
- ...streamController.hasStream() ? { onPartialReply: (payload) => streamController.onPartialReply(payload) } : {},
2425
+ ...streamController.hasStream() ? { onPartialReply: (payload) => {
2426
+ streamController.onPartialReply(payload);
2427
+ return false;
2428
+ } } : {},
2296
2429
  ...progressCallbacks,
2297
2430
  ...shouldSuppressDefaultToolProgressMessages ? { suppressDefaultToolProgressMessages: true } : {},
2298
2431
  disableBlockStreaming: blockStreamingResolved == null ? void 0 : !blockStreamingResolved,
@@ -2310,14 +2443,7 @@ const sentMessages = (0, openclaw_plugin_sdk_dedupe_runtime.createPersistentDedu
2310
2443
  namespace: "msteams.sent-messages",
2311
2444
  maxEntries: 1e3,
2312
2445
  openStore: (options) => require_runtime.getOptionalMSTeamsRuntime()?.state.openKeyedStore(options),
2313
- logError: (error) => {
2314
- try {
2315
- require_runtime.getOptionalMSTeamsRuntime()?.logging.getChildLogger({
2316
- plugin: "msteams",
2317
- feature: "sent-message-state"
2318
- }).warn("Microsoft Teams persistent sent-message state failed", { error: String(error) });
2319
- } catch {}
2320
- },
2446
+ logError: (0, openclaw_plugin_sdk_plugin_state_runtime.createPluginStateErrorReporter)(require_runtime.getOptionalMSTeamsRuntime, "msteams", "sent-message-state", "Microsoft Teams persistent sent-message state failed"),
2321
2447
  readTimestamp: (record) => record.sentAt
2322
2448
  }
2323
2449
  });
@@ -2389,7 +2515,21 @@ async function dispatchMSTeamsInboundTurn(params) {
2389
2515
  }) : true;
2390
2516
  const bodyForAgent = threadContext ? `[Thread history]\n${threadContext}\n[/Thread history]\n\n${agentBody}` : agentBody;
2391
2517
  const nativeChannelId = isChannel && teamAadGroupId ? `${teamAadGroupId}/${graphChannelId}` : void 0;
2392
- const ctxPayload = (0, openclaw_plugin_sdk_channel_inbound.buildChannelInboundEventContext)({
2518
+ const boundIngress = await resolveMSTeamsSenderAccess({
2519
+ cfg,
2520
+ activity,
2521
+ hasControlCommand: admission.isControlCommand,
2522
+ conversationThreadId: facts.threadId,
2523
+ contextBinding: {
2524
+ agentId: route.agentId,
2525
+ sessionKey: route.sessionKey,
2526
+ ...activity.id ? { messageId: activity.id } : {},
2527
+ ...nativeChannelId ? { nativeChannelId } : {},
2528
+ inboundEventKind: "user_request"
2529
+ }
2530
+ });
2531
+ const ctxPayload = core.channel.inbound.buildContext({
2532
+ channelIngress: boundIngress.channelIngress,
2393
2533
  channel: "msteams",
2394
2534
  contextVisibility: contextVisibilityMode,
2395
2535
  supplemental: { quote: quoteInfo ? {
@@ -2422,6 +2562,8 @@ async function dispatchMSTeamsInboundTurn(params) {
2422
2562
  },
2423
2563
  reply: {
2424
2564
  to: teamsTo,
2565
+ originatingTo: `conversation:${conversationId}`,
2566
+ messageThreadId: facts.threadId ?? void 0,
2425
2567
  replyToId: activity.replyToId ?? void 0,
2426
2568
  nativeChannelId
2427
2569
  },
@@ -2502,7 +2644,7 @@ async function dispatchMSTeamsInboundTurn(params) {
2502
2644
  },
2503
2645
  ctxPayload,
2504
2646
  record: { onRecordError: (err) => {
2505
- logVerboseMessage(`msteams: failed updating session meta: ${require_setup_surface.formatUnknownError(err)}`);
2647
+ logVerboseMessage(`msteams: failed updating session meta: ${require_channel_setup.formatUnknownError(err)}`);
2506
2648
  } },
2507
2649
  history: {
2508
2650
  isGroup: isRoomish,
@@ -2521,18 +2663,15 @@ async function dispatchMSTeamsInboundTurn(params) {
2521
2663
  });
2522
2664
  const dispatchResult = turnResult.dispatched ? turnResult.dispatchResult : void 0;
2523
2665
  const counts = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundReplyDispatchCounts)(dispatchResult);
2524
- log.info("dispatch complete", {
2525
- queuedFinal: dispatchResult?.queuedFinal ?? false,
2526
- counts
2527
- });
2666
+ log.info("dispatch complete", { counts });
2528
2667
  if ((0, openclaw_plugin_sdk_channel_inbound.hasFinalInboundReplyDispatch)(dispatchResult)) logVerboseMessage(`msteams: delivered ${counts.final} repl${counts.final === 1 ? "y" : "ies"} to ${teamsTo}`);
2529
2668
  return {
2530
2669
  kind: "completed",
2531
2670
  finalResponses: counts.final
2532
2671
  };
2533
2672
  } catch (err) {
2534
- log.error("dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
2535
- runtime.error(`msteams dispatch failed: ${require_setup_surface.formatUnknownError(err)}`);
2673
+ log.error("dispatch failed", { error: require_channel_setup.formatUnknownError(err) });
2674
+ runtime.error(`msteams dispatch failed: ${require_channel_setup.formatUnknownError(err)}`);
2536
2675
  if (facts.turnAdoptionLifecycle) throw err;
2537
2676
  try {
2538
2677
  await context.sendActivity("⚠️ Something went wrong. Please try again.");
@@ -2797,7 +2936,7 @@ function prepareMSTeamsThreadRouting(params) {
2797
2936
  }).catch((err) => {
2798
2937
  params.log.debug?.("failed to resolve Teams AAD group ID", {
2799
2938
  teamId: conversationTeamId,
2800
- error: require_setup_surface.formatUnknownError(err)
2939
+ error: require_channel_setup.formatUnknownError(err)
2801
2940
  });
2802
2941
  });
2803
2942
  teamAadGroupId = await teamGroupIdPromise;
@@ -2831,7 +2970,7 @@ async function resolveMSTeamsThreadContext(params) {
2831
2970
  work: () => require_messenger.fetchChatMessageText(graphToken, params.conversationId, quoteMessageId, deadline)
2832
2971
  });
2833
2972
  } catch (err) {
2834
- params.log.debug?.("failed to fetch full quoted message text", { error: require_setup_surface.formatUnknownError(err) });
2973
+ params.log.debug?.("failed to fetch full quoted message text", { error: require_channel_setup.formatUnknownError(err) });
2835
2974
  }
2836
2975
  let threadContext;
2837
2976
  const threadParentId = activity.replyToId;
@@ -2848,8 +2987,8 @@ async function resolveMSTeamsThreadContext(params) {
2848
2987
  });
2849
2988
  const parentMsg = parentResult.status === "fulfilled" ? parentResult.value : void 0;
2850
2989
  const replies = repliesResult.status === "fulfilled" ? repliesResult.value : [];
2851
- if (parentResult.status === "rejected") params.log.debug?.("failed to fetch parent message", { error: require_setup_surface.formatUnknownError(parentResult.reason) });
2852
- if (repliesResult.status === "rejected") params.log.debug?.("failed to fetch thread replies", { error: require_setup_surface.formatUnknownError(repliesResult.reason) });
2990
+ if (parentResult.status === "rejected") params.log.debug?.("failed to fetch parent message", { error: require_channel_setup.formatUnknownError(parentResult.reason) });
2991
+ if (repliesResult.status === "rejected") params.log.debug?.("failed to fetch thread replies", { error: require_channel_setup.formatUnknownError(repliesResult.reason) });
2853
2992
  const isThreadSenderAllowed = (message) => (0, openclaw_plugin_sdk_channel_inbound.resolveInboundSupplementalSenderAllowed)({
2854
2993
  isGroup: params.isChannel,
2855
2994
  groupPolicy: params.groupPolicy,
@@ -2886,7 +3025,7 @@ async function resolveMSTeamsThreadContext(params) {
2886
3025
  });
2887
3026
  threadContext = require_messenger.formatThreadContext(threadMessages, activity.id) || void 0;
2888
3027
  } catch (err) {
2889
- params.log.debug?.("failed to fetch thread history", { error: require_setup_surface.formatUnknownError(err) });
3028
+ params.log.debug?.("failed to fetch thread history", { error: require_channel_setup.formatUnknownError(err) });
2890
3029
  }
2891
3030
  quoteSenderName ??= params.quoteInfo?.sender;
2892
3031
  return {
@@ -2965,7 +3104,7 @@ function createMSTeamsMessageHandler(deps) {
2965
3104
  } catch (err) {
2966
3105
  log.error("failed to record poll vote", {
2967
3106
  pollId: pollVote.pollId,
2968
- error: require_setup_surface.formatUnknownError(err)
3107
+ error: require_channel_setup.formatUnknownError(err)
2969
3108
  });
2970
3109
  }
2971
3110
  return;
@@ -3142,7 +3281,7 @@ function createMSTeamsMessageHandler(deps) {
3142
3281
  });
3143
3282
  },
3144
3283
  onError: (err) => {
3145
- runtime.error(`msteams debounce flush failed: ${require_setup_surface.formatUnknownError(err)}`);
3284
+ runtime.error(`msteams debounce flush failed: ${require_channel_setup.formatUnknownError(err)}`);
3146
3285
  }
3147
3286
  });
3148
3287
  return async function handleTeamsMessage(context, turnAdoptionLifecycle) {
@@ -3166,7 +3305,8 @@ function createMSTeamsReactionHandler(deps) {
3166
3305
  const msteamsCfg = cfg.channels?.msteams;
3167
3306
  return async function handleReaction(context, direction) {
3168
3307
  const activity = context.activity;
3169
- const reactions = direction === "added" ? activity.reactionsAdded ?? [] : activity.reactionsRemoved ?? [];
3308
+ const rawReactions = direction === "added" ? activity.reactionsAdded : activity.reactionsRemoved;
3309
+ const reactions = Array.isArray(rawReactions) ? rawReactions : [];
3170
3310
  if (reactions.length === 0) {
3171
3311
  log.debug?.("reaction activity has no reactions; skipping");
3172
3312
  return;
@@ -3376,7 +3516,7 @@ function registerMSTeamsHandlers(handler, deps) {
3376
3516
  return result;
3377
3517
  } catch (err) {
3378
3518
  if (turnAdoptionLifecycle) throw err;
3379
- deps.runtime.error(`msteams handler failed: ${require_setup_surface.formatUnknownError(err)}`);
3519
+ deps.runtime.error(`msteams handler failed: ${require_channel_setup.formatUnknownError(err)}`);
3380
3520
  }
3381
3521
  if (!nextRan) await runNext();
3382
3522
  });
@@ -3402,7 +3542,7 @@ function registerMSTeamsHandlers(handler, deps) {
3402
3542
  });
3403
3543
  deps.log.info("sent welcome card");
3404
3544
  } catch (err) {
3405
- deps.log.debug?.("failed to send welcome card", { error: require_setup_surface.formatUnknownError(err) });
3545
+ deps.log.debug?.("failed to send welcome card", { error: require_channel_setup.formatUnknownError(err) });
3406
3546
  }
3407
3547
  } else if (!isPersonal && msteamsCfg?.groupWelcomeCard === true) {
3408
3548
  const botName = ctx.activity?.recipient?.name ?? void 0;
@@ -3410,7 +3550,7 @@ function registerMSTeamsHandlers(handler, deps) {
3410
3550
  await ctx.sendActivity(buildGroupWelcomeText(botName));
3411
3551
  deps.log.info("sent group welcome message");
3412
3552
  } catch (err) {
3413
- deps.log.debug?.("failed to send group welcome", { error: require_setup_surface.formatUnknownError(err) });
3553
+ deps.log.debug?.("failed to send group welcome", { error: require_channel_setup.formatUnknownError(err) });
3414
3554
  }
3415
3555
  } else deps.log.debug?.("skipping welcome (disabled by config or conversation type)");
3416
3556
  } else deps.log.debug?.("member added", { member: member.id });
@@ -3546,7 +3686,7 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
3546
3686
  userComment,
3547
3687
  log: deps.log
3548
3688
  }).catch((err) => {
3549
- deps.log.error("feedback reflection failed", { error: require_setup_surface.formatUnknownError(err) });
3689
+ deps.log.error("feedback reflection failed", { error: require_channel_setup.formatUnknownError(err) });
3550
3690
  });
3551
3691
  return true;
3552
3692
  }
@@ -3623,7 +3763,7 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
3623
3763
  } catch (err) {
3624
3764
  log.error("file upload failed", {
3625
3765
  uploadId,
3626
- error: require_setup_surface.formatUnknownError(err)
3766
+ error: require_channel_setup.formatUnknownError(err)
3627
3767
  });
3628
3768
  await context.sendActivity("File upload failed. Please try again.");
3629
3769
  } finally {
@@ -3656,10 +3796,28 @@ async function runMSTeamsFileConsentInvokeHandler(context, log) {
3656
3796
  }
3657
3797
  });
3658
3798
  } catch (err) {
3659
- log.debug?.("file consent handler error", { error: require_setup_surface.formatUnknownError(err) });
3799
+ log.debug?.("file consent handler error", { error: require_channel_setup.formatUnknownError(err) });
3660
3800
  }
3661
3801
  }
3662
3802
  //#endregion
3803
+ //#region extensions/msteams/src/monitor-status.ts
3804
+ function publishMSTeamsBlocked(statusSink, lastError) {
3805
+ statusSink?.((0, openclaw_plugin_sdk_gateway_runtime.channelBlockedPatch)(lastError, { running: true }));
3806
+ }
3807
+ function publishMSTeamsReady(statusSink, now = Date.now()) {
3808
+ statusSink?.((0, openclaw_plugin_sdk_gateway_runtime.channelReadyPatch)({ lastConnectedAt: now }));
3809
+ }
3810
+ function publishMSTeamsRecovering(statusSink, lastError) {
3811
+ statusSink?.({
3812
+ connected: false,
3813
+ lifecycle: "recovering",
3814
+ lastError
3815
+ });
3816
+ }
3817
+ function publishMSTeamsStopped(statusSink) {
3818
+ statusSink?.((0, openclaw_plugin_sdk_gateway_runtime.channelStoppedPatch)());
3819
+ }
3820
+ //#endregion
3663
3821
  //#region extensions/msteams/src/msteams-ingress.ts
3664
3822
  const MSTEAMS_INGRESS_VERSION = 1;
3665
3823
  const MSTEAMS_INGRESS_DRAIN_INTERVAL_MS = 500;
@@ -3695,9 +3853,6 @@ function parseClaimedActivity(payload, claimedId) {
3695
3853
  if (facts.eventId !== claimedId) throw new MSTeamsIngressPayloadError("invalid-activity", "Microsoft Teams activity id changed after durable admission.");
3696
3854
  return parsed;
3697
3855
  }
3698
- function errorText(error) {
3699
- return error instanceof Error ? error.message : String(error);
3700
- }
3701
3856
  function createMSTeamsIngress(options) {
3702
3857
  const queue = options.queue ?? require_runtime.getMSTeamsRuntime().state.openChannelIngressQueue({ accountId: options.accountId });
3703
3858
  const liveContexts = /* @__PURE__ */ new Map();
@@ -3746,15 +3901,15 @@ function createMSTeamsIngress(options) {
3746
3901
  reason: error.reason,
3747
3902
  message: error.message
3748
3903
  };
3749
- return require_setup_surface.classifyMSTeamsSendError(error).kind === "auth" ? {
3904
+ return require_channel_setup.classifyMSTeamsSendError(error).kind === "auth" ? {
3750
3905
  reason: "authentication-failed",
3751
- message: errorText(error)
3906
+ message: (0, openclaw_plugin_sdk_error_runtime.formatErrorMessage)(error)
3752
3907
  } : null;
3753
3908
  },
3754
3909
  onLog: (message) => options.runtime.error?.(`msteams: ${message}`)
3755
3910
  },
3756
3911
  createStoppedError: () => /* @__PURE__ */ new Error("Microsoft Teams ingress stopped."),
3757
- onError: (error) => options.runtime.error?.(`msteams ingress drain failed: ${errorText(error)}`)
3912
+ onError: (error) => options.runtime.error?.(`msteams ingress drain failed: ${(0, openclaw_plugin_sdk_error_runtime.formatErrorMessage)(error)}`)
3758
3913
  });
3759
3914
  let stopTask;
3760
3915
  return {
@@ -3799,6 +3954,54 @@ function createMSTeamsIngress(options) {
3799
3954
  };
3800
3955
  }
3801
3956
  //#endregion
3957
+ //#region extensions/msteams/src/replay-context.ts
3958
+ function createMSTeamsReplayContext(activity, app, serviceUrlBoundary) {
3959
+ const rawConversationId = activity.conversation?.id ?? "";
3960
+ const conversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(rawConversationId);
3961
+ const conversationType = activity.conversation?.conversationType ?? "personal";
3962
+ const normalizedConversationType = conversationType.toLowerCase();
3963
+ const threadActivityId = normalizedConversationType === "channel" ? require_resolve_allowlist.extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId : void 0;
3964
+ const quoteActivityId = normalizedConversationType === "channel" || normalizedConversationType === "groupchat" ? activity.id : void 0;
3965
+ const tenantId = activity.channelData?.tenant?.id ?? activity.conversation?.tenantId;
3966
+ const reference = {
3967
+ activityId: activity.id,
3968
+ user: activity.from,
3969
+ agent: activity.recipient,
3970
+ conversation: {
3971
+ id: conversationId,
3972
+ conversationType,
3973
+ ...tenantId ? { tenantId } : {}
3974
+ },
3975
+ channelId: activity.channelId,
3976
+ serviceUrl: activity.serviceUrl,
3977
+ locale: activity.locale,
3978
+ ...tenantId ? { tenantId } : {},
3979
+ ...activity.from?.aadObjectId ? { aadObjectId: activity.from.aadObjectId } : {}
3980
+ };
3981
+ const proactiveOptions = {
3982
+ ...quoteActivityId ? { quoteActivityId } : {},
3983
+ ...threadActivityId ? { threadActivityId } : {},
3984
+ serviceUrlBoundary
3985
+ };
3986
+ const sendActivity = (outbound) => require_messenger.sendMSTeamsActivityWithReference(app, reference, outbound, proactiveOptions);
3987
+ return {
3988
+ activity,
3989
+ sendActivity,
3990
+ sendActivities: async (activities) => {
3991
+ const results = [];
3992
+ for (const outbound of activities) results.push(await sendActivity(outbound));
3993
+ return results;
3994
+ },
3995
+ updateActivity: async (outbound) => {
3996
+ return await require_messenger.updateMSTeamsActivityWithReference(app, reference, typeof outbound.id === "string" ? outbound.id : "", outbound, proactiveOptions);
3997
+ },
3998
+ deleteActivity: async (activityId) => {
3999
+ await require_messenger.deleteMSTeamsActivityWithReference(app, reference, activityId, proactiveOptions);
4000
+ },
4001
+ getTeamDetails: (teamId) => app.api.teams.getById(teamId)
4002
+ };
4003
+ }
4004
+ //#endregion
3802
4005
  //#region extensions/msteams/src/webhook-timeouts.ts
3803
4006
  const MSTEAMS_WEBHOOK_INACTIVITY_TIMEOUT_MS = 3e4;
3804
4007
  const MSTEAMS_WEBHOOK_REQUEST_TIMEOUT_MS = 3e4;
@@ -3820,6 +4023,7 @@ async function monitorMSTeamsProvider(opts) {
3820
4023
  let msteamsCfg = cfg.channels?.msteams;
3821
4024
  if (!msteamsCfg?.enabled) {
3822
4025
  log.debug?.("msteams provider disabled");
4026
+ publishMSTeamsBlocked(opts.statusSink, "Microsoft Teams provider is disabled");
3823
4027
  return {
3824
4028
  app: null,
3825
4029
  shutdown: async () => {}
@@ -3828,6 +4032,7 @@ async function monitorMSTeamsProvider(opts) {
3828
4032
  const creds = require_resolve_allowlist.resolveMSTeamsCredentials(msteamsCfg);
3829
4033
  if (!creds) {
3830
4034
  log.error("msteams credentials not configured");
4035
+ publishMSTeamsBlocked(opts.statusSink, "Microsoft Teams credentials are not configured");
3831
4036
  return {
3832
4037
  app: null,
3833
4038
  shutdown: async () => {}
@@ -3901,7 +4106,7 @@ async function monitorMSTeamsProvider(opts) {
3901
4106
  (0, openclaw_plugin_sdk_allow_from.summarizeMapping)("msteams channels", resolved.mapping, resolved.unresolved, runtime);
3902
4107
  }
3903
4108
  } catch (err) {
3904
- runtime.error?.(`msteams resolve failed; mutable allowlist entries are disabled. ${require_setup_surface.formatUnknownError(err)}`);
4109
+ runtime.error?.(`msteams resolve failed; mutable allowlist entries are disabled. ${require_channel_setup.formatUnknownError(err)}`);
3905
4110
  }
3906
4111
  if (configuredGroupAllowFrom == null && groupAllowFrom) groupAllowFrom = (0, openclaw_plugin_sdk_allow_from.mergeAllowlist)({
3907
4112
  existing: groupAllowFrom,
@@ -3922,9 +4127,10 @@ async function monitorMSTeamsProvider(opts) {
3922
4127
  };
3923
4128
  const port = msteamsCfg.webhook?.port ?? 3978;
3924
4129
  const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "msteams");
3925
- const MB = 1024 * 1024;
3926
- const agentDefaults = cfg.agents?.defaults;
3927
- const mediaMaxBytes = typeof agentDefaults?.mediaMaxMb === "number" && agentDefaults.mediaMaxMb > 0 ? Math.floor(agentDefaults.mediaMaxMb * MB) : 8 * MB;
4130
+ const mediaMaxBytes = (0, openclaw_plugin_sdk_media_runtime.resolveChannelMediaMaxBytes)({
4131
+ cfg,
4132
+ resolveChannelLimitMb: ({ cfg: channelCfg }) => channelCfg.channels?.msteams?.mediaMaxMb
4133
+ }) ?? 8 * 1024 * 1024;
3928
4134
  const conversationStore = opts.conversationStore ?? require_polls.createMSTeamsConversationStoreState();
3929
4135
  const pollStore = opts.pollStore ?? require_polls.createMSTeamsPollStoreState();
3930
4136
  log.info(`starting provider (port ${port})`);
@@ -4054,7 +4260,7 @@ async function monitorMSTeamsProvider(opts) {
4054
4260
  } catch (err) {
4055
4261
  log.error("failed to record poll vote", {
4056
4262
  pollId: vote.pollId,
4057
- error: require_setup_surface.formatUnknownError(err)
4263
+ error: require_channel_setup.formatUnknownError(err)
4058
4264
  });
4059
4265
  return {
4060
4266
  statusCode: 500,
@@ -4077,7 +4283,7 @@ async function monitorMSTeamsProvider(opts) {
4077
4283
  value: "OK"
4078
4284
  };
4079
4285
  } catch (err) {
4080
- log.error("msteams card.action failed", { error: require_setup_surface.formatUnknownError(err) });
4286
+ log.error("msteams card.action failed", { error: require_channel_setup.formatUnknownError(err) });
4081
4287
  return {
4082
4288
  statusCode: 500,
4083
4289
  type: "application/vnd.microsoft.error",
@@ -4145,7 +4351,7 @@ async function monitorMSTeamsProvider(opts) {
4145
4351
  hasExpiry: Boolean(ctx.token.expiration)
4146
4352
  });
4147
4353
  })().catch((err) => {
4148
- log.error("msteams sso token persistence failed", { error: require_setup_surface.formatUnknownError(err) });
4354
+ log.error("msteams sso token persistence failed", { error: require_channel_setup.formatUnknownError(err) });
4149
4355
  });
4150
4356
  });
4151
4357
  app.on("message.submit", async (ctx) => {
@@ -4166,32 +4372,37 @@ async function monitorMSTeamsProvider(opts) {
4166
4372
  try {
4167
4373
  await handler.run(adaptedCtx);
4168
4374
  } catch (err) {
4169
- log.error("msteams non-turn activity failed", { error: require_setup_surface.formatUnknownError(err) });
4375
+ log.error("msteams non-turn activity failed", { error: require_channel_setup.formatUnknownError(err) });
4170
4376
  }
4171
4377
  });
4172
4378
  await app.initialize();
4173
4379
  ingress.start();
4380
+ const privateQaRuntime = require_resolve_allowlist.resolveMSTeamsPrivateQaRuntime();
4174
4381
  const httpServer = await new Promise((resolve, reject) => {
4175
- const server = expressApp.listen(port, (err) => err ? reject(err) : resolve(server));
4382
+ const onListen = (err) => err ? reject(err) : resolve(server);
4383
+ const server = privateQaRuntime ? expressApp.listen(port, privateQaRuntime.listenHost, onListen) : expressApp.listen(port, onListen);
4176
4384
  }).catch(async (err) => {
4177
- log.error("msteams server error", { error: require_setup_surface.formatUnknownError(err) });
4385
+ log.error("msteams server error", { error: require_channel_setup.formatUnknownError(err) });
4178
4386
  await ingress.stop();
4179
4387
  throw err;
4180
4388
  });
4181
4389
  log.info(`msteams provider started on port ${port}`);
4390
+ publishMSTeamsReady(opts.statusSink);
4182
4391
  applyMSTeamsWebhookTimeouts(httpServer);
4183
4392
  httpServer.on("error", (err) => {
4184
- log.error("msteams server error", { error: require_setup_surface.formatUnknownError(err) });
4393
+ log.error("msteams server error", { error: require_channel_setup.formatUnknownError(err) });
4394
+ publishMSTeamsRecovering(opts.statusSink, require_channel_setup.formatUnknownError(err));
4185
4395
  });
4186
4396
  const shutdown = async () => {
4187
4397
  log.info("shutting down msteams provider");
4188
4398
  await new Promise((resolve) => {
4189
4399
  httpServer.close((err) => {
4190
- if (err) log.debug?.("msteams server close error", { error: require_setup_surface.formatUnknownError(err) });
4400
+ if (err) log.debug?.("msteams server close error", { error: require_channel_setup.formatUnknownError(err) });
4191
4401
  resolve();
4192
4402
  });
4193
4403
  });
4194
4404
  await ingress.stop();
4405
+ publishMSTeamsStopped(opts.statusSink);
4195
4406
  };
4196
4407
  await (0, openclaw_plugin_sdk_channel_outbound.keepHttpServerTaskAlive)({
4197
4408
  server: httpServer,
@@ -4247,47 +4458,6 @@ function buildActivityHandler() {
4247
4458
  };
4248
4459
  return handler;
4249
4460
  }
4250
- function createMSTeamsReplayContext(activity, app, serviceUrlBoundary) {
4251
- const rawConversationId = activity.conversation?.id ?? "";
4252
- const conversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(rawConversationId);
4253
- const conversationType = activity.conversation?.conversationType ?? "personal";
4254
- const threadActivityId = conversationType.toLowerCase() === "channel" ? require_resolve_allowlist.extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId : void 0;
4255
- const tenantId = activity.channelData?.tenant?.id ?? activity.conversation?.tenantId;
4256
- const reference = {
4257
- activityId: activity.id,
4258
- user: activity.from,
4259
- agent: activity.recipient,
4260
- conversation: {
4261
- id: conversationId,
4262
- conversationType,
4263
- ...tenantId ? { tenantId } : {}
4264
- },
4265
- channelId: activity.channelId,
4266
- serviceUrl: activity.serviceUrl,
4267
- locale: activity.locale,
4268
- ...tenantId ? { tenantId } : {},
4269
- ...activity.from?.aadObjectId ? { aadObjectId: activity.from.aadObjectId } : {}
4270
- };
4271
- const proactiveOptions = {
4272
- ...threadActivityId ? { threadActivityId } : {},
4273
- serviceUrlBoundary
4274
- };
4275
- const sendActivity = (outbound) => require_messenger.sendMSTeamsActivityWithReference(app, reference, outbound, proactiveOptions);
4276
- return {
4277
- activity,
4278
- sendActivity,
4279
- sendActivities: async (activities) => {
4280
- const results = [];
4281
- for (const outbound of activities) results.push(await sendActivity(outbound));
4282
- return results;
4283
- },
4284
- updateActivity: async (outbound) => await require_messenger.updateMSTeamsActivityWithReference(app, reference, typeof outbound.id === "string" ? outbound.id : "", outbound, proactiveOptions),
4285
- deleteActivity: async (activityId) => {
4286
- await require_messenger.deleteMSTeamsActivityWithReference(app, reference, activityId, proactiveOptions);
4287
- },
4288
- getTeamDetails: (teamId) => app.api.teams.getById(teamId)
4289
- };
4290
- }
4291
4461
  /**
4292
4462
  * Adapt a new @microsoft/teams.apps SDK context to the MSTeamsTurnContext interface
4293
4463
  * our handlers expect. The new SDK uses reply()/send() instead of sendActivity().