@openclaw/msteams 2026.9.4 → 2026.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/assets/activity.svg +40 -0
  2. package/assets/icon.png +0 -0
  3. package/dist/{approval-handler.runtime-DRhFNeJS.cjs → .setup/approval-handler.runtime-DDxGPJDz.cjs} +3 -3
  4. package/dist/{channel-B9LIyYeu.cjs → .setup/channel-Dzpbbht8.cjs} +62 -35
  5. package/dist/{channel.runtime-BergqMHg.cjs → .setup/channel.runtime-TE49QvFv.cjs} +57 -32
  6. package/dist/{channel.setup-BhxokviI.cjs → .setup/channel.setup-BVDfgdCc.cjs} +3 -3
  7. package/dist/{delegated-state-DiEz9JIa.cjs → .setup/delegated-state-u-ywZyZT.cjs} +5 -5
  8. package/dist/{messenger-TLdZf9Ar.cjs → .setup/messenger-BR1l4zw-.cjs} +108 -178
  9. package/dist/{oauth-DUtLTz2u.cjs → .setup/oauth-C6m7dvHj.cjs} +1 -1
  10. package/dist/{polls-BBdZPx_R.cjs → .setup/polls-BJ4-4uZm.cjs} +14 -7
  11. package/dist/{reaction-types-BbBBdnQ5.cjs → .setup/reaction-types-C7RA7Wxt.cjs} +1 -1
  12. package/dist/{resolve-allowlist-DmUTQxEz.cjs → .setup/resolve-allowlist-CHzaaWiC.cjs} +135 -93
  13. package/dist/.setup/secret-contract-api-ChQdBeU1.cjs +4 -0
  14. package/dist/.setup/secret-contract-api-ipiUsANg.cjs +28 -0
  15. package/dist/{send-CU61PSDN.cjs → .setup/send-D8ATH4QI.cjs} +95 -33
  16. package/dist/.setup/setup-plugin-api-DpiaA8z1.cjs +3 -0
  17. package/dist/{src-D7Do7sM5.cjs → .setup/src-CpKfpP1m.cjs} +132 -54
  18. package/dist/{sso-token-store-DTeYxubp.cjs → .setup/sso-token-store-YYLHXgFH.cjs} +1 -1
  19. package/dist/{thread-session-NmA7_njI.cjs → .setup/thread-session-CXgHK-yi.cjs} +1 -1
  20. package/dist/api.cjs +2 -2
  21. package/dist/channel-config-api.cjs +1 -1
  22. package/dist/channel-plugin-api.cjs +1 -1
  23. package/dist/configured-state.cjs +1 -1
  24. package/dist/directory-contract-api.cjs +1 -1
  25. package/dist/doctor-contract-api.cjs +64 -62
  26. package/dist/qa-runner-api.cjs +1 -1
  27. package/dist/runtime-api.cjs +1 -1
  28. package/dist/secret-contract-api.cjs +4 -13
  29. package/dist/setup-entry.cjs +5 -3
  30. package/dist/setup-plugin-api.cjs +1 -1
  31. package/openclaw.plugin.json +1 -1
  32. package/package.json +14 -11
  33. package/dist/{adapter.runtime-BzR3RK2J.cjs → .setup/adapter.runtime-ChuVK6k_.cjs} +2 -2
  34. /package/dist/{approval-card-CPNH_q3P.cjs → .setup/approval-card-CPNH_q3P.cjs} +0 -0
  35. /package/dist/{config-schema-D22Lr1Qk.cjs → .setup/config-schema-D22Lr1Qk.cjs} +0 -0
  36. /package/dist/{rolldown-runtime-VH7oDXx4.cjs → .setup/rolldown-runtime-VH7oDXx4.cjs} +0 -0
  37. /package/dist/{runtime-gZTAuFux.cjs → .setup/runtime-gZTAuFux.cjs} +0 -0
  38. /package/dist/{secret-input-C65t6kjM.cjs → .setup/secret-input-C65t6kjM.cjs} +0 -0
@@ -1,12 +1,14 @@
1
1
  const require_runtime = require("./runtime-gZTAuFux.cjs");
2
- require("./runtime-api.cjs");
3
- const require_resolve_allowlist = require("./resolve-allowlist-DmUTQxEz.cjs");
4
- const require_channel = require("./channel-B9LIyYeu.cjs");
5
- const require_channel_setup = require("./channel.setup-BhxokviI.cjs");
6
- const require_polls = require("./polls-BBdZPx_R.cjs");
7
- const require_messenger = require("./messenger-TLdZf9Ar.cjs");
2
+ require("../runtime-api.cjs");
3
+ const require_resolve_allowlist = require("./resolve-allowlist-CHzaaWiC.cjs");
4
+ const require_channel = require("./channel-Dzpbbht8.cjs");
5
+ const require_channel_setup = require("./channel.setup-BVDfgdCc.cjs");
6
+ const require_polls = require("./polls-BJ4-4uZm.cjs");
7
+ const require_messenger = require("./messenger-BR1l4zw-.cjs");
8
8
  let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
9
9
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
10
+ let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
11
+ let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
10
12
  let openclaw_plugin_sdk_markdown_table_runtime = require("openclaw/plugin-sdk/markdown-table-runtime");
