@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.
- package/assets/activity.svg +40 -0
- package/assets/icon.png +0 -0
- package/dist/{approval-handler.runtime-DRhFNeJS.cjs → .setup/approval-handler.runtime-DDxGPJDz.cjs} +3 -3
- package/dist/{channel-B9LIyYeu.cjs → .setup/channel-Dzpbbht8.cjs} +62 -35
- package/dist/{channel.runtime-BergqMHg.cjs → .setup/channel.runtime-TE49QvFv.cjs} +57 -32
- package/dist/{channel.setup-BhxokviI.cjs → .setup/channel.setup-BVDfgdCc.cjs} +3 -3
- package/dist/{delegated-state-DiEz9JIa.cjs → .setup/delegated-state-u-ywZyZT.cjs} +5 -5
- package/dist/{messenger-TLdZf9Ar.cjs → .setup/messenger-BR1l4zw-.cjs} +108 -178
- package/dist/{oauth-DUtLTz2u.cjs → .setup/oauth-C6m7dvHj.cjs} +1 -1
- package/dist/{polls-BBdZPx_R.cjs → .setup/polls-BJ4-4uZm.cjs} +14 -7
- package/dist/{reaction-types-BbBBdnQ5.cjs → .setup/reaction-types-C7RA7Wxt.cjs} +1 -1
- package/dist/{resolve-allowlist-DmUTQxEz.cjs → .setup/resolve-allowlist-CHzaaWiC.cjs} +135 -93
- package/dist/.setup/secret-contract-api-ChQdBeU1.cjs +4 -0
- package/dist/.setup/secret-contract-api-ipiUsANg.cjs +28 -0
- package/dist/{send-CU61PSDN.cjs → .setup/send-D8ATH4QI.cjs} +95 -33
- package/dist/.setup/setup-plugin-api-DpiaA8z1.cjs +3 -0
- package/dist/{src-D7Do7sM5.cjs → .setup/src-CpKfpP1m.cjs} +132 -54
- package/dist/{sso-token-store-DTeYxubp.cjs → .setup/sso-token-store-YYLHXgFH.cjs} +1 -1
- package/dist/{thread-session-NmA7_njI.cjs → .setup/thread-session-CXgHK-yi.cjs} +1 -1
- package/dist/api.cjs +2 -2
- package/dist/channel-config-api.cjs +1 -1
- package/dist/channel-plugin-api.cjs +1 -1
- package/dist/configured-state.cjs +1 -1
- package/dist/directory-contract-api.cjs +1 -1
- package/dist/doctor-contract-api.cjs +64 -62
- package/dist/qa-runner-api.cjs +1 -1
- package/dist/runtime-api.cjs +1 -1
- package/dist/secret-contract-api.cjs +4 -13
- package/dist/setup-entry.cjs +5 -3
- package/dist/setup-plugin-api.cjs +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +14 -11
- package/dist/{adapter.runtime-BzR3RK2J.cjs → .setup/adapter.runtime-ChuVK6k_.cjs} +2 -2
- /package/dist/{approval-card-CPNH_q3P.cjs → .setup/approval-card-CPNH_q3P.cjs} +0 -0
- /package/dist/{config-schema-D22Lr1Qk.cjs → .setup/config-schema-D22Lr1Qk.cjs} +0 -0
- /package/dist/{rolldown-runtime-VH7oDXx4.cjs → .setup/rolldown-runtime-VH7oDXx4.cjs} +0 -0
- /package/dist/{runtime-gZTAuFux.cjs → .setup/runtime-gZTAuFux.cjs} +0 -0
- /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("
|
|
3
|
-
const require_resolve_allowlist = require("./resolve-allowlist-
|
|
4
|
-
const require_channel = require("./channel-
|
|
5
|
-
const require_channel_setup = require("./channel.setup-
|
|
6
|
-
const require_polls = require("./polls-
|
|
7
|
-
const require_messenger = require("./messenger-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
@@ -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("
|
|
4
|
-
const require_resolve_allowlist = require("./resolve-allowlist-
|
|
5
|
-
const require_channel = require("./channel-
|
|
6
|
-
const require_thread_session = require("./thread-session-
|
|
7
|
-
const require_channel_setup = require("./channel.setup-
|
|
8
|
-
const require_polls = require("./polls-
|
|
9
|
-
const require_sso_token_store = require("./sso-token-store-
|
|
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-
|
|
12
|
-
const require_reaction_types = require("./reaction-types-
|
|
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.
|
|
57
|
-
|
|
63
|
+
if (!store.observe || !store.compareAndApply) throw new Error("plugin state atomic comparison is unavailable");
|
|
64
|
+
const prepared = {
|
|
58
65
|
sessionKey: params.sessionKey,
|
|
59
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
1074
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
1105
|
-
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(
|
|
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(
|
|
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), {
|
|
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(
|
|
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
|
|
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
|
-
|
|
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:
|
|
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-
|
|
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-
|
|
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("
|
|
3
|
-
const require_channel_setup = require("
|
|
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("
|
|
2
|
+
const require_config_schema = require("./.setup/config-schema-D22Lr1Qk.cjs");
|
|
3
3
|
exports.MSTeamsChannelConfigSchema = require_config_schema.MSTeamsChannelConfigSchema;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
require("
|
|
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("
|
|
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",
|