@openclaw/msteams 2026.9.5 → 2026.9.7
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/icon.png +0 -0
- package/dist/.setup/{adapter.runtime-ChuVK6k_.cjs → adapter.runtime-BiWeMKXE.cjs} +24 -30
- package/dist/.setup/{approval-card-CPNH_q3P.cjs → approval-card-DWPDItvq.cjs} +6 -3
- package/dist/.setup/{approval-handler.runtime-DDxGPJDz.cjs → approval-handler.runtime-D1s7UR3U.cjs} +4 -4
- package/dist/.setup/bot-framework-service-url-BUhTe8F3.cjs +55 -0
- package/dist/.setup/{channel-Dzpbbht8.cjs → channel-ByzS7QJ0.cjs} +338 -220
- package/dist/.setup/{channel.runtime-TE49QvFv.cjs → channel.runtime-Baz8XDFs.cjs} +47 -144
- package/dist/.setup/{channel.setup-BVDfgdCc.cjs → channel.setup-DL8lcWT_.cjs} +6 -6
- package/dist/.setup/{config-schema-D22Lr1Qk.cjs → config-schema-0KoBzb0t.cjs} +24 -38
- package/dist/.setup/{messenger-BR1l4zw-.cjs → conversation-store-state-B7JuyZTm.cjs} +697 -297
- package/dist/.setup/{src-CpKfpP1m.cjs → monitor-BJ7IyH4y.cjs} +440 -663
- package/dist/.setup/{oauth-C6m7dvHj.cjs → oauth-DQjOnJ4l.cjs} +1 -1
- package/dist/.setup/{reaction-types-C7RA7Wxt.cjs → reaction-types-CQ4oSUe7.cjs} +2 -6
- package/dist/.setup/{resolve-allowlist-CHzaaWiC.cjs → resolve-allowlist-Pl_NQFYH.cjs} +55 -312
- package/dist/.setup/{rolldown-runtime-VH7oDXx4.cjs → rolldown-runtime-BloeesfJ.cjs} +22 -0
- package/dist/.setup/{send-D8ATH4QI.cjs → send-VUM7Sryb.cjs} +47 -97
- package/dist/.setup/{setup-plugin-api-DpiaA8z1.cjs → setup-plugin-api-Dyf8kF-l.cjs} +1 -1
- package/dist/.setup/{thread-session-CXgHK-yi.cjs → thread-session--rw3BTal.cjs} +2 -5
- 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 +0 -1
- package/dist/directory-contract-api.cjs +1 -1
- package/dist/doctor-contract-api.cjs +201 -371
- package/dist/qa-runner-api.cjs +1 -1
- package/dist/runtime-api.cjs +0 -6
- package/dist/setup-entry.cjs +1 -1
- package/dist/setup-plugin-api.cjs +1 -1
- package/openclaw.plugin.json +46 -5
- package/package.json +10 -10
- package/dist/.setup/polls-BJ4-4uZm.cjs +0 -668
- package/dist/.setup/secret-input-C65t6kjM.cjs +0 -1
- package/dist/.setup/sso-token-store-YYLHXgFH.cjs +0 -111
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
const require_rolldown_runtime = require("./rolldown-runtime-
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-BloeesfJ.cjs");
|
|
2
2
|
const require_runtime = require("./runtime-gZTAuFux.cjs");
|
|
3
3
|
require("../runtime-api.cjs");
|
|
4
|
-
const require_resolve_allowlist = require("./resolve-allowlist-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const require_reaction_types = require("./reaction-types-C7RA7Wxt.cjs");
|
|
4
|
+
const require_resolve_allowlist = require("./resolve-allowlist-Pl_NQFYH.cjs");
|
|
5
|
+
const require_bot_framework_service_url = require("./bot-framework-service-url-BUhTe8F3.cjs");
|
|
6
|
+
const require_channel = require("./channel-ByzS7QJ0.cjs");
|
|
7
|
+
const require_thread_session = require("./thread-session--rw3BTal.cjs");
|
|
8
|
+
const require_channel_setup = require("./channel.setup-DL8lcWT_.cjs");
|
|
9
|
+
const require_approval_card = require("./approval-card-DWPDItvq.cjs");
|
|
10
|
+
const require_conversation_store_state = require("./conversation-store-state-B7JuyZTm.cjs");
|
|
11
|
+
const require_reaction_types = require("./reaction-types-CQ4oSUe7.cjs");
|
|
13
12
|
let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
|
|
14
13
|
let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
|
|
15
14
|
let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
|
|
@@ -17,13 +16,16 @@ let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-ht
|
|
|
17
16
|
let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
|
|
18
17
|
let openclaw_plugin_sdk_media_runtime = require("openclaw/plugin-sdk/media-runtime");
|
|
19
18
|
let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
|
|
19
|
+
let openclaw_plugin_sdk_ssrf_policy = require("openclaw/plugin-sdk/ssrf-policy");
|
|
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
22
|
let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
|
|
23
23
|
let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
|
|
24
|
+
let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
|
|
24
25
|
let node_crypto = require("node:crypto");
|
|
25
26
|
node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
|
|
26
|
-
let
|
|
27
|
+
let openclaw_plugin_sdk_security_runtime = require("openclaw/plugin-sdk/security-runtime");
|
|
28
|
+
let openclaw_plugin_sdk_webhook_targets = require("openclaw/plugin-sdk/webhook-targets");
|
|
27
29
|
let openclaw_plugin_sdk_approval_gateway_runtime = require("openclaw/plugin-sdk/approval-gateway-runtime");
|
|
28
30
|
let openclaw_plugin_sdk_channel_ingress_runtime = require("openclaw/plugin-sdk/channel-ingress-runtime");
|
|
29
31
|
let openclaw_plugin_sdk_context_visibility_runtime = require("openclaw/plugin-sdk/context-visibility-runtime");
|
|
@@ -32,10 +34,9 @@ let openclaw_plugin_sdk_runtime_config_snapshot = require("openclaw/plugin-sdk/r
|
|
|
32
34
|
let openclaw_plugin_sdk_agent_runtime = require("openclaw/plugin-sdk/agent-runtime");
|
|
33
35
|
let openclaw_plugin_sdk_extension_shared = require("openclaw/plugin-sdk/extension-shared");
|
|
34
36
|
let openclaw_plugin_sdk_plugin_runtime = require("openclaw/plugin-sdk/plugin-runtime");
|
|
35
|
-
let openclaw_plugin_sdk_dedupe_runtime = require("openclaw/plugin-sdk/dedupe-runtime");
|
|
36
|
-
let openclaw_plugin_sdk_plugin_state_runtime = require("openclaw/plugin-sdk/plugin-state-runtime");
|
|
37
37
|
let openclaw_plugin_sdk_collection_runtime = require("openclaw/plugin-sdk/collection-runtime");
|
|
38
38
|
let openclaw_plugin_sdk_gateway_runtime = require("openclaw/plugin-sdk/gateway-runtime");
|
|
39
|
+
let openclaw_plugin_sdk_time_runtime = require("openclaw/plugin-sdk/time-runtime");
|
|
39
40
|
let openclaw_plugin_sdk_channel_pairing = require("openclaw/plugin-sdk/channel-pairing");
|
|
40
41
|
let openclaw_plugin_sdk_runtime_group_policy = require("openclaw/plugin-sdk/runtime-group-policy");
|
|
41
42
|
let openclaw_plugin_sdk_dangerous_name_runtime = require("openclaw/plugin-sdk/dangerous-name-runtime");
|
|
@@ -150,7 +151,7 @@ async function runFeedbackReflection(params) {
|
|
|
150
151
|
return;
|
|
151
152
|
}
|
|
152
153
|
try {
|
|
153
|
-
await
|
|
154
|
+
await require_conversation_store_state.sendMSTeamsActivityWithReference(params.app, require_conversation_store_state.buildConversationReference(params.conversationRef), {
|
|
154
155
|
type: "message",
|
|
155
156
|
text: reflection.userMessage
|
|
156
157
|
}, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(cfg.channels?.msteams) });
|
|
@@ -248,12 +249,8 @@ async function maybeHandleMSTeamsApprovalCardSubmit(params) {
|
|
|
248
249
|
senderId
|
|
249
250
|
});
|
|
250
251
|
await context.updateActivity({
|
|
251
|
-
|
|
252
|
-
id: consumed.activityId
|
|
253
|
-
attachments: [{
|
|
254
|
-
contentType: "application/vnd.microsoft.card.adaptive",
|
|
255
|
-
content: require_approval_card.buildMSTeamsCanonicalApprovalTerminalCard(result)
|
|
256
|
-
}]
|
|
252
|
+
...require_conversation_store_state.buildMSTeamsAdaptiveCardActivity(require_approval_card.buildMSTeamsCanonicalApprovalTerminalCard(result)),
|
|
253
|
+
id: consumed.activityId
|
|
257
254
|
});
|
|
258
255
|
return result;
|
|
259
256
|
});
|
|
@@ -350,7 +347,7 @@ async function resolveMSTeamsSenderAccess(params) {
|
|
|
350
347
|
channelName: activity.channelData?.channel?.name,
|
|
351
348
|
allowNameMatching
|
|
352
349
|
});
|
|
353
|
-
const resolved = await
|
|
350
|
+
const resolved = await core.channel.inbound.ingress.resolveStable({
|
|
354
351
|
channelId: "msteams",
|
|
355
352
|
accountId: pairing.accountId,
|
|
356
353
|
identity: {
|
|
@@ -455,7 +452,7 @@ async function admitMSTeamsMessage(params) {
|
|
|
455
452
|
params.log.debug?.("dropping dm (dms disabled)");
|
|
456
453
|
return null;
|
|
457
454
|
}
|
|
458
|
-
const allowMatch =
|
|
455
|
+
const allowMatch = (0, openclaw_plugin_sdk_allow_from.resolveAllowlistMatchSimple)({
|
|
459
456
|
allowFrom: effectiveDmAllowFrom,
|
|
460
457
|
senderId,
|
|
461
458
|
senderName,
|
|
@@ -520,7 +517,7 @@ async function admitMSTeamsMessage(params) {
|
|
|
520
517
|
return null;
|
|
521
518
|
}
|
|
522
519
|
if (!senderAccess.allowed) {
|
|
523
|
-
const allowMatch =
|
|
520
|
+
const allowMatch = (0, openclaw_plugin_sdk_allow_from.resolveAllowlistMatchSimple)({
|
|
524
521
|
allowFrom: effectiveGroupAllowFrom,
|
|
525
522
|
senderId,
|
|
526
523
|
senderName,
|
|
@@ -562,11 +559,19 @@ async function admitMSTeamsMessage(params) {
|
|
|
562
559
|
};
|
|
563
560
|
}
|
|
564
561
|
//#endregion
|
|
562
|
+
//#region src/channels/mention-gating.ts
|
|
563
|
+
function resolveBotThreadMentionPolicy(params) {
|
|
564
|
+
if (!params.isBotOwnedThread || params.requireMentionInBotThreads === void 0) return {
|
|
565
|
+
requireMention: params.requireMention,
|
|
566
|
+
implicitMentionKinds: params.implicitMentionKinds
|
|
567
|
+
};
|
|
568
|
+
return {
|
|
569
|
+
requireMention: params.requireMentionInBotThreads,
|
|
570
|
+
implicitMentionKinds: params.requireMentionInBotThreads ? params.implicitMentionKinds?.filter((kind) => kind === "native") : params.implicitMentionKinds
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
//#endregion
|
|
565
574
|
//#region extensions/msteams/src/attachments/bot-framework.ts
|
|
566
|
-
/**
|
|
567
|
-
* Bot Framework Service token scope for requesting a token used against
|
|
568
|
-
* the Bot Connector (v3) REST endpoints such as `/v3/attachments/{id}`.
|
|
569
|
-
*/
|
|
570
575
|
const BOT_FRAMEWORK_SCOPE = "https://api.botframework.com";
|
|
571
576
|
/**
|
|
572
577
|
* Detect Bot Framework personal chat ("a:") and MSA orgid ("8:orgid:") conversation
|
|
@@ -580,45 +585,39 @@ function isBotFrameworkPersonalChatId(conversationId) {
|
|
|
580
585
|
const trimmed = conversationId.trim();
|
|
581
586
|
return trimmed.startsWith("a:") || trimmed.startsWith("8:orgid:");
|
|
582
587
|
}
|
|
583
|
-
function
|
|
584
|
-
return serviceUrl.replace(/\/+$/, "");
|
|
585
|
-
}
|
|
586
|
-
function buildBotFrameworkAttachmentHeaders(params) {
|
|
587
|
-
const headers = require_resolve_allowlist.ensureUserAgentHeader();
|
|
588
|
-
require_resolve_allowlist.applyAuthorizationHeaderForUrl({
|
|
589
|
-
headers,
|
|
590
|
-
url: params.url,
|
|
591
|
-
authAllowHosts: params.policy.authAllowHosts,
|
|
592
|
-
bearerToken: params.accessToken
|
|
593
|
-
});
|
|
594
|
-
return headers;
|
|
595
|
-
}
|
|
596
|
-
async function fetchBotFrameworkAttachmentInfo(params) {
|
|
597
|
-
const url = `${normalizeServiceUrl(params.serviceUrl)}/v3/attachments/${encodeURIComponent(params.attachmentId)}`;
|
|
588
|
+
async function fetchBotFrameworkAttachment(params, kind) {
|
|
598
589
|
let response;
|
|
599
590
|
try {
|
|
591
|
+
const headers = require_resolve_allowlist.ensureUserAgentHeader();
|
|
592
|
+
require_resolve_allowlist.applyAuthorizationHeaderForUrl({
|
|
593
|
+
headers,
|
|
594
|
+
url: params.url,
|
|
595
|
+
authAllowHosts: params.policy.authAllowHosts,
|
|
596
|
+
bearerToken: params.accessToken
|
|
597
|
+
});
|
|
600
598
|
response = await require_resolve_allowlist.safeFetchWithPolicy({
|
|
601
|
-
url,
|
|
599
|
+
url: params.url,
|
|
602
600
|
policy: params.policy,
|
|
603
601
|
fetchFn: params.fetchFn,
|
|
604
602
|
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
605
603
|
resolveFn: params.resolveFn,
|
|
606
|
-
requestInit: { headers
|
|
607
|
-
url,
|
|
608
|
-
accessToken: params.accessToken,
|
|
609
|
-
policy: params.policy
|
|
610
|
-
}) },
|
|
604
|
+
requestInit: { headers },
|
|
611
605
|
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
612
606
|
});
|
|
613
607
|
} catch (err) {
|
|
614
|
-
params.logger?.warn?.(
|
|
608
|
+
params.logger?.warn?.(`msteams botFramework ${kind} fetch failed`, { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
|
|
615
609
|
return;
|
|
616
610
|
}
|
|
617
611
|
if (!response.ok) {
|
|
618
612
|
await response.body?.cancel().catch(() => void 0);
|
|
619
|
-
params.logger?.warn?.(
|
|
613
|
+
params.logger?.warn?.(`msteams botFramework ${kind} non-ok`, { status: response.status });
|
|
620
614
|
return;
|
|
621
615
|
}
|
|
616
|
+
return response;
|
|
617
|
+
}
|
|
618
|
+
async function fetchBotFrameworkAttachmentInfo(params) {
|
|
619
|
+
const response = await fetchBotFrameworkAttachment(params, "attachmentInfo");
|
|
620
|
+
if (!response) return;
|
|
622
621
|
try {
|
|
623
622
|
return await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(response, "msteams botFramework attachmentInfo");
|
|
624
623
|
} catch (err) {
|
|
@@ -627,31 +626,8 @@ async function fetchBotFrameworkAttachmentInfo(params) {
|
|
|
627
626
|
}
|
|
628
627
|
}
|
|
629
628
|
async function saveBotFrameworkAttachmentView(params) {
|
|
630
|
-
const
|
|
631
|
-
|
|
632
|
-
try {
|
|
633
|
-
response = await require_resolve_allowlist.safeFetchWithPolicy({
|
|
634
|
-
url,
|
|
635
|
-
policy: params.policy,
|
|
636
|
-
fetchFn: params.fetchFn,
|
|
637
|
-
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
638
|
-
resolveFn: params.resolveFn,
|
|
639
|
-
requestInit: { headers: buildBotFrameworkAttachmentHeaders({
|
|
640
|
-
url,
|
|
641
|
-
accessToken: params.accessToken,
|
|
642
|
-
policy: params.policy
|
|
643
|
-
}) },
|
|
644
|
-
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
645
|
-
});
|
|
646
|
-
} catch (err) {
|
|
647
|
-
params.logger?.warn?.("msteams botFramework attachmentView fetch failed", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
if (!response.ok) {
|
|
651
|
-
await response.body?.cancel().catch(() => void 0);
|
|
652
|
-
params.logger?.warn?.("msteams botFramework attachmentView non-ok", { status: response.status });
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
629
|
+
const response = await fetchBotFrameworkAttachment(params, "attachmentView");
|
|
630
|
+
if (!response) return;
|
|
655
631
|
let contentLength;
|
|
656
632
|
try {
|
|
657
633
|
contentLength = (0, openclaw_plugin_sdk_media_runtime.parseMediaContentLength)(response.headers.get("content-length"));
|
|
@@ -666,7 +642,7 @@ async function saveBotFrameworkAttachmentView(params) {
|
|
|
666
642
|
}
|
|
667
643
|
try {
|
|
668
644
|
return await require_runtime.getMSTeamsRuntime().channel.media.saveResponseMedia(response, {
|
|
669
|
-
sourceUrl: url,
|
|
645
|
+
sourceUrl: params.url,
|
|
670
646
|
filePathHint: params.fileNameHint,
|
|
671
647
|
maxBytes: params.maxBytes,
|
|
672
648
|
fallbackContentType: params.contentTypeHint,
|
|
@@ -680,12 +656,6 @@ async function saveBotFrameworkAttachmentView(params) {
|
|
|
680
656
|
await response.body?.cancel().catch(() => void 0);
|
|
681
657
|
}
|
|
682
658
|
}
|
|
683
|
-
/**
|
|
684
|
-
* Download media for a single attachment via the Bot Framework v3 attachments
|
|
685
|
-
* endpoint. Used for personal DM conversations where the Graph `/chats/{id}`
|
|
686
|
-
* path is not usable because the Bot Framework conversation ID (`a:...`) is
|
|
687
|
-
* not a valid Graph chat identifier.
|
|
688
|
-
*/
|
|
689
659
|
async function downloadMSTeamsBotFrameworkAttachment(params) {
|
|
690
660
|
if (!params.serviceUrl || !params.attachmentId || !params.tokenProvider) return;
|
|
691
661
|
const tokenProvider = params.tokenProvider;
|
|
@@ -693,8 +663,8 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
|
|
|
693
663
|
allowHosts: params.allowHosts,
|
|
694
664
|
authAllowHosts: params.authAllowHosts
|
|
695
665
|
});
|
|
696
|
-
const baseUrl = `${
|
|
697
|
-
if (!
|
|
666
|
+
const baseUrl = `${params.serviceUrl.replace(/\/+$/, "")}/v3/attachments/${encodeURIComponent(params.attachmentId)}`;
|
|
667
|
+
if (!(0, openclaw_plugin_sdk_ssrf_policy.isHttpsUrlAllowedByHostnameSuffixAllowlist)(baseUrl, policy.allowHosts)) return;
|
|
698
668
|
let accessToken;
|
|
699
669
|
try {
|
|
700
670
|
accessToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
@@ -707,9 +677,8 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
|
|
|
707
677
|
return;
|
|
708
678
|
}
|
|
709
679
|
if (!accessToken) return;
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
attachmentId: params.attachmentId,
|
|
680
|
+
const request = {
|
|
681
|
+
url: baseUrl,
|
|
713
682
|
accessToken,
|
|
714
683
|
policy,
|
|
715
684
|
fetchFn: params.fetchFn,
|
|
@@ -717,7 +686,8 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
|
|
|
717
686
|
resolveFn: params.resolveFn,
|
|
718
687
|
logger: params.logger,
|
|
719
688
|
deadline: params.deadline
|
|
720
|
-
}
|
|
689
|
+
};
|
|
690
|
+
const info = await fetchBotFrameworkAttachmentInfo(request);
|
|
721
691
|
if (!info) return;
|
|
722
692
|
const views = Array.isArray(info.views) ? info.views : [];
|
|
723
693
|
const candidateView = views.find((view) => view?.viewId === "original") ?? views.find((view) => typeof view?.viewId === "string");
|
|
@@ -727,20 +697,12 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
|
|
|
727
697
|
const fileNameHint = typeof params.fileNameHint === "string" && params.fileNameHint || typeof info.name === "string" && info.name || void 0;
|
|
728
698
|
const contentTypeHint = typeof params.contentTypeHint === "string" && params.contentTypeHint || typeof info.type === "string" && info.type || void 0;
|
|
729
699
|
const saved = await saveBotFrameworkAttachmentView({
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
viewId,
|
|
733
|
-
accessToken,
|
|
700
|
+
...request,
|
|
701
|
+
url: `${baseUrl}/views/${encodeURIComponent(viewId)}`,
|
|
734
702
|
maxBytes: params.maxBytes,
|
|
735
703
|
fileNameHint,
|
|
736
704
|
contentTypeHint,
|
|
737
|
-
preserveFilenames: params.preserveFilenames
|
|
738
|
-
policy,
|
|
739
|
-
fetchFn: params.fetchFn,
|
|
740
|
-
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
741
|
-
resolveFn: params.resolveFn,
|
|
742
|
-
logger: params.logger,
|
|
743
|
-
deadline: params.deadline
|
|
705
|
+
preserveFilenames: params.preserveFilenames
|
|
744
706
|
});
|
|
745
707
|
if (!saved) return;
|
|
746
708
|
return {
|
|
@@ -759,15 +721,7 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
|
|
|
759
721
|
* reuse the existing logging path.
|
|
760
722
|
*/
|
|
761
723
|
async function downloadMSTeamsBotFrameworkAttachments(params) {
|
|
762
|
-
const
|
|
763
|
-
const unique = [];
|
|
764
|
-
for (const id of params.attachmentIds ?? []) {
|
|
765
|
-
if (typeof id !== "string") continue;
|
|
766
|
-
const trimmed = id.trim();
|
|
767
|
-
if (!trimmed || seen.has(trimmed)) continue;
|
|
768
|
-
seen.add(trimmed);
|
|
769
|
-
unique.push(trimmed);
|
|
770
|
-
}
|
|
724
|
+
const unique = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeUniqueTrimmedStringList)(params.attachmentIds);
|
|
771
725
|
if (unique.length === 0 || !params.serviceUrl || !params.tokenProvider) return {
|
|
772
726
|
media: [],
|
|
773
727
|
attachmentCount: unique.length
|
|
@@ -775,20 +729,8 @@ async function downloadMSTeamsBotFrameworkAttachments(params) {
|
|
|
775
729
|
const media = [];
|
|
776
730
|
for (const attachmentId of unique) try {
|
|
777
731
|
const item = await downloadMSTeamsBotFrameworkAttachment({
|
|
778
|
-
|
|
779
|
-
attachmentId
|
|
780
|
-
tokenProvider: params.tokenProvider,
|
|
781
|
-
maxBytes: params.maxBytes,
|
|
782
|
-
allowHosts: params.allowHosts,
|
|
783
|
-
authAllowHosts: params.authAllowHosts,
|
|
784
|
-
fetchFn: params.fetchFn,
|
|
785
|
-
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
786
|
-
resolveFn: params.resolveFn,
|
|
787
|
-
deadline: params.deadline,
|
|
788
|
-
fileNameHint: params.fileNameHint,
|
|
789
|
-
contentTypeHint: params.contentTypeHint,
|
|
790
|
-
preserveFilenames: params.preserveFilenames,
|
|
791
|
-
logger: params.logger
|
|
732
|
+
...params,
|
|
733
|
+
attachmentId
|
|
792
734
|
});
|
|
793
735
|
if (item) media.push({
|
|
794
736
|
...item,
|
|
@@ -828,11 +770,9 @@ function extractMSTeamsHtmlAttachmentIds(attachments) {
|
|
|
828
770
|
const html = require_resolve_allowlist.extractHtmlFromAttachment(att);
|
|
829
771
|
if (!html) continue;
|
|
830
772
|
require_resolve_allowlist.ATTACHMENT_TAG_RE.lastIndex = 0;
|
|
831
|
-
|
|
832
|
-
while (match) {
|
|
773
|
+
for (const match of html.matchAll(require_resolve_allowlist.ATTACHMENT_TAG_RE)) {
|
|
833
774
|
const id = match[1]?.trim();
|
|
834
775
|
if (id) ids.add(id);
|
|
835
|
-
match = require_resolve_allowlist.ATTACHMENT_TAG_RE.exec(html);
|
|
836
776
|
}
|
|
837
777
|
}
|
|
838
778
|
return Array.from(ids);
|
|
@@ -852,8 +792,7 @@ function summarizeMSTeamsHtmlAttachments(attachments) {
|
|
|
852
792
|
if (!html) continue;
|
|
853
793
|
htmlAttachments += 1;
|
|
854
794
|
require_resolve_allowlist.IMG_SRC_RE.lastIndex = 0;
|
|
855
|
-
|
|
856
|
-
while (match) {
|
|
795
|
+
for (const match of html.matchAll(require_resolve_allowlist.IMG_SRC_RE)) {
|
|
857
796
|
imgTags += 1;
|
|
858
797
|
const src = match[1]?.trim();
|
|
859
798
|
if (src) {
|
|
@@ -861,15 +800,12 @@ function summarizeMSTeamsHtmlAttachments(attachments) {
|
|
|
861
800
|
else if (src.startsWith("cid:")) cidImages += 1;
|
|
862
801
|
else srcHosts.add(require_resolve_allowlist.safeHostForUrl(src));
|
|
863
802
|
}
|
|
864
|
-
match = require_resolve_allowlist.IMG_SRC_RE.exec(html);
|
|
865
803
|
}
|
|
866
804
|
require_resolve_allowlist.ATTACHMENT_TAG_RE.lastIndex = 0;
|
|
867
|
-
|
|
868
|
-
while (attachmentMatch) {
|
|
805
|
+
for (const attachmentMatch of html.matchAll(require_resolve_allowlist.ATTACHMENT_TAG_RE)) {
|
|
869
806
|
attachmentTags += 1;
|
|
870
807
|
const id = attachmentMatch[1]?.trim();
|
|
871
808
|
if (id) attachmentIds.add(id);
|
|
872
|
-
attachmentMatch = require_resolve_allowlist.ATTACHMENT_TAG_RE.exec(html);
|
|
873
809
|
}
|
|
874
810
|
}
|
|
875
811
|
if (htmlAttachments === 0) return;
|
|
@@ -893,9 +829,10 @@ function resolveUnrepresentedHtmlAttachmentIds(attachments) {
|
|
|
893
829
|
return extractMSTeamsHtmlAttachmentIds(attachments).filter((id) => !representedIds.has(id));
|
|
894
830
|
}
|
|
895
831
|
function createAdvertisedMediaFact(kind, sourceId) {
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
832
|
+
return {
|
|
833
|
+
kind,
|
|
834
|
+
...sourceId ? { sourceId } : {}
|
|
835
|
+
};
|
|
899
836
|
}
|
|
900
837
|
function resolveMSTeamsAdvertisedMedia(attachments) {
|
|
901
838
|
const list = Array.isArray(attachments) ? attachments : [];
|
|
@@ -919,49 +856,28 @@ function resolveMSTeamsAdvertisedMedia(attachments) {
|
|
|
919
856
|
* implementation. This lets Teams-specific auth fallback own the request
|
|
920
857
|
* sequence while keeping redirect and DNS pinning inside `safeFetchWithPolicy`.
|
|
921
858
|
*/
|
|
922
|
-
async function
|
|
859
|
+
async function downloadAndStoreMSTeamsRemoteMedia(params) {
|
|
923
860
|
const response = await params.fetchImpl(params.url, { redirect: "follow" });
|
|
924
861
|
try {
|
|
925
|
-
|
|
862
|
+
const saved = await (0, openclaw_plugin_sdk_media_runtime.saveResponseMedia)(response, {
|
|
926
863
|
sourceUrl: params.url,
|
|
927
864
|
filePathHint: params.filePathHint,
|
|
928
865
|
maxBytes: params.maxBytes,
|
|
929
866
|
fallbackContentType: params.contentTypeHint,
|
|
930
|
-
originalFilename: params.
|
|
867
|
+
originalFilename: params.preserveFilenames ? params.filePathHint : void 0
|
|
931
868
|
});
|
|
869
|
+
return {
|
|
870
|
+
path: saved.path,
|
|
871
|
+
contentType: saved.contentType,
|
|
872
|
+
kind: params.kind ?? require_resolve_allowlist.resolveMSTeamsMediaKind({
|
|
873
|
+
contentType: saved.contentType,
|
|
874
|
+
fileName: params.filePathHint
|
|
875
|
+
})
|
|
876
|
+
};
|
|
932
877
|
} finally {
|
|
933
878
|
await response.body?.cancel().catch(() => void 0);
|
|
934
879
|
}
|
|
935
880
|
}
|
|
936
|
-
async function downloadAndStoreMSTeamsRemoteMedia(params) {
|
|
937
|
-
const originalFilename = params.preserveFilenames ? params.filePathHint : void 0;
|
|
938
|
-
let saved;
|
|
939
|
-
if (params.useDirectFetch && params.fetchImpl) saved = await saveRemoteMediaDirect({
|
|
940
|
-
url: params.url,
|
|
941
|
-
filePathHint: params.filePathHint,
|
|
942
|
-
fetchImpl: params.fetchImpl,
|
|
943
|
-
maxBytes: params.maxBytes,
|
|
944
|
-
contentTypeHint: params.contentTypeHint,
|
|
945
|
-
originalFilename
|
|
946
|
-
});
|
|
947
|
-
else saved = await require_runtime.getMSTeamsRuntime().channel.media.saveRemoteMedia({
|
|
948
|
-
url: params.url,
|
|
949
|
-
fetchImpl: params.fetchImpl,
|
|
950
|
-
filePathHint: params.filePathHint,
|
|
951
|
-
maxBytes: params.maxBytes,
|
|
952
|
-
ssrfPolicy: params.ssrfPolicy,
|
|
953
|
-
fallbackContentType: params.contentTypeHint,
|
|
954
|
-
originalFilename
|
|
955
|
-
});
|
|
956
|
-
return {
|
|
957
|
-
path: saved.path,
|
|
958
|
-
contentType: saved.contentType,
|
|
959
|
-
kind: params.kind ?? require_resolve_allowlist.resolveMSTeamsMediaKind({
|
|
960
|
-
contentType: saved.contentType,
|
|
961
|
-
fileName: params.filePathHint
|
|
962
|
-
})
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
881
|
//#endregion
|
|
966
882
|
//#region extensions/msteams/src/attachments/download.ts
|
|
967
883
|
function withSourceId(media, sourceId) {
|
|
@@ -1021,22 +937,8 @@ function scopeCandidatesForUrl(url) {
|
|
|
1021
937
|
}
|
|
1022
938
|
function canonicalizeInlineBase64Payload(value) {
|
|
1023
939
|
let cleaned = "";
|
|
1024
|
-
|
|
1025
|
-
|
|
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;
|
|
940
|
+
for (const char of value) if (char.charCodeAt(0) > 32) cleaned += char;
|
|
941
|
+
return cleaned.length % 4 === 0 && /^[A-Za-z0-9+/]+={0,2}$/.test(cleaned) ? cleaned : void 0;
|
|
1040
942
|
}
|
|
1041
943
|
function decodeInlineDataImage(src, maxBytes, totalBytes) {
|
|
1042
944
|
const match = /^data:(image\/[a-z0-9.+-]+)?(;base64)?,(.*)$/i.exec(src);
|
|
@@ -1079,7 +981,7 @@ async function fetchWithAuthFallback(params) {
|
|
|
1079
981
|
if (!params.tokenProvider) return firstAttempt;
|
|
1080
982
|
const tokenProvider = params.tokenProvider;
|
|
1081
983
|
if (firstAttempt.status !== 401 && firstAttempt.status !== 403) return firstAttempt;
|
|
1082
|
-
if (!
|
|
984
|
+
if (!(0, openclaw_plugin_sdk_ssrf_policy.isHttpsUrlAllowedByHostnameSuffixAllowlist)(params.url, params.policy.authAllowHosts)) return firstAttempt;
|
|
1083
985
|
let fallbackAttempt = firstAttempt;
|
|
1084
986
|
const scopes = scopeCandidatesForUrl(params.url);
|
|
1085
987
|
const fetchFn = params.fetchFn ?? fetch;
|
|
@@ -1109,10 +1011,6 @@ async function fetchWithAuthFallback(params) {
|
|
|
1109
1011
|
} catch {}
|
|
1110
1012
|
return fallbackAttempt;
|
|
1111
1013
|
}
|
|
1112
|
-
/**
|
|
1113
|
-
* Download all file attachments from a Teams message (images, documents, etc.).
|
|
1114
|
-
* Renamed from downloadMSTeamsImageAttachments to support all file types.
|
|
1115
|
-
*/
|
|
1116
1014
|
async function downloadMSTeamsAttachments(params) {
|
|
1117
1015
|
const list = Array.isArray(params.attachments) ? params.attachments : [];
|
|
1118
1016
|
if (list.length === 0) return [];
|
|
@@ -1121,7 +1019,6 @@ async function downloadMSTeamsAttachments(params) {
|
|
|
1121
1019
|
authAllowHosts: params.authAllowHosts
|
|
1122
1020
|
});
|
|
1123
1021
|
const allowHosts = policy.allowHosts;
|
|
1124
|
-
const ssrfPolicy = require_resolve_allowlist.resolveMediaSsrfPolicy(allowHosts);
|
|
1125
1022
|
const candidates = list.filter(require_resolve_allowlist.isAdvertisedFileAttachment).map((attachment) => {
|
|
1126
1023
|
return (require_resolve_allowlist.isDownloadableAttachment(attachment) ? resolveDownloadCandidate(attachment) : null) ?? {
|
|
1127
1024
|
kind: "unavailable",
|
|
@@ -1192,7 +1089,7 @@ async function downloadMSTeamsAttachments(params) {
|
|
|
1192
1089
|
}
|
|
1193
1090
|
continue;
|
|
1194
1091
|
}
|
|
1195
|
-
if (!
|
|
1092
|
+
if (!(0, openclaw_plugin_sdk_ssrf_policy.isHttpsUrlAllowedByHostnameSuffixAllowlist)(candidate.url, allowHosts)) {
|
|
1196
1093
|
out.push(withSourceId({ kind: candidate.mediaKind }, candidate.sourceId));
|
|
1197
1094
|
continue;
|
|
1198
1095
|
}
|
|
@@ -1204,8 +1101,6 @@ async function downloadMSTeamsAttachments(params) {
|
|
|
1204
1101
|
contentTypeHint: candidate.contentTypeHint,
|
|
1205
1102
|
kind: candidate.mediaKind,
|
|
1206
1103
|
preserveFilenames: params.preserveFilenames,
|
|
1207
|
-
ssrfPolicy,
|
|
1208
|
-
useDirectFetch: true,
|
|
1209
1104
|
fetchImpl: (input, init) => fetchWithAuthFallback({
|
|
1210
1105
|
url: require_resolve_allowlist.resolveRequestUrl(input),
|
|
1211
1106
|
tokenProvider: params.tokenProvider,
|
|
@@ -1308,10 +1203,6 @@ function normalizeGraphAttachment(att) {
|
|
|
1308
1203
|
content
|
|
1309
1204
|
};
|
|
1310
1205
|
}
|
|
1311
|
-
/**
|
|
1312
|
-
* Download all hosted content from a Teams message (images, documents, etc.).
|
|
1313
|
-
* Renamed from downloadGraphHostedImages to support all file types.
|
|
1314
|
-
*/
|
|
1315
1206
|
async function downloadGraphHostedContent(params) {
|
|
1316
1207
|
let hosted;
|
|
1317
1208
|
try {
|
|
@@ -1389,7 +1280,7 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1389
1280
|
allowHosts: params.allowHosts,
|
|
1390
1281
|
authAllowHosts: params.authAllowHosts
|
|
1391
1282
|
});
|
|
1392
|
-
const ssrfPolicy =
|
|
1283
|
+
const ssrfPolicy = (0, openclaw_plugin_sdk_ssrf_policy.buildHostnameAllowlistPolicyFromSuffixAllowlist)(policy.allowHosts);
|
|
1393
1284
|
const messageUrl = params.messageUrl;
|
|
1394
1285
|
let accessToken;
|
|
1395
1286
|
try {
|
|
@@ -1476,7 +1367,7 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1476
1367
|
downloadedReferenceUrls.add(shareUrl);
|
|
1477
1368
|
try {
|
|
1478
1369
|
const sharesUrl = `${require_resolve_allowlist.GRAPH_ROOT}/shares/${require_resolve_allowlist.encodeGraphShareId(shareUrl)}/driveItem/content`;
|
|
1479
|
-
if (!
|
|
1370
|
+
if (!(0, openclaw_plugin_sdk_ssrf_policy.isHttpsUrlAllowedByHostnameSuffixAllowlist)(sharesUrl, policy.allowHosts)) {
|
|
1480
1371
|
params.logger?.debug?.("graph media sharepoint url not in allowHosts", {
|
|
1481
1372
|
messageUrl,
|
|
1482
1373
|
sharesUrl
|
|
@@ -1490,8 +1381,6 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1490
1381
|
maxBytes: params.maxBytes,
|
|
1491
1382
|
contentTypeHint: "application/octet-stream",
|
|
1492
1383
|
preserveFilenames: params.preserveFilenames,
|
|
1493
|
-
ssrfPolicy,
|
|
1494
|
-
useDirectFetch: true,
|
|
1495
1384
|
fetchImpl: async (input, init) => {
|
|
1496
1385
|
const requestUrl = require_resolve_allowlist.resolveRequestUrl(input);
|
|
1497
1386
|
const headers = require_resolve_allowlist.ensureUserAgentHeader(init?.headers);
|
|
@@ -1532,7 +1421,6 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1532
1421
|
messageUrl,
|
|
1533
1422
|
maxBytes: params.maxBytes,
|
|
1534
1423
|
fetchFn: params.fetchFn,
|
|
1535
|
-
preserveFilenames: params.preserveFilenames,
|
|
1536
1424
|
ssrfPolicy,
|
|
1537
1425
|
logger: params.logger,
|
|
1538
1426
|
deadline: params.deadline
|
|
@@ -1875,7 +1763,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
1875
1763
|
} : payload;
|
|
1876
1764
|
};
|
|
1877
1765
|
const finalStreamActivity = (text) => ({
|
|
1878
|
-
...
|
|
1766
|
+
...require_conversation_store_state.buildMSTeamsMessageActivity(text === void 0 ? void 0 : require_conversation_store_state.formatMSTeamsMarkdown(text, params.tableMode ?? "code")),
|
|
1879
1767
|
channelData: params.feedbackLoopEnabled ? { feedbackLoopEnabled: true } : {}
|
|
1880
1768
|
});
|
|
1881
1769
|
const deferredReplacementLogicalContent = () => {
|
|
@@ -1895,7 +1783,21 @@ function createTeamsReplyStreamController(params) {
|
|
|
1895
1783
|
deferredReplacementEntries = [];
|
|
1896
1784
|
return payloads;
|
|
1897
1785
|
};
|
|
1786
|
+
const finalizeWithoutReceipt = (logicalContent, canceled = false) => {
|
|
1787
|
+
const pending = pendingFinalPayload;
|
|
1788
|
+
pendingFinalPayload = void 0;
|
|
1789
|
+
const fallback = pending && !canceled ? fallbackPayloadAfterAcknowledgedText(pending) : void 0;
|
|
1790
|
+
const postNativePayloads = replacementSettlementPending && !canceled ? takeDeferredReplacementPayloads(fallback) : [];
|
|
1791
|
+
if (canceled) deferredReplacementEntries = [];
|
|
1792
|
+
return {
|
|
1793
|
+
...acknowledgedNativeDelivery(),
|
|
1794
|
+
...!canceled && logicalContent ? { logicalContent } : {},
|
|
1795
|
+
...!replacementSettlementPending && fallback ? { fallbackPayload: fallback } : {},
|
|
1796
|
+
...postNativePayloads.length > 0 ? { postNativePayloads } : {}
|
|
1797
|
+
};
|
|
1798
|
+
};
|
|
1898
1799
|
const progressDraft = (0, openclaw_plugin_sdk_channel_outbound.createChannelProgressDraftCompositor)({
|
|
1800
|
+
preparedItems: true,
|
|
1899
1801
|
formatPlainText: (text) => text,
|
|
1900
1802
|
entry: params.msteamsConfig,
|
|
1901
1803
|
mode: streamMode,
|
|
@@ -1914,7 +1816,6 @@ function createTeamsReplyStreamController(params) {
|
|
|
1914
1816
|
}
|
|
1915
1817
|
});
|
|
1916
1818
|
return {
|
|
1917
|
-
async onReplyStart() {},
|
|
1918
1819
|
onPartialReply(payload) {
|
|
1919
1820
|
if (!stream || !payload.text || wasCanceled() || streamMode !== "partial") return;
|
|
1920
1821
|
if (replacementSettlementPending && replacementFinalPending) {
|
|
@@ -1954,36 +1855,18 @@ function createTeamsReplyStreamController(params) {
|
|
|
1954
1855
|
params.log?.warn?.(`msteams stream emit failed, falling back to block delivery: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
|
|
1955
1856
|
}
|
|
1956
1857
|
},
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
},
|
|
1964
|
-
async pushReasoningProgress(text, options) {
|
|
1965
|
-
await progressDraft.pushReasoningProgress(text, options);
|
|
1966
|
-
},
|
|
1967
|
-
resetReasoningProgress: () => progressDraft.resetReasoningProgress(),
|
|
1968
|
-
async pushApprovalEvent(payload) {
|
|
1969
|
-
await progressDraft.pushApprovalEvent(payload);
|
|
1970
|
-
},
|
|
1971
|
-
async pushPlanProgress(steps, options) {
|
|
1972
|
-
await progressDraft.pushPlanProgress(steps, options);
|
|
1973
|
-
},
|
|
1858
|
+
pushItemEvent: progressDraft.pushItemEvent.bind(progressDraft),
|
|
1859
|
+
pushToolEvent: progressDraft.pushToolEvent,
|
|
1860
|
+
pushReasoningProgress: progressDraft.pushReasoningProgress.bind(progressDraft),
|
|
1861
|
+
resetReasoningProgress: progressDraft.resetReasoningProgress,
|
|
1862
|
+
pushApprovalEvent: progressDraft.pushApprovalEvent.bind(progressDraft),
|
|
1863
|
+
pushPlanProgress: progressDraft.pushPlanProgress.bind(progressDraft),
|
|
1974
1864
|
preparePayload(payload) {
|
|
1975
1865
|
if (!stream) return payload;
|
|
1976
1866
|
if (wasCanceled()) return;
|
|
1977
1867
|
if (payload.text) progressDraft.markFinalReplyStarted();
|
|
1978
1868
|
if (replacementSettlementPending) {
|
|
1979
|
-
if (!replacementFinalPending) {
|
|
1980
|
-
deferredReplacementEntries.push({
|
|
1981
|
-
kind: "payload",
|
|
1982
|
-
payload
|
|
1983
|
-
});
|
|
1984
|
-
return;
|
|
1985
|
-
}
|
|
1986
|
-
if (!payload.text) {
|
|
1869
|
+
if (!replacementFinalPending || !payload.text) {
|
|
1987
1870
|
deferredReplacementEntries.push({
|
|
1988
1871
|
kind: "payload",
|
|
1989
1872
|
payload
|
|
@@ -2108,24 +1991,10 @@ function createTeamsReplyStreamController(params) {
|
|
|
2108
1991
|
}
|
|
2109
1992
|
const result = await stream.close();
|
|
2110
1993
|
streamFinalizationPending = false;
|
|
2111
|
-
if (!result)
|
|
2112
|
-
const fallback = pendingFinalPayload;
|
|
2113
|
-
pendingFinalPayload = void 0;
|
|
2114
|
-
const canceled = wasCanceled();
|
|
2115
|
-
const replacementFallback = replacementSettlementPending && fallback && !canceled ? fallbackPayloadAfterAcknowledgedText(fallback) : void 0;
|
|
2116
|
-
const fallbackPayload = !replacementSettlementPending && fallback && !canceled ? fallbackPayloadAfterAcknowledgedText(fallback) : void 0;
|
|
2117
|
-
const postNativePayloads = replacementSettlementPending && !canceled ? takeDeferredReplacementPayloads(replacementFallback) : [];
|
|
2118
|
-
if (canceled) deferredReplacementEntries = [];
|
|
2119
|
-
return {
|
|
2120
|
-
...acknowledgedNativeDelivery(),
|
|
2121
|
-
...!canceled && logicalContent ? { logicalContent } : {},
|
|
2122
|
-
...fallbackPayload ? { fallbackPayload } : {},
|
|
2123
|
-
...postNativePayloads.length > 0 ? { postNativePayloads } : {}
|
|
2124
|
-
};
|
|
2125
|
-
}
|
|
1994
|
+
if (!result) return finalizeWithoutReceipt(logicalContent, wasCanceled());
|
|
2126
1995
|
const replacementFallback = replacementSettlementPending && replacementEmitFailed && pendingFinalPayload ? fallbackPayloadAfterAcknowledgedText(pendingFinalPayload) : void 0;
|
|
2127
1996
|
pendingFinalPayload = void 0;
|
|
2128
|
-
const messageId =
|
|
1997
|
+
const messageId = require_conversation_store_state.extractMessageId(result) ?? acknowledgedStreamId;
|
|
2129
1998
|
const postNativePayloads = replacementSettlementPending ? takeDeferredReplacementPayloads(replacementFallback) : [];
|
|
2130
1999
|
const nativeContent = replacementEmitFailed ? acknowledgedText || void 0 : content;
|
|
2131
2000
|
return {
|
|
@@ -2146,17 +2015,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
2146
2015
|
streamFailed = true;
|
|
2147
2016
|
streamFinalizationPending = false;
|
|
2148
2017
|
params.log?.warn?.(`msteams stream finalize failed: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
|
|
2149
|
-
|
|
2150
|
-
pendingFinalPayload = void 0;
|
|
2151
|
-
const replacementFallback = replacementSettlementPending && fallback ? fallbackPayloadAfterAcknowledgedText(fallback) : void 0;
|
|
2152
|
-
const fallbackPayload = !replacementSettlementPending && fallback ? fallbackPayloadAfterAcknowledgedText(fallback) : void 0;
|
|
2153
|
-
const postNativePayloads = replacementSettlementPending ? takeDeferredReplacementPayloads(replacementFallback) : [];
|
|
2154
|
-
return {
|
|
2155
|
-
...acknowledgedNativeDelivery(),
|
|
2156
|
-
...logicalContent ? { logicalContent } : {},
|
|
2157
|
-
...fallbackPayload ? { fallbackPayload } : {},
|
|
2158
|
-
...postNativePayloads.length > 0 ? { postNativePayloads } : {}
|
|
2159
|
-
};
|
|
2018
|
+
return finalizeWithoutReceipt(logicalContent);
|
|
2160
2019
|
} finally {
|
|
2161
2020
|
failedSegmentFallbackPrepared = true;
|
|
2162
2021
|
queuedFinalActivity = void 0;
|
|
@@ -2184,43 +2043,22 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2184
2043
|
const msteamsCfg = params.cfg.channels?.msteams;
|
|
2185
2044
|
const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationRef.conversation?.conversationType);
|
|
2186
2045
|
const isTypingSupported = conversationType === "personal" || conversationType === "groupchat";
|
|
2187
|
-
/**
|
|
2188
|
-
* Keepalive cadence for the typing indicator while the bot is running
|
|
2189
|
-
* (including long tool chains). Bot Framework 1:1 TurnContext proxies
|
|
2190
|
-
* expire after ~30s of inactivity; sending a typing activity every 8s
|
|
2191
|
-
* keeps the proxy alive so the post-tool reply can still land via the
|
|
2192
|
-
* turn context. Sits in the middle of the 5-10s range recommended in
|
|
2193
|
-
* #59731.
|
|
2194
|
-
*/
|
|
2195
2046
|
const TYPING_KEEPALIVE_INTERVAL_MS = 8e3;
|
|
2196
|
-
/**
|
|
2197
|
-
* TTL ceiling for the typing keepalive loop. The default in
|
|
2198
|
-
* createTypingCallbacks is 60s, which is too short for the Teams long tool
|
|
2199
|
-
* chains described in #59731 (60s+ total runs are common). Give tool
|
|
2200
|
-
* chains up to 10 minutes before auto-stopping the keepalive.
|
|
2201
|
-
*/
|
|
2202
2047
|
const TYPING_KEEPALIVE_MAX_DURATION_MS = 6e5;
|
|
2203
|
-
const
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
await require_messenger.withRevokedProxyFallback({
|
|
2048
|
+
const sendTypingIndicator = async () => {
|
|
2049
|
+
if (!isTypingSupported || streamController.isStreamActive() || streamController.wasCanceled()) return;
|
|
2050
|
+
await require_conversation_store_state.withRevokedProxyFallback({
|
|
2207
2051
|
run: async () => {
|
|
2208
2052
|
await params.context.sendActivity({ type: "typing" });
|
|
2209
2053
|
},
|
|
2210
2054
|
onRevoked: async () => {
|
|
2211
|
-
|
|
2212
|
-
await require_messenger.sendMSTeamsActivityWithReference(params.app, baseRef, { type: "typing" }, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg) });
|
|
2055
|
+
await require_conversation_store_state.sendMSTeamsActivityWithReference(params.app, require_conversation_store_state.buildConversationReference(params.conversationRef), { type: "typing" }, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg) });
|
|
2213
2056
|
},
|
|
2214
2057
|
onRevokedLog: () => {
|
|
2215
2058
|
params.log.debug?.("turn context revoked, sending typing via proactive messaging");
|
|
2216
2059
|
}
|
|
2217
2060
|
});
|
|
2218
2061
|
};
|
|
2219
|
-
const sendTypingIndicator = isTypingSupported ? async () => {
|
|
2220
|
-
if (streamActiveRef.current()) return;
|
|
2221
|
-
if (streamCanceledRef.current()) return;
|
|
2222
|
-
await rawSendTypingIndicator();
|
|
2223
|
-
} : async () => {};
|
|
2224
2062
|
const { onModelSelected, typingCallbacks, ...replyPipeline } = (0, openclaw_plugin_sdk_channel_outbound.createChannelMessageReplyPipeline)({
|
|
2225
2063
|
cfg: params.cfg,
|
|
2226
2064
|
agentId: params.agentId,
|
|
@@ -2261,8 +2099,6 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2261
2099
|
msteamsConfig: msteamsCfg,
|
|
2262
2100
|
progressSeed: `${params.accountId ?? "default"}:${params.conversationRef.conversation?.id ?? ""}`
|
|
2263
2101
|
});
|
|
2264
|
-
streamActiveRef.current = () => streamController.isStreamActive();
|
|
2265
|
-
streamCanceledRef.current = () => streamController.wasCanceled();
|
|
2266
2102
|
const teamsStreamMode = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelPreviewStreamMode)(msteamsCfg, "partial");
|
|
2267
2103
|
const blockStreamingResolved = teamsStreamMode === "block" ? true : (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingBlockEnabled)(msteamsCfg);
|
|
2268
2104
|
const blockStreamingEnabled = blockStreamingResolved ?? false;
|
|
@@ -2272,7 +2108,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2272
2108
|
const findPendingNativeDelivery = () => pendingDeliveries.find((candidate) => candidate.native && !candidate.nativeSettled);
|
|
2273
2109
|
const joinAcceptedContents = (contents) => contents.filter((content) => Boolean(content)).join("\n");
|
|
2274
2110
|
const sendMessages = async (messages) => {
|
|
2275
|
-
return
|
|
2111
|
+
return require_conversation_store_state.sendMSTeamsMessages({
|
|
2276
2112
|
replyStyle: params.replyStyle,
|
|
2277
2113
|
app: params.app,
|
|
2278
2114
|
appId: params.appId,
|
|
@@ -2311,7 +2147,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2311
2147
|
});
|
|
2312
2148
|
};
|
|
2313
2149
|
const renderReplyPayload = (payload) => {
|
|
2314
|
-
return
|
|
2150
|
+
return require_conversation_store_state.renderReplyPayloadsToMessages([payload], {
|
|
2315
2151
|
textChunkLimit: params.textLimit,
|
|
2316
2152
|
chunkText: true,
|
|
2317
2153
|
mediaMode: "split",
|
|
@@ -2404,7 +2240,6 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2404
2240
|
...replyPipeline,
|
|
2405
2241
|
humanDelay: (0, openclaw_plugin_sdk_agent_runtime.resolveHumanDelayConfig)(params.cfg, params.agentId),
|
|
2406
2242
|
onReplyStart: async () => {
|
|
2407
|
-
await streamController.onReplyStart();
|
|
2408
2243
|
if (typingIndicatorEnabled) await typingCallbacks?.onReplyStart?.();
|
|
2409
2244
|
},
|
|
2410
2245
|
typingCallbacks
|
|
@@ -2492,35 +2327,8 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2492
2327
|
streamController.resetReasoningProgress();
|
|
2493
2328
|
return false;
|
|
2494
2329
|
},
|
|
2495
|
-
onToolStart:
|
|
2496
|
-
|
|
2497
|
-
const detailMode = typeof payload?.detailMode === "string" ? payload.detailMode : void 0;
|
|
2498
|
-
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
|
|
2499
|
-
event: "tool",
|
|
2500
|
-
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
2501
|
-
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
2502
|
-
...name ? { name } : {},
|
|
2503
|
-
...typeof payload?.phase === "string" ? { phase: payload.phase } : {},
|
|
2504
|
-
...payload?.args && typeof payload.args === "object" ? { args: payload.args } : {}
|
|
2505
|
-
}, detailMode === "explain" || detailMode === "raw" ? { detailMode } : void 0), name ? { toolName: name } : void 0);
|
|
2506
|
-
return false;
|
|
2507
|
-
},
|
|
2508
|
-
onItemEvent: async (payload) => {
|
|
2509
|
-
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
|
|
2510
|
-
event: "item",
|
|
2511
|
-
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
2512
|
-
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
2513
|
-
...typeof payload?.kind === "string" ? { itemKind: payload.kind } : {},
|
|
2514
|
-
...typeof payload?.title === "string" ? { title: payload.title } : {},
|
|
2515
|
-
...typeof payload?.name === "string" ? { name: payload.name } : {},
|
|
2516
|
-
...typeof payload?.phase === "string" ? { phase: payload.phase } : {},
|
|
2517
|
-
...typeof payload?.status === "string" ? { status: payload.status } : {},
|
|
2518
|
-
...typeof payload?.summary === "string" ? { summary: payload.summary } : {},
|
|
2519
|
-
...typeof payload?.progressText === "string" ? { progressText: payload.progressText } : {},
|
|
2520
|
-
...typeof payload?.meta === "string" ? { meta: payload.meta } : {}
|
|
2521
|
-
}));
|
|
2522
|
-
return false;
|
|
2523
|
-
},
|
|
2330
|
+
onToolStart: streamController.pushToolEvent,
|
|
2331
|
+
onItemEvent: streamController.pushItemEvent,
|
|
2524
2332
|
onPlanUpdate: async (payload) => {
|
|
2525
2333
|
if (payload?.phase !== "update") return false;
|
|
2526
2334
|
await streamController.pushPlanProgress((0, openclaw_plugin_sdk_channel_outbound.normalizeAgentPlanSteps)(payload.steps), {
|
|
@@ -2539,36 +2347,6 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2539
2347
|
...typeof payload?.message === "string" ? { message: payload.message } : {}
|
|
2540
2348
|
});
|
|
2541
2349
|
return false;
|
|
2542
|
-
},
|
|
2543
|
-
onCommandOutput: async (payload) => {
|
|
2544
|
-
if (payload?.phase !== "end") return false;
|
|
2545
|
-
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
|
|
2546
|
-
event: "command-output",
|
|
2547
|
-
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
2548
|
-
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
2549
|
-
phase: payload.phase,
|
|
2550
|
-
...typeof payload?.title === "string" ? { title: payload.title } : {},
|
|
2551
|
-
...typeof payload?.name === "string" ? { name: payload.name } : {},
|
|
2552
|
-
...typeof payload?.status === "string" ? { status: payload.status } : {},
|
|
2553
|
-
...typeof payload?.exitCode === "number" ? { exitCode: payload.exitCode } : {}
|
|
2554
|
-
}));
|
|
2555
|
-
return false;
|
|
2556
|
-
},
|
|
2557
|
-
onPatchSummary: async (payload) => {
|
|
2558
|
-
if (payload?.phase !== "end") return false;
|
|
2559
|
-
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
|
|
2560
|
-
event: "patch",
|
|
2561
|
-
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
2562
|
-
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
2563
|
-
phase: payload.phase,
|
|
2564
|
-
...typeof payload?.title === "string" ? { title: payload.title } : {},
|
|
2565
|
-
...typeof payload?.name === "string" ? { name: payload.name } : {},
|
|
2566
|
-
...Array.isArray(payload?.added) && payload.added.every((s) => typeof s === "string") ? { added: payload.added } : {},
|
|
2567
|
-
...Array.isArray(payload?.modified) && payload.modified.every((s) => typeof s === "string") ? { modified: payload.modified } : {},
|
|
2568
|
-
...Array.isArray(payload?.deleted) && payload.deleted.every((s) => typeof s === "string") ? { deleted: payload.deleted } : {},
|
|
2569
|
-
...typeof payload?.summary === "string" ? { summary: payload.summary } : {}
|
|
2570
|
-
}));
|
|
2571
|
-
return false;
|
|
2572
2350
|
}
|
|
2573
2351
|
} : {};
|
|
2574
2352
|
return {
|
|
@@ -2578,6 +2356,8 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2578
2356
|
},
|
|
2579
2357
|
delivery,
|
|
2580
2358
|
replyOptions: {
|
|
2359
|
+
progressPreambleEnabled: shouldSuppressDefaultToolProgressMessages,
|
|
2360
|
+
commentaryProgressEnabled: shouldSuppressDefaultToolProgressMessages,
|
|
2581
2361
|
...streamController.hasStream() ? { onPartialReply: (payload) => {
|
|
2582
2362
|
streamController.onPartialReply(payload);
|
|
2583
2363
|
return false;
|
|
@@ -2590,32 +2370,6 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2590
2370
|
};
|
|
2591
2371
|
}
|
|
2592
2372
|
//#endregion
|
|
2593
|
-
//#region extensions/msteams/src/sent-message-cache.ts
|
|
2594
|
-
const sentMessages = (0, openclaw_plugin_sdk_dedupe_runtime.createPersistentDedupeCache)({
|
|
2595
|
-
globalKey: Symbol.for("openclaw.msteamsSentMessages"),
|
|
2596
|
-
ttlMs: 864e5,
|
|
2597
|
-
maxSize: 2e4,
|
|
2598
|
-
persistent: {
|
|
2599
|
-
namespace: "msteams.sent-messages",
|
|
2600
|
-
maxEntries: 1e3,
|
|
2601
|
-
openStore: (options) => require_runtime.getOptionalMSTeamsRuntime()?.state.openKeyedStore(options),
|
|
2602
|
-
logError: (0, openclaw_plugin_sdk_plugin_state_runtime.createPluginStateErrorReporter)(require_runtime.getOptionalMSTeamsRuntime, "msteams", "sent-message-state", "Microsoft Teams persistent sent-message state failed"),
|
|
2603
|
-
readTimestamp: (record) => record.sentAt
|
|
2604
|
-
}
|
|
2605
|
-
});
|
|
2606
|
-
function makeKey(conversationId, messageId) {
|
|
2607
|
-
return `${conversationId}:${messageId}`;
|
|
2608
|
-
}
|
|
2609
|
-
function recordMSTeamsSentMessage(conversationId, messageId) {
|
|
2610
|
-
if (!conversationId || !messageId) return;
|
|
2611
|
-
const sentAt = Date.now();
|
|
2612
|
-
sentMessages.register(makeKey(conversationId, messageId), { sentAt }, { at: sentAt });
|
|
2613
|
-
}
|
|
2614
|
-
async function wasMSTeamsMessageSentWithPersistence(params) {
|
|
2615
|
-
if (!params.conversationId || !params.messageId) return false;
|
|
2616
|
-
return await sentMessages.lookup(makeKey(params.conversationId, params.messageId));
|
|
2617
|
-
}
|
|
2618
|
-
//#endregion
|
|
2619
2373
|
//#region extensions/msteams/src/monitor-handler/inbound-dispatch.ts
|
|
2620
2374
|
async function dispatchMSTeamsInboundTurn(params) {
|
|
2621
2375
|
const core = require_runtime.getMSTeamsRuntime();
|
|
@@ -2640,7 +2394,7 @@ async function dispatchMSTeamsInboundTurn(params) {
|
|
|
2640
2394
|
body: agentBody
|
|
2641
2395
|
});
|
|
2642
2396
|
const isRoomish = !isDirectMessage;
|
|
2643
|
-
const historyKey = isRoomish ?
|
|
2397
|
+
const historyKey = isRoomish ? facts.historyKey : void 0;
|
|
2644
2398
|
if (isRoomish && historyKey) combinedBody = (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).buildPendingContext({
|
|
2645
2399
|
historyKey,
|
|
2646
2400
|
limit: historyLimit,
|
|
@@ -2662,7 +2416,7 @@ async function dispatchMSTeamsInboundTurn(params) {
|
|
|
2662
2416
|
isGroup: !isDirectMessage,
|
|
2663
2417
|
groupPolicy,
|
|
2664
2418
|
allowFrom: effectiveGroupAllowFrom,
|
|
2665
|
-
isSenderAllowed: (allowFrom) =>
|
|
2419
|
+
isSenderAllowed: (allowFrom) => (0, openclaw_plugin_sdk_allow_from.resolveAllowlistMatchSimple)({
|
|
2666
2420
|
allowFrom,
|
|
2667
2421
|
senderId: quoteSenderId ?? "",
|
|
2668
2422
|
senderName: quoteSenderName,
|
|
@@ -2770,7 +2524,7 @@ async function dispatchMSTeamsInboundTurn(params) {
|
|
|
2770
2524
|
replyStyle,
|
|
2771
2525
|
textLimit,
|
|
2772
2526
|
onSentMessageIds: (ids) => {
|
|
2773
|
-
for (const id of ids) recordMSTeamsSentMessage(conversationId, id);
|
|
2527
|
+
for (const id of ids) require_conversation_store_state.recordMSTeamsSentMessage(conversationId, id);
|
|
2774
2528
|
},
|
|
2775
2529
|
tokenProvider,
|
|
2776
2530
|
sharePointSiteId: cfg.channels?.msteams?.sharePointSiteId
|
|
@@ -2864,7 +2618,7 @@ async function prepareMSTeamsDebounceEntry(params) {
|
|
|
2864
2618
|
const text = rawText ? require_resolve_allowlist.stripMSTeamsMentionTags(rawText) : extractTextFromHtmlAttachments(attachments) || require_resolve_allowlist.stripMSTeamsMentionTags(serializeMSTeamsAdaptiveCardActionValue(activity.value) || "");
|
|
2865
2619
|
const conversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
2866
2620
|
const replyToId = activity.replyToId ?? void 0;
|
|
2867
|
-
const implicitMentionKinds = conversationId && replyToId && await wasMSTeamsMessageSentWithPersistence({
|
|
2621
|
+
const implicitMentionKinds = conversationId && replyToId && await require_conversation_store_state.wasMSTeamsMessageSentWithPersistence({
|
|
2868
2622
|
conversationId,
|
|
2869
2623
|
messageId: replyToId
|
|
2870
2624
|
}) ? ["reply_to_bot"] : [];
|
|
@@ -2885,7 +2639,7 @@ function buildStoredConversationReference(params) {
|
|
|
2885
2639
|
const clientInfo = activity.entities?.find((entity) => entity.type === "clientInfo");
|
|
2886
2640
|
const tenantId = activity.channelData?.tenant?.id ?? conversation?.tenantId;
|
|
2887
2641
|
const aadObjectId = from?.aadObjectId;
|
|
2888
|
-
const serviceUrl =
|
|
2642
|
+
const serviceUrl = require_bot_framework_service_url.tryNormalizeBotFrameworkServiceUrl(activity.serviceUrl);
|
|
2889
2643
|
return {
|
|
2890
2644
|
activityId: activity.id,
|
|
2891
2645
|
user: from ? {
|
|
@@ -2938,6 +2692,7 @@ function assembleMSTeamsInboundFacts(entry) {
|
|
|
2938
2692
|
teamId,
|
|
2939
2693
|
graphChannelId: activity.channelData?.channel?.id?.trim() || conversationId,
|
|
2940
2694
|
threadId,
|
|
2695
|
+
historyKey: threadId ? `${conversationId}:thread:${threadId}` : conversationId,
|
|
2941
2696
|
conversationRef: buildStoredConversationReference({
|
|
2942
2697
|
activity,
|
|
2943
2698
|
conversationId,
|
|
@@ -2983,12 +2738,9 @@ const parentCache = /* @__PURE__ */ new Map();
|
|
|
2983
2738
|
const INJECTED_MAX = 200;
|
|
2984
2739
|
const injectedParents = /* @__PURE__ */ new Map();
|
|
2985
2740
|
function touchLru(map, key, value, max) {
|
|
2986
|
-
|
|
2987
|
-
else if (map.size >= max) {
|
|
2988
|
-
const firstKey = map.keys().next().value;
|
|
2989
|
-
if (firstKey !== void 0) map.delete(firstKey);
|
|
2990
|
-
}
|
|
2741
|
+
map.delete(key);
|
|
2991
2742
|
map.set(key, value);
|
|
2743
|
+
(0, openclaw_plugin_sdk_collection_runtime.pruneMapToMaxSize)(map, max);
|
|
2992
2744
|
}
|
|
2993
2745
|
function buildParentCacheKey(groupId, channelId, parentId) {
|
|
2994
2746
|
return `${groupId}\u0000${channelId}\u0000${parentId}`;
|
|
@@ -2997,12 +2749,6 @@ function resolveParentCacheExpiresAt(nowRaw) {
|
|
|
2997
2749
|
const nowMs = (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(nowRaw);
|
|
2998
2750
|
return nowMs === void 0 ? void 0 : (0, openclaw_plugin_sdk_number_runtime.resolveExpiresAtMsFromDurationMs)(PARENT_CACHE_TTL_MS, { nowMs });
|
|
2999
2751
|
}
|
|
3000
|
-
/**
|
|
3001
|
-
* Fetch a channel parent message with an LRU+TTL cache.
|
|
3002
|
-
*
|
|
3003
|
-
* Uses the injected `fetchParent` (defaults to `fetchChannelMessage`) so
|
|
3004
|
-
* tests can swap in a stub without mocking the Graph transport.
|
|
3005
|
-
*/
|
|
3006
2752
|
async function fetchParentMessageCached(token, groupId, channelId, parentId, fetchParent = require_reaction_types.fetchChannelMessage) {
|
|
3007
2753
|
const key = buildParentCacheKey(groupId, channelId, parentId);
|
|
3008
2754
|
const now = (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(Date.now());
|
|
@@ -3023,11 +2769,6 @@ async function fetchParentMessageCached(token, groupId, channelId, parentId, fet
|
|
|
3023
2769
|
return message;
|
|
3024
2770
|
}
|
|
3025
2771
|
const PARENT_TEXT_MAX_CHARS = 400;
|
|
3026
|
-
/**
|
|
3027
|
-
* Extract a compact summary (sender + plain-text body) from a Graph parent
|
|
3028
|
-
* message. Returns undefined when the parent cannot be summarized (missing
|
|
3029
|
-
* or blank body).
|
|
3030
|
-
*/
|
|
3031
2772
|
function summarizeParentMessage(message) {
|
|
3032
2773
|
if (!message) return;
|
|
3033
2774
|
const sender = message.from?.user?.displayName ?? message.from?.application?.displayName ?? "unknown";
|
|
@@ -3040,28 +2781,12 @@ function summarizeParentMessage(message) {
|
|
|
3040
2781
|
text: text.length > PARENT_TEXT_MAX_CHARS ? `${(0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(text, 399)}…` : text
|
|
3041
2782
|
};
|
|
3042
2783
|
}
|
|
3043
|
-
/**
|
|
3044
|
-
* Build the single-line `Replying to @sender: body` system event text.
|
|
3045
|
-
* Callers should pass this text to `enqueueSystemEvent` together with a
|
|
3046
|
-
* stable contextKey derived from the parent id.
|
|
3047
|
-
*/
|
|
3048
2784
|
function formatParentContextEvent(summary) {
|
|
3049
2785
|
return `Replying to @${summary.sender}: ${summary.text}`;
|
|
3050
2786
|
}
|
|
3051
|
-
/**
|
|
3052
|
-
* Decide whether a parent context event should be enqueued for the current
|
|
3053
|
-
* session. Returns `false` when we already injected the same parent for this
|
|
3054
|
-
* session recently (prevents re-prepending identical context on every reply
|
|
3055
|
-
* in the thread).
|
|
3056
|
-
*/
|
|
3057
2787
|
function shouldInjectParentContext(sessionKey, parentId) {
|
|
3058
|
-
|
|
3059
|
-
return injectedParents.get(key) !== parentId;
|
|
2788
|
+
return injectedParents.get(sessionKey) !== parentId;
|
|
3060
2789
|
}
|
|
3061
|
-
/**
|
|
3062
|
-
* Record that `parentId` was just injected for `sessionKey` so subsequent
|
|
3063
|
-
* replies with the same parent can short-circuit via `shouldInjectParentContext`.
|
|
3064
|
-
*/
|
|
3065
2790
|
function markParentContextInjected(sessionKey, parentId) {
|
|
3066
2791
|
touchLru(injectedParents, sessionKey, parentId, INJECTED_MAX);
|
|
3067
2792
|
}
|
|
@@ -3155,7 +2880,7 @@ async function resolveMSTeamsThreadContext(params) {
|
|
|
3155
2880
|
isGroup: params.isChannel,
|
|
3156
2881
|
groupPolicy: params.groupPolicy,
|
|
3157
2882
|
allowFrom: params.effectiveGroupAllowFrom,
|
|
3158
|
-
isSenderAllowed: (allowFrom) =>
|
|
2883
|
+
isSenderAllowed: (allowFrom) => (0, openclaw_plugin_sdk_allow_from.resolveAllowlistMatchSimple)({
|
|
3159
2884
|
allowFrom,
|
|
3160
2885
|
senderId: message.from?.user?.id ?? "",
|
|
3161
2886
|
senderName: message.from?.user?.displayName,
|
|
@@ -3211,7 +2936,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
3211
2936
|
cfg,
|
|
3212
2937
|
channel: "msteams"
|
|
3213
2938
|
});
|
|
3214
|
-
const historyLimit =
|
|
2939
|
+
const historyLimit = (0, openclaw_plugin_sdk_number_runtime.resolvePromptHistoryLimit)(msteamsCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit);
|
|
3215
2940
|
const conversationHistories = /* @__PURE__ */ new Map();
|
|
3216
2941
|
const readConfig = (0, openclaw_plugin_sdk_runtime_config_snapshot.createRuntimeConfigReader)(cfg);
|
|
3217
2942
|
const resolveDebounceMs = () => core.channel.debounce.resolveInboundDebounceMs({
|
|
@@ -3235,8 +2960,35 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
3235
2960
|
log.debug?.("skipping message without from.id");
|
|
3236
2961
|
return;
|
|
3237
2962
|
}
|
|
2963
|
+
let currentCfg = readConfig();
|
|
2964
|
+
let isBotOwnedThread = false;
|
|
2965
|
+
if (isChannel && facts.threadId && activity.recipient?.id) {
|
|
2966
|
+
const channelConfig = currentCfg.channels?.msteams;
|
|
2967
|
+
const routeConfig = require_channel.resolveMSTeamsRouteConfig({
|
|
2968
|
+
cfg: channelConfig,
|
|
2969
|
+
teamId,
|
|
2970
|
+
teamName: activity.channelData?.team?.name,
|
|
2971
|
+
conversationId,
|
|
2972
|
+
channelName: activity.channelData?.channel?.name,
|
|
2973
|
+
allowNameMatching: channelConfig?.dangerouslyAllowNameMatching === true
|
|
2974
|
+
});
|
|
2975
|
+
if (require_channel.resolveMSTeamsReplyPolicy({
|
|
2976
|
+
isDirectMessage: false,
|
|
2977
|
+
globalConfig: channelConfig,
|
|
2978
|
+
...routeConfig
|
|
2979
|
+
}).requireMentionInBotThreads !== void 0) {
|
|
2980
|
+
isBotOwnedThread = await require_conversation_store_state.wasMSTeamsMessageSentWithPersistence({
|
|
2981
|
+
conversationId,
|
|
2982
|
+
messageId: facts.threadId,
|
|
2983
|
+
botId: activity.recipient.id
|
|
2984
|
+
});
|
|
2985
|
+
currentCfg = readConfig();
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
const currentMSTeamsCfg = currentCfg.channels?.msteams;
|
|
2989
|
+
if (currentMSTeamsCfg?.enabled === false || currentMSTeamsCfg?.appId && currentMSTeamsCfg.appId !== appId) return;
|
|
3238
2990
|
const admission = await admitMSTeamsMessage({
|
|
3239
|
-
cfg,
|
|
2991
|
+
cfg: currentCfg,
|
|
3240
2992
|
activity,
|
|
3241
2993
|
text,
|
|
3242
2994
|
conversationId,
|
|
@@ -3248,7 +3000,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
3248
3000
|
});
|
|
3249
3001
|
if (!admission) return;
|
|
3250
3002
|
const { senderId, senderName, isDirectMessage, channelGate, allowNameMatching, groupPolicy, commandAuthorized, effectiveGroupAllowFrom, allowTextCommands, isControlCommand } = admission;
|
|
3251
|
-
const pollVote =
|
|
3003
|
+
const pollVote = require_conversation_store_state.extractMSTeamsPollVote(activity);
|
|
3252
3004
|
if (pollVote) {
|
|
3253
3005
|
try {
|
|
3254
3006
|
if (!await pollStore.recordVote({
|
|
@@ -3270,7 +3022,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
3270
3022
|
return;
|
|
3271
3023
|
}
|
|
3272
3024
|
const threadRouting = prepareMSTeamsThreadRouting({
|
|
3273
|
-
cfg,
|
|
3025
|
+
cfg: currentCfg,
|
|
3274
3026
|
context,
|
|
3275
3027
|
isDirectMessage,
|
|
3276
3028
|
isChannel,
|
|
@@ -3288,18 +3040,25 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
3288
3040
|
});
|
|
3289
3041
|
const channelId = conversationId;
|
|
3290
3042
|
const { teamConfig, channelConfig } = channelGate;
|
|
3291
|
-
const
|
|
3043
|
+
const replyPolicy = require_channel.resolveMSTeamsReplyPolicy({
|
|
3292
3044
|
isDirectMessage,
|
|
3293
|
-
globalConfig:
|
|
3045
|
+
globalConfig: currentMSTeamsCfg,
|
|
3294
3046
|
teamConfig,
|
|
3295
3047
|
channelConfig
|
|
3296
3048
|
});
|
|
3049
|
+
const { requireMention, implicitMentionKinds } = resolveBotThreadMentionPolicy({
|
|
3050
|
+
isBotOwnedThread,
|
|
3051
|
+
requireMention: replyPolicy.requireMention,
|
|
3052
|
+
requireMentionInBotThreads: replyPolicy.requireMentionInBotThreads,
|
|
3053
|
+
implicitMentionKinds: params.implicitMentionKinds
|
|
3054
|
+
});
|
|
3055
|
+
const { replyStyle } = replyPolicy;
|
|
3297
3056
|
const timestamp = require_resolve_allowlist.parseMSTeamsActivityTimestamp(activity.timestamp);
|
|
3298
3057
|
const mentionDecision = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundMentionDecision)({
|
|
3299
3058
|
facts: {
|
|
3300
3059
|
canDetectMention: true,
|
|
3301
3060
|
wasMentioned: params.wasMentioned,
|
|
3302
|
-
implicitMentionKinds
|
|
3061
|
+
implicitMentionKinds
|
|
3303
3062
|
},
|
|
3304
3063
|
policy: {
|
|
3305
3064
|
isGroup: !isDirectMessage,
|
|
@@ -3321,7 +3080,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
3321
3080
|
if (historyBody) {
|
|
3322
3081
|
enqueuePrimaryMessageSystemEvent();
|
|
3323
3082
|
(0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).record({
|
|
3324
|
-
historyKey:
|
|
3083
|
+
historyKey: facts.historyKey,
|
|
3325
3084
|
limit: historyLimit,
|
|
3326
3085
|
entry: {
|
|
3327
3086
|
sender: senderName,
|
|
@@ -3346,9 +3105,9 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
3346
3105
|
resolveTeamAadGroupId: threadRouting.resolveTeamAadGroupId,
|
|
3347
3106
|
mediaMaxBytes,
|
|
3348
3107
|
tokenProvider,
|
|
3349
|
-
mediaAllowHosts:
|
|
3350
|
-
mediaAuthAllowHosts:
|
|
3351
|
-
graphMediaFallback:
|
|
3108
|
+
mediaAllowHosts: currentMSTeamsCfg?.mediaAllowHosts,
|
|
3109
|
+
mediaAuthAllowHosts: currentMSTeamsCfg?.mediaAuthAllowHosts,
|
|
3110
|
+
graphMediaFallback: currentMSTeamsCfg?.graphMediaFallback,
|
|
3352
3111
|
deadline: preprocessingDeadline,
|
|
3353
3112
|
log
|
|
3354
3113
|
});
|
|
@@ -3369,7 +3128,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
3369
3128
|
log
|
|
3370
3129
|
});
|
|
3371
3130
|
await dispatchMSTeamsInboundTurn({
|
|
3372
|
-
cfg,
|
|
3131
|
+
cfg: currentCfg,
|
|
3373
3132
|
runtime,
|
|
3374
3133
|
appId,
|
|
3375
3134
|
app,
|
|
@@ -3394,10 +3153,19 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
3394
3153
|
debounceMs: resolveDebounceMs(),
|
|
3395
3154
|
resolveDebounceMs,
|
|
3396
3155
|
buildKey: (entry) => {
|
|
3397
|
-
const
|
|
3156
|
+
const activity = entry.context.activity;
|
|
3157
|
+
const rawConversationId = activity.conversation?.id ?? "";
|
|
3158
|
+
const conversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(rawConversationId);
|
|
3159
|
+
const threadId = activity.conversation?.conversationType === "channel" ? require_resolve_allowlist.extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId : void 0;
|
|
3398
3160
|
const senderId = entry.context.activity.from?.aadObjectId ?? entry.context.activity.from?.id ?? "";
|
|
3399
3161
|
if (!senderId || !conversationId) return null;
|
|
3400
|
-
return
|
|
3162
|
+
return JSON.stringify([
|
|
3163
|
+
"msteams",
|
|
3164
|
+
appId,
|
|
3165
|
+
conversationId,
|
|
3166
|
+
threadId ?? null,
|
|
3167
|
+
senderId
|
|
3168
|
+
]);
|
|
3401
3169
|
},
|
|
3402
3170
|
shouldDebounce: (entry) => {
|
|
3403
3171
|
if (!entry.text.trim()) return false;
|
|
@@ -3520,6 +3288,11 @@ function createMSTeamsReactionHandler(deps) {
|
|
|
3520
3288
|
},
|
|
3521
3289
|
...teamId ? { teamId } : {}
|
|
3522
3290
|
});
|
|
3291
|
+
const sessionKey = require_thread_session.resolveMSTeamsRouteSessionKey({
|
|
3292
|
+
baseSessionKey: route.sessionKey,
|
|
3293
|
+
isChannel,
|
|
3294
|
+
conversationMessageId: require_resolve_allowlist.extractMSTeamsConversationMessageId(rawConversationId)
|
|
3295
|
+
});
|
|
3523
3296
|
const targetMessageId = activity.replyToId ?? "unknown";
|
|
3524
3297
|
for (const reaction of reactions) {
|
|
3525
3298
|
const reactionType = reaction.type ?? "unknown";
|
|
@@ -3533,7 +3306,7 @@ function createMSTeamsReactionHandler(deps) {
|
|
|
3533
3306
|
conversationId
|
|
3534
3307
|
});
|
|
3535
3308
|
core.system.enqueueSystemEvent(label, {
|
|
3536
|
-
sessionKey
|
|
3309
|
+
sessionKey,
|
|
3537
3310
|
contextKey: `msteams:reaction:${conversationId}:${targetMessageId}:${senderId}:${reactionType}:${direction}`
|
|
3538
3311
|
});
|
|
3539
3312
|
}
|
|
@@ -3665,56 +3438,10 @@ async function isCardActionInvokeAuthorized(context, deps) {
|
|
|
3665
3438
|
includeInvokeName: true
|
|
3666
3439
|
});
|
|
3667
3440
|
}
|
|
3668
|
-
function
|
|
3441
|
+
function createMSTeamsActivityHandler(deps) {
|
|
3669
3442
|
const handleTeamsMessage = createMSTeamsMessageHandler(deps);
|
|
3670
3443
|
const handleReaction = createMSTeamsReactionHandler(deps);
|
|
3671
|
-
const
|
|
3672
|
-
if (originalRun) handler.run = async (context, turnAdoptionLifecycle) => {
|
|
3673
|
-
const ctx = context;
|
|
3674
|
-
if (ctx.activity?.type === "invoke" && ctx.activity?.name === "adaptiveCard/action") {
|
|
3675
|
-
if (await maybeHandleMSTeamsApprovalCardSubmit({
|
|
3676
|
-
context: ctx,
|
|
3677
|
-
deps
|
|
3678
|
-
})) return;
|
|
3679
|
-
const text = serializeMSTeamsAdaptiveCardActionValue(ctx.activity?.value);
|
|
3680
|
-
if (text) return await handleTeamsMessage({
|
|
3681
|
-
...ctx,
|
|
3682
|
-
activity: {
|
|
3683
|
-
...ctx.activity,
|
|
3684
|
-
type: "message",
|
|
3685
|
-
text
|
|
3686
|
-
}
|
|
3687
|
-
}, turnAdoptionLifecycle);
|
|
3688
|
-
return;
|
|
3689
|
-
}
|
|
3690
|
-
return originalRun.call(handler, context, turnAdoptionLifecycle);
|
|
3691
|
-
};
|
|
3692
|
-
handler.onMessage(async (context, next, turnAdoptionLifecycle) => {
|
|
3693
|
-
let nextRan = false;
|
|
3694
|
-
const runNext = async () => {
|
|
3695
|
-
nextRan = true;
|
|
3696
|
-
await next();
|
|
3697
|
-
};
|
|
3698
|
-
try {
|
|
3699
|
-
const ctx = context;
|
|
3700
|
-
if (await maybeHandleMSTeamsApprovalCardSubmit({
|
|
3701
|
-
context: ctx,
|
|
3702
|
-
deps
|
|
3703
|
-
})) {
|
|
3704
|
-
await runNext();
|
|
3705
|
-
return;
|
|
3706
|
-
}
|
|
3707
|
-
const result = await handleTeamsMessage(ctx, turnAdoptionLifecycle);
|
|
3708
|
-
await runNext();
|
|
3709
|
-
return result;
|
|
3710
|
-
} catch (err) {
|
|
3711
|
-
if (turnAdoptionLifecycle) throw err;
|
|
3712
|
-
deps.runtime.error(`msteams handler failed: ${require_channel_setup.formatUnknownError(err)}`);
|
|
3713
|
-
}
|
|
3714
|
-
if (!nextRan) await runNext();
|
|
3715
|
-
});
|
|
3716
|
-
handler.onMembersAdded(async (context, next) => {
|
|
3717
|
-
const ctx = context;
|
|
3444
|
+
const handleMembersAdded = async (ctx) => {
|
|
3718
3445
|
const membersAdded = ctx.activity?.membersAdded ?? [];
|
|
3719
3446
|
const botId = ctx.activity?.recipient?.id;
|
|
3720
3447
|
const msteamsCfg = deps.cfg.channels?.msteams;
|
|
@@ -3726,13 +3453,7 @@ function registerMSTeamsHandlers(handler, deps) {
|
|
|
3726
3453
|
promptStarters: msteamsCfg?.promptStarters
|
|
3727
3454
|
});
|
|
3728
3455
|
try {
|
|
3729
|
-
await ctx.sendActivity(
|
|
3730
|
-
type: "message",
|
|
3731
|
-
attachments: [{
|
|
3732
|
-
contentType: "application/vnd.microsoft.card.adaptive",
|
|
3733
|
-
content: card
|
|
3734
|
-
}]
|
|
3735
|
-
});
|
|
3456
|
+
await ctx.sendActivity(require_conversation_store_state.buildMSTeamsAdaptiveCardActivity(card));
|
|
3736
3457
|
deps.log.info("sent welcome card");
|
|
3737
3458
|
} catch (err) {
|
|
3738
3459
|
deps.log.debug?.("failed to send welcome card", { error: require_channel_setup.formatUnknownError(err) });
|
|
@@ -3747,25 +3468,45 @@ function registerMSTeamsHandlers(handler, deps) {
|
|
|
3747
3468
|
}
|
|
3748
3469
|
} else deps.log.debug?.("skipping welcome (disabled by config or conversation type)");
|
|
3749
3470
|
} else deps.log.debug?.("member added", { member: member.id });
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
await
|
|
3755
|
-
|
|
3756
|
-
|
|
3471
|
+
};
|
|
3472
|
+
return async (context, turnAdoptionLifecycle) => {
|
|
3473
|
+
const activity = context.activity;
|
|
3474
|
+
if (activity?.type === "invoke" && activity.name === "adaptiveCard/action") {
|
|
3475
|
+
if (await maybeHandleMSTeamsApprovalCardSubmit({
|
|
3476
|
+
context,
|
|
3477
|
+
deps
|
|
3478
|
+
})) return;
|
|
3479
|
+
const text = serializeMSTeamsAdaptiveCardActionValue(activity.value);
|
|
3480
|
+
if (text) return handleTeamsMessage({
|
|
3481
|
+
...context,
|
|
3482
|
+
activity: {
|
|
3483
|
+
...activity,
|
|
3484
|
+
type: "message",
|
|
3485
|
+
text
|
|
3486
|
+
}
|
|
3487
|
+
}, turnAdoptionLifecycle);
|
|
3488
|
+
return;
|
|
3757
3489
|
}
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3490
|
+
if (activity?.type === "message") try {
|
|
3491
|
+
if (await maybeHandleMSTeamsApprovalCardSubmit({
|
|
3492
|
+
context,
|
|
3493
|
+
deps
|
|
3494
|
+
})) return;
|
|
3495
|
+
return await handleTeamsMessage(context, turnAdoptionLifecycle);
|
|
3763
3496
|
} catch (err) {
|
|
3764
|
-
|
|
3497
|
+
if (turnAdoptionLifecycle) throw err;
|
|
3498
|
+
deps.runtime.error(`msteams handler failed: ${require_channel_setup.formatUnknownError(err)}`);
|
|
3765
3499
|
}
|
|
3766
|
-
await
|
|
3767
|
-
|
|
3768
|
-
|
|
3500
|
+
else if (activity?.type === "conversationUpdate") await handleMembersAdded(context);
|
|
3501
|
+
else if (activity?.type === "messageReaction") for (const direction of ["added", "removed"]) {
|
|
3502
|
+
if (!(direction === "added" ? activity.reactionsAdded : activity.reactionsRemoved)?.length) continue;
|
|
3503
|
+
try {
|
|
3504
|
+
await handleReaction(context, direction);
|
|
3505
|
+
} catch (err) {
|
|
3506
|
+
deps.runtime.error(`msteams reaction handler failed: ${String(err)}`);
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
};
|
|
3769
3510
|
}
|
|
3770
3511
|
//#endregion
|
|
3771
3512
|
//#region extensions/msteams/src/feedback-invoke.ts
|
|
@@ -3892,14 +3633,14 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3892
3633
|
const expiredUploadMessage = "The file upload request has expired. Please try sending the file again.";
|
|
3893
3634
|
const activity = context.activity;
|
|
3894
3635
|
if (activity.type !== "invoke" || activity.name !== "fileConsent/invoke") return false;
|
|
3895
|
-
const consentResponse =
|
|
3636
|
+
const consentResponse = require_conversation_store_state.parseFileConsentInvoke(activity);
|
|
3896
3637
|
if (!consentResponse) {
|
|
3897
3638
|
log.debug?.("invalid file consent invoke", { value: activity.value });
|
|
3898
3639
|
return false;
|
|
3899
3640
|
}
|
|
3900
3641
|
const uploadId = typeof consentResponse.context?.uploadId === "string" ? consentResponse.context.uploadId : void 0;
|
|
3901
|
-
const inMemoryFile =
|
|
3902
|
-
const fsFile = inMemoryFile ? void 0 : await
|
|
3642
|
+
const inMemoryFile = require_conversation_store_state.getPendingUpload(uploadId);
|
|
3643
|
+
const fsFile = inMemoryFile ? void 0 : await require_conversation_store_state.getPendingUploadFs(uploadId);
|
|
3903
3644
|
const pendingFile = inMemoryFile ?? fsFile;
|
|
3904
3645
|
if (pendingFile) {
|
|
3905
3646
|
const pendingConversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(pendingFile.conversationId);
|
|
@@ -3922,33 +3663,29 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3922
3663
|
size: pendingFile.buffer.length
|
|
3923
3664
|
});
|
|
3924
3665
|
try {
|
|
3925
|
-
await
|
|
3666
|
+
await require_conversation_store_state.uploadToConsentUrl({
|
|
3926
3667
|
url: consentResponse.uploadInfo.uploadUrl,
|
|
3927
3668
|
buffer: pendingFile.buffer,
|
|
3928
3669
|
contentType: pendingFile.contentType
|
|
3929
3670
|
});
|
|
3930
|
-
const
|
|
3931
|
-
filename: consentResponse.uploadInfo.name,
|
|
3932
|
-
contentUrl: consentResponse.uploadInfo.contentUrl,
|
|
3933
|
-
uniqueId: consentResponse.uploadInfo.uniqueId,
|
|
3934
|
-
fileType: consentResponse.uploadInfo.fileType
|
|
3935
|
-
});
|
|
3936
|
-
if (!pendingFile.consentCardActivityId) await context.sendActivity({
|
|
3671
|
+
const fileActivity = {
|
|
3937
3672
|
type: "message",
|
|
3938
|
-
attachments: [
|
|
3939
|
-
|
|
3673
|
+
attachments: [require_conversation_store_state.buildFileInfoCard({
|
|
3674
|
+
filename: consentResponse.uploadInfo.name,
|
|
3675
|
+
contentUrl: consentResponse.uploadInfo.contentUrl,
|
|
3676
|
+
uniqueId: consentResponse.uploadInfo.uniqueId,
|
|
3677
|
+
fileType: consentResponse.uploadInfo.fileType
|
|
3678
|
+
})]
|
|
3679
|
+
};
|
|
3940
3680
|
if (pendingFile.consentCardActivityId) try {
|
|
3941
3681
|
await context.updateActivity({
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
attachments: [fileInfoCard]
|
|
3682
|
+
...fileActivity,
|
|
3683
|
+
id: pendingFile.consentCardActivityId
|
|
3945
3684
|
});
|
|
3946
3685
|
} catch {
|
|
3947
|
-
await context.sendActivity(
|
|
3948
|
-
type: "message",
|
|
3949
|
-
attachments: [fileInfoCard]
|
|
3950
|
-
});
|
|
3686
|
+
await context.sendActivity(fileActivity);
|
|
3951
3687
|
}
|
|
3688
|
+
else await context.sendActivity(fileActivity);
|
|
3952
3689
|
log.info("file upload complete", {
|
|
3953
3690
|
uploadId,
|
|
3954
3691
|
filename: consentResponse.uploadInfo.name,
|
|
@@ -3961,8 +3698,8 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3961
3698
|
});
|
|
3962
3699
|
await context.sendActivity("File upload failed. Please try again.");
|
|
3963
3700
|
} finally {
|
|
3964
|
-
|
|
3965
|
-
await
|
|
3701
|
+
require_conversation_store_state.removePendingUpload(uploadId);
|
|
3702
|
+
await require_conversation_store_state.removePendingUploadFs(uploadId);
|
|
3966
3703
|
}
|
|
3967
3704
|
} else {
|
|
3968
3705
|
log.debug?.("pending file not found for consent", { uploadId });
|
|
@@ -3970,8 +3707,8 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3970
3707
|
}
|
|
3971
3708
|
} else {
|
|
3972
3709
|
log.debug?.("user declined file consent", { uploadId });
|
|
3973
|
-
|
|
3974
|
-
await
|
|
3710
|
+
require_conversation_store_state.removePendingUpload(uploadId);
|
|
3711
|
+
await require_conversation_store_state.removePendingUploadFs(uploadId);
|
|
3975
3712
|
}
|
|
3976
3713
|
return true;
|
|
3977
3714
|
}
|
|
@@ -3982,7 +3719,7 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3982
3719
|
*/
|
|
3983
3720
|
async function runMSTeamsFileConsentInvokeHandler(context, log) {
|
|
3984
3721
|
try {
|
|
3985
|
-
await
|
|
3722
|
+
await require_conversation_store_state.withRevokedProxyFallback({
|
|
3986
3723
|
run: async () => await handleMSTeamsFileConsentInvoke(context, log),
|
|
3987
3724
|
onRevoked: async () => true,
|
|
3988
3725
|
onRevokedLog: () => {
|
|
@@ -4001,13 +3738,6 @@ function publishMSTeamsBlocked(statusSink, lastError) {
|
|
|
4001
3738
|
function publishMSTeamsReady(statusSink, now = Date.now()) {
|
|
4002
3739
|
statusSink?.((0, openclaw_plugin_sdk_gateway_runtime.channelReadyPatch)({ lastConnectedAt: now }));
|
|
4003
3740
|
}
|
|
4004
|
-
function publishMSTeamsRecovering(statusSink, lastError) {
|
|
4005
|
-
statusSink?.({
|
|
4006
|
-
connected: false,
|
|
4007
|
-
lifecycle: "recovering",
|
|
4008
|
-
lastError
|
|
4009
|
-
});
|
|
4010
|
-
}
|
|
4011
3741
|
function publishMSTeamsStopped(statusSink) {
|
|
4012
3742
|
statusSink?.((0, openclaw_plugin_sdk_gateway_runtime.channelStoppedPatch)());
|
|
4013
3743
|
}
|
|
@@ -4177,7 +3907,7 @@ function createMSTeamsReplayContext(activity, app, serviceUrlBoundary) {
|
|
|
4177
3907
|
...threadActivityId ? { threadActivityId } : {},
|
|
4178
3908
|
serviceUrlBoundary
|
|
4179
3909
|
};
|
|
4180
|
-
const sendActivity = (outbound) =>
|
|
3910
|
+
const sendActivity = (outbound) => require_conversation_store_state.sendMSTeamsActivityWithReference(app, reference, outbound, proactiveOptions);
|
|
4181
3911
|
return {
|
|
4182
3912
|
activity,
|
|
4183
3913
|
sendActivity,
|
|
@@ -4187,26 +3917,108 @@ function createMSTeamsReplayContext(activity, app, serviceUrlBoundary) {
|
|
|
4187
3917
|
return results;
|
|
4188
3918
|
},
|
|
4189
3919
|
updateActivity: async (outbound) => {
|
|
4190
|
-
return await
|
|
3920
|
+
return await require_conversation_store_state.updateMSTeamsActivityWithReference(app, reference, typeof outbound.id === "string" ? outbound.id : "", outbound, proactiveOptions);
|
|
4191
3921
|
},
|
|
4192
3922
|
deleteActivity: async (activityId) => {
|
|
4193
|
-
await
|
|
3923
|
+
await require_conversation_store_state.deleteMSTeamsActivityWithReference(app, reference, activityId, proactiveOptions);
|
|
4194
3924
|
},
|
|
4195
3925
|
getTeamDetails: (teamId) => app.api.teams.getById(teamId)
|
|
4196
3926
|
};
|
|
4197
3927
|
}
|
|
4198
3928
|
//#endregion
|
|
4199
|
-
//#region extensions/msteams/src/
|
|
4200
|
-
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
3929
|
+
//#region extensions/msteams/src/sso-token-store.ts
|
|
3930
|
+
/**
|
|
3931
|
+
* SQLite-backed store for Bot Framework OAuth SSO tokens.
|
|
3932
|
+
*
|
|
3933
|
+
* Tokens are keyed by (connectionName, userId). `userId` should be the
|
|
3934
|
+
* stable AAD object ID (`activity.from.aadObjectId`) when available,
|
|
3935
|
+
* falling back to the Bot Framework `activity.from.id`.
|
|
3936
|
+
*
|
|
3937
|
+
* The store is intentionally minimal: it persists the exchanged user
|
|
3938
|
+
* token plus its expiration so consumers (for example tool handlers
|
|
3939
|
+
* that call Microsoft Graph with delegated permissions) can fetch a
|
|
3940
|
+
* valid token without reaching back into Bot Framework every turn.
|
|
3941
|
+
*/
|
|
3942
|
+
const MSTEAMS_SSO_TOKENS_NAMESPACE = "sso-tokens";
|
|
3943
|
+
const SSO_TOKEN_MUTATION_KEY = "sso-tokens";
|
|
3944
|
+
const MSTEAMS_MAX_SSO_TOKENS = 5e3;
|
|
3945
|
+
const STORE_KEY_VERSION_PREFIX = "v2:";
|
|
3946
|
+
function makeMSTeamsSsoTokenStoreKey(connectionName, userId) {
|
|
3947
|
+
return `${STORE_KEY_VERSION_PREFIX}${(0, node_crypto.createHash)("sha256").update(JSON.stringify([connectionName, userId])).digest("hex")}`;
|
|
3948
|
+
}
|
|
3949
|
+
function createTokenStore(params) {
|
|
3950
|
+
return require_runtime.getMSTeamsRuntime().state.openKeyedStore({
|
|
3951
|
+
namespace: MSTEAMS_SSO_TOKENS_NAMESPACE,
|
|
3952
|
+
maxEntries: MSTEAMS_MAX_SSO_TOKENS,
|
|
3953
|
+
env: require_conversation_store_state.resolveMSTeamsSqliteStateEnv(params)
|
|
3954
|
+
});
|
|
3955
|
+
}
|
|
3956
|
+
function createMSTeamsSsoTokenStoreFs(params) {
|
|
3957
|
+
const tokenStore = createTokenStore(params);
|
|
3958
|
+
return {
|
|
3959
|
+
async get({ connectionName, userId }) {
|
|
3960
|
+
return await tokenStore.lookup(makeMSTeamsSsoTokenStoreKey(connectionName, userId)) ?? null;
|
|
3961
|
+
},
|
|
3962
|
+
async save(token) {
|
|
3963
|
+
await require_conversation_store_state.withMSTeamsSqliteMutationLock(params, SSO_TOKEN_MUTATION_KEY, async () => {
|
|
3964
|
+
await tokenStore.register(makeMSTeamsSsoTokenStoreKey(token.connectionName, token.userId), require_conversation_store_state.toPluginJsonValue({ ...token }));
|
|
3965
|
+
});
|
|
3966
|
+
},
|
|
3967
|
+
async remove({ connectionName, userId }) {
|
|
3968
|
+
let removed = false;
|
|
3969
|
+
await require_conversation_store_state.withMSTeamsSqliteMutationLock(params, SSO_TOKEN_MUTATION_KEY, async () => {
|
|
3970
|
+
removed = await tokenStore.delete(makeMSTeamsSsoTokenStoreKey(connectionName, userId));
|
|
3971
|
+
});
|
|
3972
|
+
return removed;
|
|
3973
|
+
}
|
|
3974
|
+
};
|
|
3975
|
+
}
|
|
3976
|
+
//#endregion
|
|
3977
|
+
//#region extensions/msteams/src/webhook-handler.ts
|
|
3978
|
+
/** Keep Express response ownership until the channel's route can be released. */
|
|
3979
|
+
function createMSTeamsWebhookHandler(expressApp, warn) {
|
|
3980
|
+
const responses = /* @__PURE__ */ new Map();
|
|
3981
|
+
let closing = false;
|
|
3982
|
+
let closeTask;
|
|
3983
|
+
return {
|
|
3984
|
+
handler: ((req, res) => {
|
|
3985
|
+
if (closing) {
|
|
3986
|
+
res.writeHead(503, { "Retry-After": "1" });
|
|
3987
|
+
res.end("Service Unavailable");
|
|
3988
|
+
return Promise.resolve();
|
|
3989
|
+
}
|
|
3990
|
+
const response = (0, openclaw_plugin_sdk_extension_shared.createDeferred)();
|
|
3991
|
+
responses.set(res, response.promise);
|
|
3992
|
+
const done = () => {
|
|
3993
|
+
res.off("finish", done);
|
|
3994
|
+
res.off("close", done);
|
|
3995
|
+
responses.delete(res);
|
|
3996
|
+
response.resolve();
|
|
3997
|
+
};
|
|
3998
|
+
res.once("finish", done);
|
|
3999
|
+
res.once("close", done);
|
|
4000
|
+
try {
|
|
4001
|
+
expressApp(req, res);
|
|
4002
|
+
} catch (error) {
|
|
4003
|
+
done();
|
|
4004
|
+
throw error;
|
|
4005
|
+
}
|
|
4006
|
+
return response.promise;
|
|
4007
|
+
}),
|
|
4008
|
+
close: () => {
|
|
4009
|
+
closing = true;
|
|
4010
|
+
closeTask ??= (async () => {
|
|
4011
|
+
if (responses.size === 0) return;
|
|
4012
|
+
try {
|
|
4013
|
+
await (0, openclaw_plugin_sdk_time_runtime.withTimeout)(Promise.all(responses.values()), require_resolve_allowlist.MSTEAMS_REQUEST_TIMEOUT_MS, "Microsoft Teams webhook shutdown");
|
|
4014
|
+
} catch {
|
|
4015
|
+
warn("Microsoft Teams webhook shutdown exceeded 30 seconds; closing active responses");
|
|
4016
|
+
for (const response of responses.keys()) response.destroy();
|
|
4017
|
+
}
|
|
4018
|
+
})();
|
|
4019
|
+
return closeTask;
|
|
4020
|
+
}
|
|
4021
|
+
};
|
|
4210
4022
|
}
|
|
4211
4023
|
//#endregion
|
|
4212
4024
|
//#region extensions/msteams/src/monitor.ts
|
|
@@ -4320,20 +4132,26 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4320
4132
|
msteams: msteamsCfg
|
|
4321
4133
|
}
|
|
4322
4134
|
};
|
|
4323
|
-
const
|
|
4135
|
+
const legacyListener = require_channel.resolveMSTeamsLegacyWebhook(msteamsCfg);
|
|
4136
|
+
const pathIssue = require_channel.resolveMSTeamsWebhookPathIssue({ cfg });
|
|
4137
|
+
if (pathIssue) {
|
|
4138
|
+
if (!legacyListener) throw new Error(pathIssue);
|
|
4139
|
+
log.warn?.(pathIssue);
|
|
4140
|
+
}
|
|
4324
4141
|
const textLimit = core.channel.text.resolveTextChunkLimit(cfg, "msteams");
|
|
4325
4142
|
const mediaMaxBytes = (0, openclaw_plugin_sdk_account_helpers.resolveChannelMediaMaxBytes)({
|
|
4326
4143
|
cfg,
|
|
4327
4144
|
resolveChannelLimitMb: ({ cfg: channelCfg }) => channelCfg.channels?.msteams?.mediaMaxMb
|
|
4328
4145
|
}) ?? 8388608;
|
|
4329
|
-
const conversationStore = opts.conversationStore ??
|
|
4330
|
-
const pollStore = opts.pollStore ??
|
|
4331
|
-
log.info(
|
|
4146
|
+
const conversationStore = opts.conversationStore ?? require_conversation_store_state.createMSTeamsConversationStoreState();
|
|
4147
|
+
const pollStore = opts.pollStore ?? require_conversation_store_state.createMSTeamsPollStoreState();
|
|
4148
|
+
log.info("starting provider on Gateway HTTP routes");
|
|
4332
4149
|
const express = await import("express");
|
|
4333
4150
|
const expressApp = express.default();
|
|
4151
|
+
const privateQaToken = await require_resolve_allowlist.resolveMSTeamsPrivateQaRuntime()?.token();
|
|
4334
4152
|
expressApp.use((req, res, next) => {
|
|
4335
4153
|
const auth = req.headers.authorization;
|
|
4336
|
-
if (!auth || !auth.startsWith("Bearer ")) {
|
|
4154
|
+
if (!auth || !auth.startsWith("Bearer ") || privateQaToken && !(0, openclaw_plugin_sdk_security_runtime.safeEqualSecret)(auth.slice(7), privateQaToken)) {
|
|
4337
4155
|
res.status(401).json({ error: "Unauthorized" });
|
|
4338
4156
|
return;
|
|
4339
4157
|
}
|
|
@@ -4347,7 +4165,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4347
4165
|
}
|
|
4348
4166
|
next(err);
|
|
4349
4167
|
});
|
|
4350
|
-
const configuredPath = msteamsCfg.webhook?.path
|
|
4168
|
+
const configuredPath = msteamsCfg.webhook?.path || "/api/messages";
|
|
4351
4169
|
const ssoConnectionName = msteamsCfg.sso?.enabled && msteamsCfg.sso.connectionName ? msteamsCfg.sso.connectionName : void 0;
|
|
4352
4170
|
const { app } = await require_resolve_allowlist.loadMSTeamsSdkWithAuth(creds, {
|
|
4353
4171
|
...require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg),
|
|
@@ -4368,11 +4186,10 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4368
4186
|
}
|
|
4369
4187
|
const tokenProvider = require_resolve_allowlist.createMSTeamsTokenProvider(app);
|
|
4370
4188
|
const ssoDeps = ssoConnectionName ? {
|
|
4371
|
-
tokenStore:
|
|
4189
|
+
tokenStore: createMSTeamsSsoTokenStoreFs(),
|
|
4372
4190
|
connectionName: ssoConnectionName
|
|
4373
4191
|
} : void 0;
|
|
4374
4192
|
if (ssoDeps) log.debug?.("msteams sso enabled", { connectionName: ssoDeps.connectionName });
|
|
4375
|
-
const handler = buildActivityHandler();
|
|
4376
4193
|
const handlerDeps = {
|
|
4377
4194
|
cfg,
|
|
4378
4195
|
runtime,
|
|
@@ -4385,37 +4202,29 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4385
4202
|
pollStore,
|
|
4386
4203
|
log
|
|
4387
4204
|
};
|
|
4388
|
-
|
|
4205
|
+
const handleActivity = createMSTeamsActivityHandler(handlerDeps);
|
|
4389
4206
|
const ingress = createMSTeamsIngress({
|
|
4390
4207
|
accountId: appId,
|
|
4391
4208
|
runtime,
|
|
4392
4209
|
dispatch: async (activity, lifecycle, liveContext) => {
|
|
4393
4210
|
if (liveContext) liveContext.activity = activity;
|
|
4394
4211
|
const context = liveContext ?? createMSTeamsReplayContext(activity, app, require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg));
|
|
4395
|
-
return await
|
|
4212
|
+
return await handleActivity(context, lifecycle);
|
|
4396
4213
|
}
|
|
4397
4214
|
});
|
|
4398
4215
|
app.on("card.action", async (ctx) => {
|
|
4399
4216
|
const adaptedCtx = adaptSdkContext(ctx, app);
|
|
4400
4217
|
try {
|
|
4401
4218
|
const activity = adaptedCtx.activity;
|
|
4402
|
-
const vote =
|
|
4219
|
+
const vote = require_conversation_store_state.extractMSTeamsPollVote(activity);
|
|
4403
4220
|
if (vote) {
|
|
4404
4221
|
const voterId = activity?.from?.aadObjectId ?? activity?.from?.id ?? "unknown";
|
|
4405
4222
|
try {
|
|
4406
|
-
if (!await isCardActionInvokeAuthorized(adaptedCtx, handlerDeps)) return
|
|
4407
|
-
statusCode: 200,
|
|
4408
|
-
type: "application/vnd.microsoft.activity.message",
|
|
4409
|
-
value: "Not authorized."
|
|
4410
|
-
};
|
|
4223
|
+
if (!await isCardActionInvokeAuthorized(adaptedCtx, handlerDeps)) return cardActionMessage("Not authorized.");
|
|
4411
4224
|
const existingPoll = await pollStore.getPoll(vote.pollId);
|
|
4412
4225
|
if (!existingPoll) {
|
|
4413
4226
|
log.debug?.("poll vote ignored (poll not found)", { pollId: vote.pollId });
|
|
4414
|
-
return
|
|
4415
|
-
statusCode: 200,
|
|
4416
|
-
type: "application/vnd.microsoft.activity.message",
|
|
4417
|
-
value: "Poll not found."
|
|
4418
|
-
};
|
|
4227
|
+
return cardActionMessage("Poll not found.");
|
|
4419
4228
|
}
|
|
4420
4229
|
const pollConversationId = existingPoll.conversationId ? require_resolve_allowlist.normalizeMSTeamsConversationId(existingPoll.conversationId) : void 0;
|
|
4421
4230
|
const activityConversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(activity?.conversation?.id ?? "");
|
|
@@ -4425,11 +4234,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4425
4234
|
expectedConversationId: pollConversationId,
|
|
4426
4235
|
receivedConversationId: activityConversationId || void 0
|
|
4427
4236
|
});
|
|
4428
|
-
return
|
|
4429
|
-
statusCode: 200,
|
|
4430
|
-
type: "application/vnd.microsoft.activity.message",
|
|
4431
|
-
value: "Poll not found."
|
|
4432
|
-
};
|
|
4237
|
+
return cardActionMessage("Poll not found.");
|
|
4433
4238
|
}
|
|
4434
4239
|
if (await pollStore.recordVote({
|
|
4435
4240
|
pollId: vote.pollId,
|
|
@@ -4440,18 +4245,10 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4440
4245
|
pollId: vote.pollId,
|
|
4441
4246
|
voterId
|
|
4442
4247
|
});
|
|
4443
|
-
return
|
|
4444
|
-
statusCode: 200,
|
|
4445
|
-
type: "application/vnd.microsoft.activity.message",
|
|
4446
|
-
value: "Vote recorded."
|
|
4447
|
-
};
|
|
4248
|
+
return cardActionMessage("Vote recorded.");
|
|
4448
4249
|
}
|
|
4449
4250
|
log.debug?.("poll vote ignored (poll not found)", { pollId: vote.pollId });
|
|
4450
|
-
return
|
|
4451
|
-
statusCode: 200,
|
|
4452
|
-
type: "application/vnd.microsoft.activity.message",
|
|
4453
|
-
value: "Poll not found."
|
|
4454
|
-
};
|
|
4251
|
+
return cardActionMessage("Poll not found.");
|
|
4455
4252
|
} catch (err) {
|
|
4456
4253
|
log.error("failed to record poll vote", {
|
|
4457
4254
|
pollId: vote.pollId,
|
|
@@ -4472,11 +4269,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4472
4269
|
}
|
|
4473
4270
|
}
|
|
4474
4271
|
await ingress.accept(activity, adaptedCtx);
|
|
4475
|
-
return
|
|
4476
|
-
statusCode: 200,
|
|
4477
|
-
type: "application/vnd.microsoft.activity.message",
|
|
4478
|
-
value: "OK"
|
|
4479
|
-
};
|
|
4272
|
+
return cardActionMessage("OK");
|
|
4480
4273
|
} catch (err) {
|
|
4481
4274
|
log.error("msteams card.action failed", { error: require_channel_setup.formatUnknownError(err) });
|
|
4482
4275
|
return {
|
|
@@ -4499,26 +4292,27 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4499
4292
|
app.on("file.consent.decline", (ctx) => {
|
|
4500
4293
|
runMSTeamsFileConsentInvokeHandler(adaptSdkContext(ctx, app), log);
|
|
4501
4294
|
});
|
|
4502
|
-
const
|
|
4503
|
-
|
|
4504
|
-
|
|
4295
|
+
const processActivity = app.process.bind(app);
|
|
4296
|
+
app.process = async (event) => {
|
|
4297
|
+
const activity = event.body;
|
|
4298
|
+
if (activity.type !== "invoke" || !("name" in activity) || activity.name !== "signin/tokenExchange" && activity.name !== "signin/verifyState") return processActivity(event);
|
|
4299
|
+
if (!await isSigninInvokeAuthorized({ activity: {
|
|
4300
|
+
...activity,
|
|
4301
|
+
type: activity.type,
|
|
4302
|
+
name: activity.name
|
|
4303
|
+
} }, handlerDeps)) return {
|
|
4505
4304
|
status: 200,
|
|
4506
4305
|
body: {}
|
|
4507
4306
|
};
|
|
4508
4307
|
if (!ssoDeps) {
|
|
4509
|
-
log.debug?.("signin invoke received but msteams.sso is not configured", { name:
|
|
4308
|
+
log.debug?.("signin invoke received but msteams.sso is not configured", { name: activity.name });
|
|
4510
4309
|
return {
|
|
4511
4310
|
status: 200,
|
|
4512
4311
|
body: {}
|
|
4513
4312
|
};
|
|
4514
4313
|
}
|
|
4515
|
-
|
|
4516
|
-
const delegate = sdkSigninApp[delegateName];
|
|
4517
|
-
if (typeof delegate !== "function") throw new Error(`Teams SDK ${delegateName} handler is unavailable`);
|
|
4518
|
-
return delegate.call(sdkSigninApp, ctx);
|
|
4314
|
+
return processActivity(event);
|
|
4519
4315
|
};
|
|
4520
|
-
app.on("signin.token-exchange", (ctx) => handleSdkSigninInvoke(ctx, "onTokenExchange"));
|
|
4521
|
-
app.on("signin.verify-state", (ctx) => handleSdkSigninInvoke(ctx, "onVerifyState"));
|
|
4522
4316
|
if (ssoDeps) app.event("signin", (ctx) => {
|
|
4523
4317
|
(async () => {
|
|
4524
4318
|
if (!await isSigninInvokeAuthorized(adaptSdkContext(ctx, app), handlerDeps)) return;
|
|
@@ -4565,93 +4359,76 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4565
4359
|
return;
|
|
4566
4360
|
}
|
|
4567
4361
|
try {
|
|
4568
|
-
await
|
|
4362
|
+
await handleActivity(adaptedCtx);
|
|
4569
4363
|
} catch (err) {
|
|
4570
4364
|
log.error("msteams non-turn activity failed", { error: require_channel_setup.formatUnknownError(err) });
|
|
4571
4365
|
}
|
|
4572
4366
|
});
|
|
4573
4367
|
await app.initialize();
|
|
4574
4368
|
ingress.start();
|
|
4575
|
-
const
|
|
4576
|
-
const
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4369
|
+
const unregisterRoutes = [];
|
|
4370
|
+
const webhook = createMSTeamsWebhookHandler(expressApp, (message) => log.warn?.(message));
|
|
4371
|
+
try {
|
|
4372
|
+
unregisterRoutes.push((0, openclaw_plugin_sdk_webhook_targets.registerPluginHttpRoute)({
|
|
4373
|
+
path: configuredPath,
|
|
4374
|
+
auth: "plugin",
|
|
4375
|
+
pluginId: "msteams",
|
|
4376
|
+
source: "msteams-webhook",
|
|
4377
|
+
accountId: appId,
|
|
4378
|
+
handler: webhook.handler,
|
|
4379
|
+
legacyListener: legacyListener ? {
|
|
4380
|
+
...legacyListener,
|
|
4381
|
+
timeouts: {
|
|
4382
|
+
headers: 15e3,
|
|
4383
|
+
request: 3e4,
|
|
4384
|
+
socket: 3e4
|
|
4385
|
+
}
|
|
4386
|
+
} : void 0,
|
|
4387
|
+
throwOnFailure: true,
|
|
4388
|
+
log: (message) => log.warn?.(message)
|
|
4389
|
+
}));
|
|
4390
|
+
if (configuredPath !== "/api/messages") unregisterRoutes.push((0, openclaw_plugin_sdk_webhook_targets.registerPluginHttpRoute)({
|
|
4391
|
+
path: "/api/messages",
|
|
4392
|
+
auth: "plugin",
|
|
4393
|
+
pluginId: "msteams",
|
|
4394
|
+
source: "msteams-webhook-alias",
|
|
4395
|
+
accountId: appId,
|
|
4396
|
+
handler: webhook.handler,
|
|
4397
|
+
log: (message) => log.warn?.(message)
|
|
4398
|
+
}));
|
|
4399
|
+
} catch (error) {
|
|
4400
|
+
for (const unregister of unregisterRoutes) unregister();
|
|
4581
4401
|
await ingress.stop();
|
|
4582
|
-
throw
|
|
4583
|
-
}
|
|
4584
|
-
log.info(`msteams provider started on
|
|
4402
|
+
throw error;
|
|
4403
|
+
}
|
|
4404
|
+
log.info(`msteams provider started on Gateway route ${configuredPath}`);
|
|
4585
4405
|
publishMSTeamsReady(opts.statusSink);
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
if (err) log.debug?.("msteams server close error", { error: require_channel_setup.formatUnknownError(err) });
|
|
4596
|
-
resolve();
|
|
4597
|
-
});
|
|
4598
|
-
});
|
|
4599
|
-
await ingress.stop();
|
|
4600
|
-
publishMSTeamsStopped(opts.statusSink);
|
|
4406
|
+
let shutdownTask;
|
|
4407
|
+
const shutdown = () => {
|
|
4408
|
+
shutdownTask ??= (async () => {
|
|
4409
|
+
await webhook.close();
|
|
4410
|
+
for (const unregister of unregisterRoutes.splice(0)) unregister();
|
|
4411
|
+
await ingress.stop();
|
|
4412
|
+
publishMSTeamsStopped(opts.statusSink);
|
|
4413
|
+
})();
|
|
4414
|
+
return shutdownTask;
|
|
4601
4415
|
};
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
}
|
|
4416
|
+
try {
|
|
4417
|
+
await (0, openclaw_plugin_sdk_channel_outbound.waitUntilAbort)(opts.abortSignal, shutdown);
|
|
4418
|
+
} finally {
|
|
4419
|
+
await shutdown();
|
|
4420
|
+
}
|
|
4607
4421
|
return {
|
|
4608
4422
|
app: expressApp,
|
|
4609
4423
|
shutdown
|
|
4610
4424
|
};
|
|
4611
4425
|
}
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
const messageHandlers = [];
|
|
4618
|
-
const membersAddedHandlers = [];
|
|
4619
|
-
const reactionsAddedHandlers = [];
|
|
4620
|
-
const reactionsRemovedHandlers = [];
|
|
4621
|
-
const handler = {
|
|
4622
|
-
onMessage(cb) {
|
|
4623
|
-
messageHandlers.push(cb);
|
|
4624
|
-
return handler;
|
|
4625
|
-
},
|
|
4626
|
-
onMembersAdded(cb) {
|
|
4627
|
-
membersAddedHandlers.push(cb);
|
|
4628
|
-
return handler;
|
|
4629
|
-
},
|
|
4630
|
-
onReactionsAdded(cb) {
|
|
4631
|
-
reactionsAddedHandlers.push(cb);
|
|
4632
|
-
return handler;
|
|
4633
|
-
},
|
|
4634
|
-
onReactionsRemoved(cb) {
|
|
4635
|
-
reactionsRemovedHandlers.push(cb);
|
|
4636
|
-
return handler;
|
|
4637
|
-
},
|
|
4638
|
-
async run(context, turnAdoptionLifecycle) {
|
|
4639
|
-
const ctx = context;
|
|
4640
|
-
const activityType = ctx?.activity?.type;
|
|
4641
|
-
const noop = async () => {};
|
|
4642
|
-
if (activityType === "message") for (const h of messageHandlers) {
|
|
4643
|
-
const result = await h(context, noop, turnAdoptionLifecycle);
|
|
4644
|
-
if (result) return result;
|
|
4645
|
-
}
|
|
4646
|
-
else if (activityType === "conversationUpdate") for (const h of membersAddedHandlers) await h(context, noop);
|
|
4647
|
-
else if (activityType === "messageReaction") {
|
|
4648
|
-
const activity = ctx?.activity;
|
|
4649
|
-
if (activity?.reactionsAdded?.length) for (const h of reactionsAddedHandlers) await h(context, noop);
|
|
4650
|
-
if (activity?.reactionsRemoved?.length) for (const h of reactionsRemovedHandlers) await h(context, noop);
|
|
4651
|
-
}
|
|
4652
|
-
}
|
|
4426
|
+
function cardActionMessage(value) {
|
|
4427
|
+
return {
|
|
4428
|
+
statusCode: 200,
|
|
4429
|
+
type: "application/vnd.microsoft.activity.message",
|
|
4430
|
+
value
|
|
4653
4431
|
};
|
|
4654
|
-
return handler;
|
|
4655
4432
|
}
|
|
4656
4433
|
/**
|
|
4657
4434
|
* Adapt a new @microsoft/teams.apps SDK context to the MSTeamsTurnContext interface
|