11
13
  let openclaw_plugin_sdk_account_helpers = require("openclaw/plugin-sdk/account-helpers");
12
14
  let openclaw_plugin_sdk_outbound_media = require("openclaw/plugin-sdk/outbound-media");
@@ -176,11 +178,27 @@ const FILE_CONSENT_THRESHOLD_BYTES = 4194304;
176
178
  */
177
179
  const MSTEAMS_MAX_MEDIA_BYTES = 104857600;
178
180
  function createMSTeamsSendError(errorPrefix, error) {
181
+ if (error instanceof Error && (error instanceof openclaw_plugin_sdk_error_runtime.PlatformMessageNotDispatchedError || (0, openclaw_plugin_sdk_channel_inbound.isChannelPartialDeliveryError)(error))) return error;
179
182
  const classification = require_channel_setup.classifyMSTeamsSendError(error);
180
183
  const hint = require_channel_setup.formatMSTeamsSendErrorHint(classification);
181
184
  const status = classification.statusCode ? ` (HTTP ${classification.statusCode})` : "";
182
185
  return new Error(`${errorPrefix} failed${status}: ${require_channel_setup.formatUnknownError(error)}${hint ? ` (${hint})` : ""}`, { cause: error });
183
186
  }
187
+ function createMSTeamsPartialSendError(error, receipt) {
188
+ return (0, openclaw_plugin_sdk_channel_inbound.createChannelPartialDeliveryError)(error, {
189
+ visibleReplySent: true,
190
+ messageIds: receipt.platformMessageIds,
191
+ receipt
192
+ });
193
+ }
194
+ async function finishMSTeamsSend(result, settle) {
195
+ try {
196
+ await settle();
197
+ } catch (error) {
198
+ throw createMSTeamsPartialSendError(error, result.receipt);
199
+ }
200
+ return result;
201
+ }
184
202
  function createMSTeamsSendReceipt(params) {
185
203
  const receipt = (0, openclaw_plugin_sdk_channel_outbound.createMessageReceiptFromOutboundResults)({
186
204
  kind: params.kind,
@@ -232,6 +250,7 @@ function createMSTeamsSendResult(params) {
232
250
  * - Group chats / channels: files require configured SharePoint storage
233
251
  */
234
252
  async function sendMessageMSTeams(params) {
253
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
235
254
  const { cfg, to, text, mediaUrl, filename, mediaAccess, mediaLocalRoots, mediaReadFile } = params;
236
255
  const tableMode = (0, openclaw_plugin_sdk_markdown_table_runtime.resolveMarkdownTableMode)({
237
256
  cfg,
@@ -275,6 +294,7 @@ async function sendMessageMSTeams(params) {
275
294
  bufferSize: media.buffer.length,
276
295
  thresholdBytes: FILE_CONSENT_THRESHOLD_BYTES
277
296
  })) {
297
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
278
298
  const { activity, uploadId } = await require_messenger.prepareFileConsentActivityFs({
279
299
  media: {
280
300
  buffer: media.buffer,
@@ -292,29 +312,37 @@ async function sendMessageMSTeams(params) {
292
312
  const messageId = await sendProactiveActivity({
293
313
  ctx,
294
314
  activity,
295
- errorPrefix: "msteams consent card send"
315
+ errorPrefix: "msteams consent card send",
316
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff,
317
+ onPlatformSendDispatch: params.onPlatformSendDispatch
296
318
  });
297
- require_messenger.setPendingUploadActivityId(uploadId, messageId);
298
- await require_messenger.setPendingUploadActivityIdFs(uploadId, messageId);
299
- log.info("sent file consent card", {
300
- conversationId,
301
- messageId,
302
- uploadId
303
- });
304
- return createMSTeamsSendResult({
319
+ const result = createMSTeamsSendResult({
305
320
  messageId,
306
321
  conversationId,
307
322
  kind: "card",
308
323
  pendingUploadId: uploadId
309
324
  });
325
+ return finishMSTeamsSend(result, async () => {
326
+ require_messenger.setPendingUploadActivityId(uploadId, messageId);
327
+ try {
328
+ await params.onDeliveryResult?.(result);
329
+ } finally {
330
+ await require_messenger.setPendingUploadActivityIdFs(uploadId, messageId);
331
+ }
332
+ log.info("sent file consent card", {
333
+ conversationId,
334
+ messageId,
335
+ uploadId
336
+ });
337
+ });
310
338
  }
311
339
  if (conversationType === "personal") {
312
340
  const base64 = media.buffer.toString("base64");
313
- return sendTextWithMedia(ctx, messageText, `data:${media.contentType};base64,${base64}`);
341
+ return sendTextWithMedia(ctx, messageText, `data:${media.contentType};base64,${base64}`, params);
314
342
  }
315
343
  if (isImage && !sharePointSiteId) {
316
344
  const base64 = media.buffer.toString("base64");
317
- return sendTextWithMedia(ctx, messageText, `data:${media.contentType};base64,${base64}`);
345
+ return sendTextWithMedia(ctx, messageText, `data:${media.contentType};base64,${base64}`, params);
318
346
  }
319
347
  try {
320
348
  const siteId = require_messenger.requireMSTeamsSharePointSiteId(sharePointSiteId);
@@ -324,6 +352,7 @@ async function sendMessageMSTeams(params) {
324
352
  siteId
325
353
  });
326
354
  const uploaded = await require_messenger.uploadAndShareSharePoint({
355
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff,
327
356
  buffer: media.buffer,
328
357
  filename: fileName,
329
358
  contentType: media.contentType,
@@ -337,6 +366,7 @@ async function sendMessageMSTeams(params) {
337
366
  shareUrl: uploaded.shareUrl
338
367
  });
339
368
  const driveItem = await require_messenger.getDriveItemProperties({
369
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff,
340
370
  siteId,
341
371
  itemId: uploaded.itemId,
342
372
  tokenProvider
@@ -351,36 +381,55 @@ async function sendMessageMSTeams(params) {
351
381
  activity: {
352
382
  ...require_messenger.buildMSTeamsMessageActivity(messageText || void 0),
353
383
  attachments: [fileCardAttachment]
354
- }
384
+ },
385
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff,
386
+ onPlatformSendDispatch: params.onPlatformSendDispatch
355
387
  });
356
388
  log.info("sent native file card", {
357
389
  conversationId,
358
390
  messageId,
359
391
  fileName: driveItem.name
360
392
  });
361
- return createMSTeamsSendResult({
393
+ const result = createMSTeamsSendResult({
362
394
  messageId,
363
395
  conversationId,
364
396
  kind: "media"
365
397
  });
398
+ return await finishMSTeamsSend(result, async () => {
399
+ await params.onDeliveryResult?.(result);
400
+ });
366
401
  } catch (err) {
367
402
  throw createMSTeamsSendError("msteams file send", err);
368
403
  }
369
404
  }
370
- return sendTextWithMedia(ctx, messageText, void 0);
405
+ return sendTextWithMedia(ctx, messageText, void 0, params);
371
406
  }
372
407
  /**
373
408
  * Send a text message with optional base64 media URL.
374
409
  */
375
- async function sendTextWithMedia(ctx, text, mediaUrl) {
410
+ async function sendTextWithMedia(ctx, text, mediaUrl, options) {
376
411
  const { app, appId, conversationId, ref, log, tokenProvider, sharePointSiteId, mediaMaxBytes, replyStyle } = ctx;
377
412
  const messages = text && mediaUrl ? [{ text }, { mediaUrl }] : [{
378
413
  text: text || void 0,
379
414
  mediaUrl
380
415
  }];
381
416
  let platformMessageIds;
417
+ const acceptedIds = [];
418
+ const acceptedKinds = [];
382
419
  try {
383
420
  platformMessageIds = await require_messenger.sendMSTeamsMessages({
421
+ assertDirectAdapterHandoff: options.assertDirectAdapterHandoff,
422
+ onPlatformSendDispatch: options.onPlatformSendDispatch,
423
+ onMessageSent: async (messageId, messageIndex) => {
424
+ const kind = messages[messageIndex]?.mediaUrl ? "media" : "text";
425
+ acceptedIds.push(messageId);
426
+ acceptedKinds.push(kind);
427
+ await options.onDeliveryResult?.(createMSTeamsSendResult({
428
+ conversationId,
429
+ messageId,
430
+ kind
431
+ }));
432
+ },
384
433
  replyStyle,
385
434
  app,
386
435
  appId,
@@ -399,7 +448,14 @@ async function sendTextWithMedia(ctx, text, mediaUrl) {
399
448
  serviceUrlBoundary: ctx.sdkCloudOptions
400
449
  });
401
450
  } catch (err) {
402
- throw createMSTeamsSendError("msteams send", err);
451
+ const error = createMSTeamsSendError("msteams send", err);
452
+ if (acceptedIds.length > 0) throw createMSTeamsPartialSendError(error, createMSTeamsSendReceipt({
453
+ conversationId,
454
+ platformMessageIds: acceptedIds,
455
+ kind: mediaUrl ? "media" : "text",
456
+ kinds: acceptedKinds
457
+ }));
458
+ throw error;
403
459
  }
404
460
  const messageId = platformMessageIds[0] ?? "unknown";
405
461
  log.info("sent proactive message", {
@@ -417,22 +473,21 @@ async function sendTextWithMedia(ctx, text, mediaUrl) {
417
473
  })
418
474
  };
419
475
  }
420
- async function sendProactiveActivityRaw({ ctx, activity }) {
476
+ async function sendProactiveActivityRaw({ ctx, activity, assertDirectAdapterHandoff, onPlatformSendDispatch }) {
421
477
  const baseRef = require_messenger.buildConversationReference(ctx.ref);
422
478
  const response = await require_messenger.sendMSTeamsActivityWithReference(ctx.app, baseRef, activity, {
479
+ assertDirectAdapterHandoff,
480
+ onPlatformSendDispatch,
423
481
  ...ctx.threadActivityId ? { threadActivityId: ctx.threadActivityId } : {},
424
482
  serviceUrlBoundary: ctx.sdkCloudOptions
425
483
  });
426
484
  return require_messenger.extractMessageId(response) ?? "unknown";
427
485
  }
428
- async function sendProactiveActivity({ ctx, activity, errorPrefix }) {
486
+ async function sendProactiveActivity(params) {
429
487
  try {
430
- return await sendProactiveActivityRaw({
431
- ctx,
432
- activity
433
- });
488
+ return await sendProactiveActivityRaw(params);
434
489
  } catch (err) {
435
- throw createMSTeamsSendError(errorPrefix, err);
490
+ throw createMSTeamsSendError(params.errorPrefix, err);
436
491
  }
437
492
  }
438
493
  /**
@@ -481,6 +536,7 @@ async function sendPollMSTeams(params) {
481
536
  * Send an arbitrary Adaptive Card to a Teams conversation or user.
482
537
  */
483
538
  async function sendAdaptiveCardMSTeams(params) {
539
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
484
540
  const { cfg, to, card } = params;
485
541
  const ctx = await resolveMSTeamsSendContext({
486
542
  cfg,
@@ -501,16 +557,22 @@ async function sendAdaptiveCardMSTeams(params) {
501
557
  content: card
502
558
  }]
503
559
  },
504
- errorPrefix: "msteams card send"
560
+ errorPrefix: "msteams card send",
561
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff,
562
+ onPlatformSendDispatch: params.onPlatformSendDispatch
505
563
  });
506
564
  log.info("sent adaptive card", {
507
565
  conversationId,
508
566
  messageId
509
567
  });
510
- return {
568
+ const result = createMSTeamsSendResult({
511
569
  messageId,
512
- conversationId
513
- };
570
+ conversationId,
571
+ kind: "card"
572
+ });
573
+ return finishMSTeamsSend(result, async () => {
574
+ await params.onDeliveryResult?.(result);
575
+ });
514
576
  }
515
577
  /**
516
578
  * Edit (update) a previously sent message in a Teams conversation.
@@ -0,0 +1,3 @@
1
+ const require_channel_setup = require("./channel.setup-BVDfgdCc.cjs");
2
+ require("../setup-plugin-api.cjs");
3
+ exports.msteamsSetupPlugin = require_channel_setup.msteamsSetupPlugin;
@@ -1,15 +1,15 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
2
2
  const require_runtime = require("./runtime-gZTAuFux.cjs");
3
- require("./runtime-api.cjs");
4
- const require_resolve_allowlist = require("./resolve-allowlist-DmUTQxEz.cjs");
5
- const require_channel = require("./channel-B9LIyYeu.cjs");
6
- const require_thread_session = require("./thread-session-NmA7_njI.cjs");
7
- const require_channel_setup = require("./channel.setup-BhxokviI.cjs");
8
- const require_polls = require("./polls-BBdZPx_R.cjs");
9
- const require_sso_token_store = require("./sso-token-store-DTeYxubp.cjs");
3
+ require("../runtime-api.cjs");
4
+ const require_resolve_allowlist = require("./resolve-allowlist-CHzaaWiC.cjs");
5
+ const require_channel = require("./channel-Dzpbbht8.cjs");
6
+ const require_thread_session = require("./thread-session-CXgHK-yi.cjs");
7
+ const require_channel_setup = require("./channel.setup-BVDfgdCc.cjs");
8
+ const require_polls = require("./polls-BJ4-4uZm.cjs");
9
+ const require_sso_token_store = require("./sso-token-store-YYLHXgFH.cjs");
10
10
  const require_approval_card = require("./approval-card-CPNH_q3P.cjs");
11
- const require_messenger = require("./messenger-TLdZf9Ar.cjs");
12
- const require_reaction_types = require("./reaction-types-BbBBdnQ5.cjs");
11
+ const require_messenger = require("./messenger-BR1l4zw-.cjs");
12
+ const require_reaction_types = require("./reaction-types-C7RA7Wxt.cjs");
13
13
  let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
14
14
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
15
15
  let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
@@ -19,10 +19,10 @@ let openclaw_plugin_sdk_media_runtime = require("openclaw/plugin-sdk/media-runti
19
19
  let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
20
20
  let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-runtime");
21
21
  let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text-utility-runtime");
22
+ let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
22
23
  let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
23
24
  let node_crypto = require("node:crypto");
24
25
  node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
25
- let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
26
26
  let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
27
27
  let openclaw_plugin_sdk_approval_gateway_runtime = require("openclaw/plugin-sdk/approval-gateway-runtime");
28
28
  let openclaw_plugin_sdk_channel_ingress_runtime = require("openclaw/plugin-sdk/channel-ingress-runtime");
@@ -47,18 +47,35 @@ const MAX_LEARNING_ENTRIES = 1e4;
47
47
  function learningStoreKey(storePath, sessionKey) {
48
48
  return node_crypto.default.createHash("sha256").update(`${storePath}\0${sessionKey}`, "utf8").digest("hex");
49
49
  }
50
+ function appendFeedbackLearning(current, prepared) {
51
+ return {
52
+ sessionKey: prepared.sessionKey,
53
+ learnings: [...current?.learnings ?? [], prepared.learning].slice(-10),
54
+ updatedAt: prepared.updatedAt
55
+ };
56
+ }
50
57
  async function storeSessionLearning(params) {
51
58
  const store = require_runtime.getMSTeamsRuntime().state.openKeyedStore({
52
59
  namespace: LEARNINGS_NAMESPACE,
53
60
  maxEntries: MAX_LEARNING_ENTRIES
54
61
  });
55
62
  const key = learningStoreKey(params.storePath, params.sessionKey);
56
- if (!store.update) throw new Error("plugin state atomic update is unavailable");
57
- await store.update(key, (existing) => ({
63
+ if (!store.observe || !store.compareAndApply) throw new Error("plugin state atomic comparison is unavailable");
64
+ const prepared = {
58
65
  sessionKey: params.sessionKey,
59
- learnings: [...existing?.learnings ?? [], params.learning].slice(-10),
66
+ learning: params.learning,
60
67
  updatedAt: Date.now()
61
- }));
68
+ };
69
+ let observed = await store.observe(key);
70
+ while (true) {
71
+ const result = await store.compareAndApply(key, observed.comparison, {
72
+ operation: "update",
73
+ action: "set",
74
+ value: appendFeedbackLearning(observed.value, prepared)
75
+ });
76
+ if (result.status !== "conflict") return;
77
+ observed = result.current;
78
+ }
62
79
  }
63
80
  //#endregion
64
81
  //#region extensions/msteams/src/feedback-reflection.ts
@@ -310,6 +327,7 @@ async function resolveMSTeamsSenderAccess(params) {
310
327
  const conversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(activity.conversation?.id ?? "unknown");
311
328
  const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(activity.conversation?.conversationType);
312
329
  const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
330
+ const hasConflictingConversationScope = isDirectMessage && (activity.conversation?.isGroup === true || activity.channelData?.team !== void 0 || activity.channelData?.channel !== void 0);
313
331
  const senderId = activity.from?.aadObjectId ?? activity.from?.id ?? "unknown";
314
332
  const senderName = activity.from?.name ?? activity.from?.id ?? senderId;
315
333
  const core = require_runtime.getMSTeamsRuntime();
@@ -397,6 +415,7 @@ async function resolveMSTeamsSenderAccess(params) {
397
415
  channelIngress: resolved,
398
416
  pairing,
399
417
  isDirectMessage,
418
+ hasConflictingConversationScope,
400
419
  conversationId,
401
420
  senderId,
402
421
  senderName,
@@ -419,9 +438,14 @@ async function admitMSTeamsMessage(params) {
419
438
  activity: params.activity,
420
439
  hasControlCommand: isControlCommand
421
440
  });
422
- const { dmPolicy, senderId, senderName, pairing, isDirectMessage, channelGate, senderAccess, commandAccess, allowNameMatching, groupPolicy, msteamsCfg } = access;
441
+ const { dmPolicy, senderId, senderName, pairing, isDirectMessage, hasConflictingConversationScope, channelGate, senderAccess, commandAccess, allowNameMatching, groupPolicy, msteamsCfg } = access;
423
442
  const effectiveDmAllowFrom = senderAccess.effectiveAllowFrom;
424
443
  const effectiveGroupAllowFrom = senderAccess.effectiveGroupAllowFrom;
444
+ if (hasConflictingConversationScope) {
445
+ params.log.info("dropping message (conflicting conversation scope)", { conversationId: params.conversationId });
446
+ params.log.debug?.("dropping message (conflicting conversation scope)");
447
+ return null;
448
+ }
425
449
  if (isDirectMessage && msteamsCfg && senderAccess.decision !== "allow") {
426
450
  if (senderAccess.reasonCode === "dm_policy_disabled") {
427
451
  params.log.info("dropping dm (dms disabled)", {
@@ -873,11 +897,11 @@ function createAdvertisedMediaFact(kind, sourceId) {
873
897
  if (sourceId) media.sourceId = sourceId;
874
898
  return media;
875
899
  }
876
- function resolveMSTeamsAdvertisedMedia(attachments, limits) {
900
+ function resolveMSTeamsAdvertisedMedia(attachments) {
877
901
  const list = Array.isArray(attachments) ? attachments : [];
878
902
  if (list.length === 0) return [];
879
903
  const fileAttachments = list.filter(require_resolve_allowlist.isAdvertisedFileAttachment);
880
- const inlineMedia = require_resolve_allowlist.extractInlineImageCandidates(list, limits).map((candidate) => createAdvertisedMediaFact("image", candidate.sourceId));
904
+ const inlineMedia = require_resolve_allowlist.extractInlineImageReferences(list).map((candidate) => createAdvertisedMediaFact("image", candidate.sourceId));
881
905
  const htmlAttachmentIds = resolveUnrepresentedHtmlAttachmentIds(list);
882
906
  return [
883
907
  ...fileAttachments.map((attachment) => createAdvertisedMediaFact(require_resolve_allowlist.isLikelyImageAttachment(attachment) ? "image" : require_resolve_allowlist.resolveMSTeamsMediaKind({
@@ -995,6 +1019,44 @@ function scopeCandidatesForUrl(url) {
995
1019
  return ["https://api.botframework.com", "https://graph.microsoft.com"];
996
1020
  }
997
1021
  }
1022
+ function canonicalizeInlineBase64Payload(value) {
1023
+ let cleaned = "";
1024
+ let padding = 0;
1025
+ let sawPadding = false;
1026
+ for (let index = 0; index < value.length; index += 1) {
1027
+ const code = value.charCodeAt(index);
1028
+ if (code <= 32) continue;
1029
+ if (code === 61) {
1030
+ padding += 1;
1031
+ if (padding > 2) return;
1032
+ sawPadding = true;
1033
+ cleaned += "=";
1034
+ continue;
1035
+ }
1036
+ if (sawPadding || !(code >= 65 && code <= 90 || code >= 97 && code <= 122 || code >= 48 && code <= 57 || code === 43 || code === 47)) return;
1037
+ cleaned += value[index];
1038
+ }
1039
+ return cleaned && cleaned.length % 4 === 0 ? cleaned : void 0;
1040
+ }
1041
+ function decodeInlineDataImage(src, maxBytes, totalBytes) {
1042
+ const match = /^data:(image\/[a-z0-9.+-]+)?(;base64)?,(.*)$/i.exec(src);
1043
+ if (!match) return null;
1044
+ const contentType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeLowercaseStringOrEmpty)(match[1] ?? "");
1045
+ if (!Boolean(match[2])) return null;
1046
+ const canonicalPayload = canonicalizeInlineBase64Payload(match[3] ?? "");
1047
+ if (!canonicalPayload) return null;
1048
+ const estimatedBytes = Buffer.byteLength(canonicalPayload, "base64");
1049
+ if (estimatedBytes <= 0 || typeof maxBytes === "number" && (estimatedBytes > maxBytes || totalBytes + estimatedBytes > maxBytes)) return null;
1050
+ try {
1051
+ return {
1052
+ data: Buffer.from(canonicalPayload, "base64"),
1053
+ contentType,
1054
+ estimatedBytes
1055
+ };
1056
+ } catch {
1057
+ return null;
1058
+ }
1059
+ }
998
1060
  async function resolveInlineDataImageMime(inline) {
999
1061
  const detectedMime = await require_runtime.getMSTeamsRuntime().media.detectMime({
1000
1062
  buffer: inline.data,
@@ -1070,15 +1132,12 @@ async function downloadMSTeamsAttachments(params) {
1070
1132
  sourceId: attachment.id?.trim() || void 0
1071
1133
  };
1072
1134
  });
1073
- candidates.push(...require_resolve_allowlist.extractInlineImageCandidates(list, {
1074
- maxInlineBytes: params.maxBytes,
1075
- maxInlineTotalBytes: params.maxBytes
1076
- }).map((candidate) => {
1135
+ const maxInlineBytes = params.maxBytes;
1136
+ candidates.push(...require_resolve_allowlist.extractInlineImageReferences(list).map((candidate) => {
1077
1137
  if (candidate.kind === "data") return {
1078
1138
  kind: "data",
1079
1139
  mediaKind: "image",
1080
- data: candidate.data,
1081
- contentType: candidate.contentType,
1140
+ src: candidate.src,
1082
1141
  sourceId: candidate.sourceId
1083
1142
  };
1084
1143
  if (candidate.kind === "url") return {
@@ -1095,30 +1154,33 @@ async function downloadMSTeamsAttachments(params) {
1095
1154
  sourceId: candidate.sourceId
1096
1155
  };
1097
1156
  }));
1098
- const advertisedMedia = resolveMSTeamsAdvertisedMedia(list, {
1099
- maxInlineBytes: params.maxBytes,
1100
- maxInlineTotalBytes: params.maxBytes
1101
- });
1102
- for (const advertised of advertisedMedia.slice(candidates.length)) candidates.push({
1157
+ for (const sourceId of resolveUnrepresentedHtmlAttachmentIds(list)) candidates.push({
1103
1158
  kind: "unavailable",
1104
- mediaKind: advertised.kind,
1105
- sourceId: advertised.sourceId
1159
+ mediaKind: "document",
1160
+ sourceId
1106
1161
  });
1107
1162
  if (candidates.length === 0) return [];
1108
1163
  const out = [];
1164
+ let totalInlineBytes = 0;
1109
1165
  for (const candidate of candidates) {
1110
1166
  if (candidate.kind === "unavailable") {
1111
1167
  out.push(withSourceId({ kind: candidate.mediaKind }, candidate.sourceId));
1112
1168
  continue;
1113
1169
  }
1114
1170
  if (candidate.kind === "data") {
1171
+ const decoded = decodeInlineDataImage(candidate.src, maxInlineBytes, totalInlineBytes);
1172
+ if (!decoded) {
1173
+ out.push(withSourceId({ kind: candidate.mediaKind }, candidate.sourceId));
1174
+ continue;
1175
+ }
1176
+ totalInlineBytes += decoded.estimatedBytes;
1115
1177
  try {
1116
- const contentType = await resolveInlineDataImageMime(candidate);
1178
+ const contentType = await resolveInlineDataImageMime(decoded);
1117
1179
  if (!contentType) {
1118
1180
  out.push(withSourceId({ kind: candidate.mediaKind }, candidate.sourceId));
1119
1181
  continue;
1120
1182
  }
1121
- const saved = await require_runtime.getMSTeamsRuntime().channel.media.saveMediaBuffer(candidate.data, contentType, "inbound", params.maxBytes);
1183
+ const saved = await require_runtime.getMSTeamsRuntime().channel.media.saveMediaBuffer(decoded.data, contentType, "inbound", params.maxBytes);
1122
1184
  out.push(withSourceId({
1123
1185
  path: saved.path,
1124
1186
  contentType: saved.contentType,
@@ -1834,6 +1896,7 @@ function createTeamsReplyStreamController(params) {
1834
1896
  return payloads;
1835
1897
  };
1836
1898
  const progressDraft = (0, openclaw_plugin_sdk_channel_outbound.createChannelProgressDraftCompositor)({
1899
+ formatPlainText: (text) => text,
1837
1900
  entry: params.msteamsConfig,
1838
1901
  mode: streamMode,
1839
1902
  active: Boolean(stream) && streamMode === "progress",
@@ -2460,7 +2523,10 @@ function createMSTeamsReplyDispatcher(params) {
2460
2523
  },
2461
2524
  onPlanUpdate: async (payload) => {
2462
2525
  if (payload?.phase !== "update") return false;
2463
- await streamController.pushPlanProgress((0, openclaw_plugin_sdk_channel_outbound.normalizeAgentPlanSteps)(payload.steps), { explanation: typeof payload.explanation === "string" ? payload.explanation : void 0 });
2526
+ await streamController.pushPlanProgress((0, openclaw_plugin_sdk_channel_outbound.normalizeAgentPlanSteps)(payload.steps), {
2527
+ explanation: typeof payload.explanation === "string" ? payload.explanation : void 0,
2528
+ explanationFormat: payload.explanationFormat === "plain" ? "plain" : void 0
2529
+ });
2464
2530
  return false;
2465
2531
  },
2466
2532
  onApprovalEvent: async (payload) => {
@@ -2843,8 +2909,7 @@ function buildStoredConversationReference(params) {
2843
2909
  ...threadId ? { threadId } : {}
2844
2910
  };
2845
2911
  }
2846
- function assembleMSTeamsInboundFacts(params) {
2847
- const { entry, mediaMaxBytes } = params;
2912
+ function assembleMSTeamsInboundFacts(entry) {
2848
2913
  const activity = entry.context.activity;
2849
2914
  const conversation = activity.conversation;
2850
2915
  const rawConversationId = conversation?.id ?? "";
@@ -2854,10 +2919,7 @@ function assembleMSTeamsInboundFacts(params) {
2854
2919
  const isChannel = conversationType === "channel";
2855
2920
  const teamId = activity.channelData?.team?.id;
2856
2921
  const threadId = isChannel ? conversationMessageId ?? activity.replyToId ?? void 0 : void 0;
2857
- const advertisedMedia = resolveMSTeamsAdvertisedMedia(entry.attachments, {
2858
- maxInlineBytes: mediaMaxBytes,
2859
- maxInlineTotalBytes: mediaMaxBytes
2860
- });
2922
+ const advertisedMedia = resolveMSTeamsAdvertisedMedia(entry.attachments);
2861
2923
  return {
2862
2924
  ...entry,
2863
2925
  activity,
@@ -3157,10 +3219,7 @@ function createMSTeamsMessageHandler(deps) {
3157
3219
  channel: "msteams"
3158
3220
  });
3159
3221
  const handleTeamsMessageNow = async (params) => {
3160
- const facts = assembleMSTeamsInboundFacts({
3161
- entry: params,
3162
- mediaMaxBytes
3163
- });
3222
+ const facts = assembleMSTeamsInboundFacts(params);
3164
3223
  const { context, activity, rawText, text, attachments, advertisedMedia, rawBody, quoteInfo, from, conversation, attachmentTypes, htmlSummary, conversationId, conversationMessageId, conversationType, isChannel, teamId, conversationRef } = facts;
3165
3224
  const historyBody = [text, (0, openclaw_plugin_sdk_channel_inbound.formatMediaPlaceholderText)(advertisedMedia)].filter(Boolean).join("\n");
3166
3225
  log.info("received message", {
@@ -3420,21 +3479,33 @@ function createMSTeamsReactionHandler(deps) {
3420
3479
  }
3421
3480
  const rawConversationId = activity.conversation?.id ?? "";
3422
3481
  const conversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(rawConversationId);
3423
- const conversationType = activity.conversation?.conversationType ?? "personal";
3424
- const isGroupChat = conversationType === "groupChat" || activity.conversation?.isGroup === true;
3425
- const isChannel = conversationType === "channel";
3426
- const isDirectMessage = !isGroupChat && !isChannel;
3482
+ const isChannel = activity.conversation?.conversationType === "channel";
3427
3483
  const senderId = from.aadObjectId ?? from.id;
3428
3484
  const senderName = from.name ?? from.id;
3485
+ const access = await resolveMSTeamsSenderAccess({
3486
+ cfg,
3487
+ activity
3488
+ });
3489
+ const { isDirectMessage, channelGate } = access;
3490
+ if (access.hasConflictingConversationScope) {
3491
+ log.info("dropping reaction (conflicting conversation scope)", { conversationId });
3492
+ return;
3493
+ }
3429
3494
  if (msteamsCfg) {
3430
- const senderAccess = await resolveMSTeamsSenderAccess({
3431
- cfg,
3432
- activity
3433
- });
3434
- if (senderAccess.senderAccess.decision !== "allow") {
3495
+ if (access.senderAccess.decision !== "allow") {
3435
3496
  log.debug?.("dropping reaction (access denied)", {
3436
3497
  sender: senderId,
3437
- reason: senderAccess.senderAccess.reasonCode
3498
+ reason: access.senderAccess.reasonCode
3499
+ });
3500
+ return;
3501
+ }
3502
+ if (!isDirectMessage && channelGate.allowlistConfigured && !channelGate.allowed) {
3503
+ log.info("dropping reaction (not in team/channel allowlist)", {
3504
+ conversationId,
3505
+ teamKey: channelGate.teamKey ?? "none",
3506
+ channelKey: channelGate.channelKey ?? "none",
3507
+ channelMatchKey: channelGate.channelMatchKey ?? "none",
3508
+ channelMatchSource: channelGate.channelMatchSource ?? "none"
3438
3509
  });
3439
3510
  return;
3440
3511
  }
@@ -3523,8 +3594,15 @@ async function isInvokeAuthorized(params) {
3523
3594
  activity: context.activity
3524
3595
  });
3525
3596
  const { msteamsCfg, isDirectMessage, conversationId, senderId } = resolved;
3526
- if (!msteamsCfg) return true;
3527
3597
  const maybeInvokeName = includeInvokeName ? { name: context.activity.name } : void 0;
3598
+ if (resolved.hasConflictingConversationScope) {
3599
+ deps.log.info("dropping invoke (conflicting conversation scope)", {
3600
+ conversationId,
3601
+ ...maybeInvokeName
3602
+ });
3603
+ return false;
3604
+ }
3605
+ if (!msteamsCfg) return true;
3528
3606
  if (isDirectMessage && resolved.senderAccess.decision !== "allow") {
3529
3607
  deps.log.debug?.(deniedLogs.dm, {
3530
3608
  sender: senderId,
@@ -1,5 +1,5 @@
1
1
  const require_runtime = require("./runtime-gZTAuFux.cjs");
2
- const require_polls = require("./polls-BBdZPx_R.cjs");
2
+ const require_polls = require("./polls-BJ4-4uZm.cjs");
3
3
  let node_crypto = require("node:crypto");
4
4
  //#region extensions/msteams/src/sso-token-store.ts
5
5
  /**
@@ -1,4 +1,4 @@
1
- const require_resolve_allowlist = require("./resolve-allowlist-DmUTQxEz.cjs");
1
+ const require_resolve_allowlist = require("./resolve-allowlist-CHzaaWiC.cjs");
2
2
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
3
3
  let openclaw_plugin_sdk_approval_auth_runtime = require("openclaw/plugin-sdk/approval-auth-runtime");
4
4
  let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
package/dist/api.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_channel = require("./channel-B9LIyYeu.cjs");
3
- const require_channel_setup = require("./channel.setup-BhxokviI.cjs");
2
+ const require_channel = require("./.setup/channel-Dzpbbht8.cjs");
3
+ const require_channel_setup = require("./.setup/channel.setup-BVDfgdCc.cjs");
4
4
  exports.createMSTeamsSetupWizardBase = require_channel_setup.createMSTeamsSetupWizardBase;
5
5
  exports.msteamsPlugin = require_channel.msteamsPlugin;
6
6
  exports.msteamsSetupAdapter = require_channel_setup.msteamsSetupAdapter;
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_config_schema = require("./config-schema-D22Lr1Qk.cjs");
2
+ const require_config_schema = require("./.setup/config-schema-D22Lr1Qk.cjs");
3
3
  exports.MSTeamsChannelConfigSchema = require_config_schema.MSTeamsChannelConfigSchema;
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_channel = require("./channel-B9LIyYeu.cjs");
2
+ const require_channel = require("./.setup/channel-Dzpbbht8.cjs");
3
3
  exports.msteamsPlugin = require_channel.msteamsPlugin;
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- require("./secret-input-C65t6kjM.cjs");
2
+ require("./.setup/secret-input-C65t6kjM.cjs");
3
3
  let openclaw_plugin_sdk_secret_input = require("openclaw/plugin-sdk/secret-input");
4
4
  //#region extensions/msteams/configured-state.ts
5
5
  /** Mirror Teams auth-mode requirements without loading the Azure SDK or full channel. */
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_resolve_allowlist = require("./resolve-allowlist-DmUTQxEz.cjs");
2
+ const require_resolve_allowlist = require("./.setup/resolve-allowlist-CHzaaWiC.cjs");
3
3
  let openclaw_plugin_sdk_directory_runtime = require("openclaw/plugin-sdk/directory-runtime");
4
4
  const msteamsDirectoryContractPlugin = {
5
5
  id: "msteams",