@openclaw/msteams 2026.10.5-beta.4 → 2026.10.10-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.setup/{adapter.runtime-JoPydWqP.cjs → adapter.runtime-B-6Q3nMI.cjs} +6 -4
- package/dist/.setup/{approval-handler.runtime-bUN9icMr.cjs → approval-handler.runtime-DccVf5AY.cjs} +16 -6
- package/dist/.setup/{bot-framework-service-url-CPp4d-Q6.cjs → bot-framework-service-url-B3FWn2SV.cjs} +13 -0
- package/dist/.setup/{channel-Cifn9fsv.cjs → channel-B7Gy3QUW.cjs} +261 -175
- package/dist/.setup/{channel.runtime-DWRAolil.cjs → channel.runtime-DzKeQAqd.cjs} +84 -49
- package/dist/.setup/channel.setup-B5ClEcNI.cjs +990 -0
- package/dist/.setup/config-schema-CkKdphSp.cjs +293 -0
- package/dist/.setup/{conversation-store-state-DprmOfl7.cjs → conversation-store-state-C3__T9mK.cjs} +79 -91
- package/dist/.setup/delegated-state-Bv5cWuec.cjs +110 -0
- package/dist/.setup/{monitor-C9x_CJhY.cjs → monitor-CzRI-Hsc.cjs} +283 -240
- package/dist/.setup/{oauth-CWJ85AMw.cjs → oauth-D9u8sPCD.cjs} +1 -1
- package/dist/.setup/{reaction-types-Ig9sQJuR.cjs → reaction-types-DSQWVA4Z.cjs} +15 -9
- package/dist/.setup/{resolve-allowlist-DdDBVCzl.cjs → resolve-allowlist-tYgAHx0t.cjs} +252 -38
- package/dist/.setup/{secret-contract-api-ChQdBeU1.cjs → secret-contract-api-YY3v6MP8.cjs} +1 -1
- package/dist/.setup/secret-contract-api-wAyY5UHv.cjs +85 -0
- package/dist/.setup/{send-rJpcVtRp.cjs → send-Bgm8RYUa.cjs} +45 -24
- package/dist/.setup/{setup-plugin-api-DfoVOqMJ.cjs → setup-plugin-api-BACfeF7_.cjs} +1 -1
- package/dist/.setup/{thread-session-B74FC1Id.cjs → thread-session-C7KTMwLU.cjs} +11 -4
- package/dist/.setup/token-config-bEpua_Q7.cjs +93 -0
- package/dist/api.cjs +2 -2
- package/dist/channel-config-api.cjs +1 -1
- package/dist/channel-plugin-api.cjs +1 -1
- package/dist/directory-contract-api.cjs +30 -22
- package/dist/doctor-contract-api.cjs +11 -10
- package/dist/qa-runner-api.cjs +1 -1
- package/dist/runtime-api.cjs +2 -2
- package/dist/secret-contract-api.cjs +1 -1
- package/dist/setup-entry.cjs +2 -2
- package/dist/setup-plugin-api.cjs +1 -1
- package/openclaw.plugin.json +715 -0
- package/package.json +57 -6
- package/dist/.setup/channel.setup-BjbHugkL.cjs +0 -632
- package/dist/.setup/config-schema-Dk0mRIam.cjs +0 -160
- package/dist/.setup/secret-contract-api-ipiUsANg.cjs +0 -28
- package/dist/.setup/token-config-CqIh8z7Q.cjs +0 -138
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-BloeesfJ.cjs");
|
|
2
|
+
const require_bot_framework_service_url = require("./bot-framework-service-url-B3FWn2SV.cjs");
|
|
3
|
+
const require_resolve_allowlist = require("./resolve-allowlist-tYgAHx0t.cjs");
|
|
2
4
|
const require_runtime = require("./runtime-gZTAuFux.cjs");
|
|
3
5
|
require("../runtime-api.cjs");
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const require_thread_session = require("./thread-session-B74FC1Id.cjs");
|
|
9
|
-
const require_channel_setup = require("./channel.setup-BjbHugkL.cjs");
|
|
6
|
+
const require_delegated_state = require("./delegated-state-Bv5cWuec.cjs");
|
|
7
|
+
const require_channel = require("./channel-B7Gy3QUW.cjs");
|
|
8
|
+
const require_thread_session = require("./thread-session-C7KTMwLU.cjs");
|
|
9
|
+
const require_channel_setup = require("./channel.setup-B5ClEcNI.cjs");
|
|
10
10
|
const require_approval_card = require("./approval-card-DWPDItvq.cjs");
|
|
11
|
-
const require_conversation_store_state = require("./conversation-store-state-
|
|
12
|
-
const require_reaction_types = require("./reaction-types-
|
|
11
|
+
const require_conversation_store_state = require("./conversation-store-state-C3__T9mK.cjs");
|
|
12
|
+
const require_reaction_types = require("./reaction-types-DSQWVA4Z.cjs");
|
|
13
13
|
let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
|
|
14
14
|
let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
|
|
15
|
+
let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
|
|
15
16
|
let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
|
|
16
17
|
let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-http");
|
|
17
|
-
let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
|
|
18
18
|
let openclaw_plugin_sdk_media_runtime = require("openclaw/plugin-sdk/media-runtime");
|
|
19
19
|
let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
|
|
20
20
|
let node_dns_promises = require("node:dns/promises");
|
|
@@ -22,12 +22,12 @@ let openclaw_plugin_sdk_ssrf_policy = require("openclaw/plugin-sdk/ssrf-policy")
|
|
|
22
22
|
let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-runtime");
|
|
23
23
|
let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text-utility-runtime");
|
|
24
24
|
let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
|
|
25
|
-
let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
|
|
26
|
-
let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
|
|
27
25
|
let node_crypto = require("node:crypto");
|
|
28
26
|
node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
|
|
29
|
-
let openclaw_plugin_sdk_security_runtime = require("openclaw/plugin-sdk/security-runtime");
|
|
30
27
|
let openclaw_plugin_sdk_webhook_targets = require("openclaw/plugin-sdk/webhook-targets");
|
|
28
|
+
let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
|
|
29
|
+
let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
|
|
30
|
+
let openclaw_plugin_sdk_security_runtime = require("openclaw/plugin-sdk/security-runtime");
|
|
31
31
|
let openclaw_plugin_sdk_approval_gateway_runtime = require("openclaw/plugin-sdk/approval-gateway-runtime");
|
|
32
32
|
let openclaw_plugin_sdk_channel_ingress_runtime = require("openclaw/plugin-sdk/channel-ingress-runtime");
|
|
33
33
|
let openclaw_plugin_sdk_context_visibility_runtime = require("openclaw/plugin-sdk/context-visibility-runtime");
|
|
@@ -86,7 +86,8 @@ async function storeSessionLearning(params) {
|
|
|
86
86
|
*/
|
|
87
87
|
async function runFeedbackReflection(params) {
|
|
88
88
|
const { cfg, log, sessionKey } = params;
|
|
89
|
-
const
|
|
89
|
+
const msteamsCfg = require_resolve_allowlist.resolveMSTeamsAccountConfig(cfg, params.accountId);
|
|
90
|
+
const cooldownMs = msteamsCfg.feedbackReflectionCooldownMs ?? openclaw_plugin_sdk_channel_inbound.DEFAULT_CHANNEL_FEEDBACK_REFLECTION_COOLDOWN_MS;
|
|
90
91
|
let reflection;
|
|
91
92
|
try {
|
|
92
93
|
reflection = await (0, openclaw_plugin_sdk_channel_inbound.runChannelFeedbackReflection)({
|
|
@@ -140,7 +141,10 @@ async function runFeedbackReflection(params) {
|
|
|
140
141
|
await require_conversation_store_state.sendMSTeamsActivityWithReference(params.app, require_conversation_store_state.buildConversationReference(params.conversationRef), {
|
|
141
142
|
type: "message",
|
|
142
143
|
text: reflection.userMessage
|
|
143
|
-
}, {
|
|
144
|
+
}, {
|
|
145
|
+
accountId: params.accountId,
|
|
146
|
+
serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg)
|
|
147
|
+
});
|
|
144
148
|
log.info("sent reflection follow-up", { sessionKey });
|
|
145
149
|
} catch (err) {
|
|
146
150
|
log.debug?.("failed to send reflection follow-up", { error: require_channel_setup.formatUnknownError(err) });
|
|
@@ -197,7 +201,7 @@ async function maybeHandleMSTeamsApprovalCardSubmit(params) {
|
|
|
197
201
|
ignored("unknown or expired card token");
|
|
198
202
|
return true;
|
|
199
203
|
}
|
|
200
|
-
if (binding.accountId !== openclaw_plugin_sdk_account_id.
|
|
204
|
+
if ((0, openclaw_plugin_sdk_account_id.normalizeAccountId)(binding.accountId) !== (0, openclaw_plugin_sdk_account_id.normalizeAccountId)(deps.accountId)) {
|
|
201
205
|
ignored("card token account mismatch");
|
|
202
206
|
return true;
|
|
203
207
|
}
|
|
@@ -213,10 +217,16 @@ async function maybeHandleMSTeamsApprovalCardSubmit(params) {
|
|
|
213
217
|
ignored("card token decision is no longer allowed");
|
|
214
218
|
return true;
|
|
215
219
|
}
|
|
220
|
+
const cfg = deps.readConfig?.() ?? deps.cfg;
|
|
221
|
+
const account = require_resolve_allowlist.resolveMSTeamsAccountConfig(cfg, binding.accountId);
|
|
222
|
+
if (account.appId && account.appId !== deps.appId) {
|
|
223
|
+
ignored("issuing account is no longer active");
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
216
226
|
const senderId = context.activity.from?.aadObjectId;
|
|
217
227
|
if (!(require_thread_session.msTeamsApprovalAuth.authorizeActorAction?.({
|
|
218
|
-
cfg
|
|
219
|
-
accountId:
|
|
228
|
+
cfg,
|
|
229
|
+
accountId: binding.accountId,
|
|
220
230
|
senderId,
|
|
221
231
|
action: "approve",
|
|
222
232
|
approvalKind: binding.approvalKind
|
|
@@ -226,12 +236,12 @@ async function maybeHandleMSTeamsApprovalCardSubmit(params) {
|
|
|
226
236
|
}
|
|
227
237
|
const outcome = await require_approval_card.msTeamsApprovalControls.settle(token, async (consumed) => {
|
|
228
238
|
const result = await (0, openclaw_plugin_sdk_approval_gateway_runtime.resolveApprovalOverGateway)({
|
|
229
|
-
cfg
|
|
239
|
+
cfg,
|
|
230
240
|
approvalId: consumed.approvalId,
|
|
231
241
|
approvalKind: consumed.approvalKind,
|
|
232
242
|
decision: consumed.decision,
|
|
233
243
|
channel: "msteams",
|
|
234
|
-
accountId:
|
|
244
|
+
accountId: consumed.accountId,
|
|
235
245
|
senderId
|
|
236
246
|
});
|
|
237
247
|
await context.updateActivity({
|
|
@@ -306,7 +316,8 @@ function formatMSTeamsSenderReason(params) {
|
|
|
306
316
|
}
|
|
307
317
|
async function resolveMSTeamsSenderAccess(params) {
|
|
308
318
|
const activity = params.activity;
|
|
309
|
-
const
|
|
319
|
+
const accountId = params.accountId ?? openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID;
|
|
320
|
+
const msteamsCfg = params.cfg.channels?.msteams ? require_resolve_allowlist.resolveMSTeamsAccountConfig(params.cfg, accountId) : void 0;
|
|
310
321
|
const conversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(activity.conversation?.id ?? "unknown");
|
|
311
322
|
const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(activity.conversation?.conversationType);
|
|
312
323
|
const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
|
|
@@ -317,7 +328,7 @@ async function resolveMSTeamsSenderAccess(params) {
|
|
|
317
328
|
const pairing = (0, openclaw_plugin_sdk_channel_pairing.createChannelPairingController)({
|
|
318
329
|
core,
|
|
319
330
|
channel: "msteams",
|
|
320
|
-
accountId
|
|
331
|
+
accountId
|
|
321
332
|
});
|
|
322
333
|
const dmPolicy = msteamsCfg?.dmPolicy ?? "pairing";
|
|
323
334
|
const configuredDmAllowFrom = msteamsCfg?.allowFrom ?? [];
|
|
@@ -418,6 +429,7 @@ async function admitMSTeamsMessage(params) {
|
|
|
418
429
|
const isControlCommand = allowTextCommands && core.channel.commands.isControlCommandMessage(params.text, params.cfg);
|
|
419
430
|
const access = await resolveMSTeamsSenderAccess({
|
|
420
431
|
cfg: params.cfg,
|
|
432
|
+
accountId: params.accountId,
|
|
421
433
|
activity: params.activity,
|
|
422
434
|
hasControlCommand: isControlCommand
|
|
423
435
|
});
|
|
@@ -543,6 +555,32 @@ function resolveBotThreadMentionPolicy(params) {
|
|
|
543
555
|
};
|
|
544
556
|
}
|
|
545
557
|
//#endregion
|
|
558
|
+
//#region extensions/msteams/src/monitor-handler/config.ts
|
|
559
|
+
function createMSTeamsHandlerConfigReader(deps) {
|
|
560
|
+
const source = deps.accountPolicyCfg ?? deps.cfg;
|
|
561
|
+
const read = (0, openclaw_plugin_sdk_runtime_config_snapshot.createRuntimeConfigReader)(source);
|
|
562
|
+
const originallyConfigured = require_resolve_allowlist.listMSTeamsAccountIds(source).includes(deps.accountId);
|
|
563
|
+
let lastSource;
|
|
564
|
+
let lastProjection = deps.cfg;
|
|
565
|
+
return () => {
|
|
566
|
+
const current = read();
|
|
567
|
+
if (current === lastSource) return lastProjection;
|
|
568
|
+
lastSource = current;
|
|
569
|
+
if (current === source && deps.accountPolicyCfg) return lastProjection = deps.cfg;
|
|
570
|
+
const retired = originallyConfigured && !require_resolve_allowlist.listMSTeamsAccountIds(current).includes(deps.accountId);
|
|
571
|
+
if (deps.accountId === "default" && !current.channels?.msteams?.accounts && !retired) return lastProjection = current;
|
|
572
|
+
const accountConfig = require_resolve_allowlist.resolveMSTeamsAccountConfig(current, deps.accountId);
|
|
573
|
+
return lastProjection = require_resolve_allowlist.withAccountScopedMSTeamsConfig({
|
|
574
|
+
cfg: current,
|
|
575
|
+
accountId: deps.accountId,
|
|
576
|
+
accountConfig: {
|
|
577
|
+
...accountConfig,
|
|
578
|
+
...current.channels?.msteams?.enabled === false || retired ? { enabled: false } : {}
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
//#endregion
|
|
546
584
|
//#region extensions/msteams/src/attachments/bot-framework.ts
|
|
547
585
|
const BOT_FRAMEWORK_SCOPE = "https://api.botframework.com";
|
|
548
586
|
/**
|
|
@@ -598,18 +636,6 @@ async function fetchBotFrameworkAttachmentInfo(params) {
|
|
|
598
636
|
async function saveBotFrameworkAttachmentView(params) {
|
|
599
637
|
const response = await fetchBotFrameworkAttachment(params, "attachmentView");
|
|
600
638
|
if (!response) return;
|
|
601
|
-
let contentLength;
|
|
602
|
-
try {
|
|
603
|
-
contentLength = (0, openclaw_plugin_sdk_media_runtime.parseMediaContentLength)(response.headers.get("content-length"));
|
|
604
|
-
} catch (err) {
|
|
605
|
-
await response.body?.cancel().catch(() => void 0);
|
|
606
|
-
params.logger?.warn?.("msteams botFramework attachmentView invalid content-length", { error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err) });
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
if (contentLength !== null && contentLength > params.maxBytes) {
|
|
610
|
-
await response.body?.cancel().catch(() => void 0);
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
639
|
try {
|
|
614
640
|
return await require_runtime.getMSTeamsRuntime().channel.media.saveResponseMedia(response, {
|
|
615
641
|
sourceUrl: params.url,
|
|
@@ -845,8 +871,9 @@ async function downloadAndStoreMSTeamsRemoteMedia(params) {
|
|
|
845
871
|
}
|
|
846
872
|
//#endregion
|
|
847
873
|
//#region extensions/msteams/src/attachments/download.ts
|
|
848
|
-
function resolveDownloadCandidate(att) {
|
|
874
|
+
function resolveDownloadCandidate(att, graphReferenceToken) {
|
|
849
875
|
const contentType = require_resolve_allowlist.normalizeContentType(att.contentType);
|
|
876
|
+
const graphReference = graphReferenceToken !== void 0 && contentType === "reference";
|
|
850
877
|
const name = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.name) ?? "";
|
|
851
878
|
if (contentType === "application/vnd.microsoft.teams.file.download.info") {
|
|
852
879
|
if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(att.content)) return null;
|
|
@@ -869,11 +896,11 @@ function resolveDownloadCandidate(att) {
|
|
|
869
896
|
sourceId: att.id?.trim() || void 0
|
|
870
897
|
};
|
|
871
898
|
}
|
|
872
|
-
const contentUrl = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.contentUrl) ?? "";
|
|
899
|
+
const contentUrl = graphReference ? att.contentUrl ?? "" : (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.contentUrl) ?? "";
|
|
873
900
|
if (!contentUrl) return null;
|
|
874
|
-
const sharesUrl = require_resolve_allowlist.tryBuildGraphSharesUrlForSharedLink(contentUrl);
|
|
901
|
+
const sharesUrl = graphReference ? `${require_resolve_allowlist.GRAPH_ROOT}/shares/${require_resolve_allowlist.encodeGraphShareId(contentUrl)}/driveItem/content` : require_resolve_allowlist.tryBuildGraphSharesUrlForSharedLink(contentUrl);
|
|
875
902
|
const resolvedUrl = sharesUrl ?? contentUrl;
|
|
876
|
-
const resolvedContentTypeHint = sharesUrl ? void 0 : contentType;
|
|
903
|
+
const resolvedContentTypeHint = graphReference ? "application/octet-stream" : sharesUrl ? void 0 : contentType;
|
|
877
904
|
return {
|
|
878
905
|
kind: "remote",
|
|
879
906
|
mediaKind: require_resolve_allowlist.resolveMSTeamsMediaKind({
|
|
@@ -881,9 +908,10 @@ function resolveDownloadCandidate(att) {
|
|
|
881
908
|
fileName: name
|
|
882
909
|
}),
|
|
883
910
|
url: resolvedUrl,
|
|
884
|
-
fileHint: name || void 0,
|
|
911
|
+
fileHint: graphReference ? att.name ?? void 0 : name || void 0,
|
|
885
912
|
contentTypeHint: resolvedContentTypeHint,
|
|
886
|
-
sourceId: att.id?.trim() || void 0
|
|
913
|
+
sourceId: att.id?.trim() || void 0,
|
|
914
|
+
graphReference
|
|
887
915
|
};
|
|
888
916
|
}
|
|
889
917
|
function scopeCandidatesForUrl(url) {
|
|
@@ -931,6 +959,19 @@ async function fetchWithAuthFallback(params) {
|
|
|
931
959
|
resolveFn: params.resolveFn,
|
|
932
960
|
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
933
961
|
});
|
|
962
|
+
if (params.bearerToken !== void 0) {
|
|
963
|
+
const headers = require_resolve_allowlist.ensureUserAgentHeader(params.requestInit?.headers);
|
|
964
|
+
require_resolve_allowlist.applyAuthorizationHeaderForUrl({
|
|
965
|
+
headers,
|
|
966
|
+
url: params.url,
|
|
967
|
+
authAllowHosts: params.policy.authAllowHosts,
|
|
968
|
+
bearerToken: params.bearerToken
|
|
969
|
+
});
|
|
970
|
+
return fetchAttempt(params.fetchFn, {
|
|
971
|
+
...params.requestInit,
|
|
972
|
+
headers
|
|
973
|
+
});
|
|
974
|
+
}
|
|
934
975
|
const firstAttempt = await fetchAttempt(params.fetchFn, params.requestInit);
|
|
935
976
|
if (firstAttempt.ok) return firstAttempt;
|
|
936
977
|
if (!params.tokenProvider) return firstAttempt;
|
|
@@ -967,7 +1008,7 @@ async function downloadMSTeamsAttachments(params) {
|
|
|
967
1008
|
});
|
|
968
1009
|
const allowHosts = policy.allowHosts;
|
|
969
1010
|
const candidates = list.filter(require_resolve_allowlist.isAdvertisedFileAttachment).map((attachment) => {
|
|
970
|
-
return (require_resolve_allowlist.isDownloadableAttachment(attachment) ? resolveDownloadCandidate(attachment) : null) ?? {
|
|
1011
|
+
return (require_resolve_allowlist.isDownloadableAttachment(attachment) ? resolveDownloadCandidate(attachment, params.graphReferenceToken) : null) ?? {
|
|
971
1012
|
kind: "unavailable",
|
|
972
1013
|
mediaKind: require_resolve_allowlist.resolveMSTeamsMediaKind({
|
|
973
1014
|
contentType: require_resolve_allowlist.normalizeContentType(attachment.contentType),
|
|
@@ -1022,7 +1063,7 @@ async function downloadMSTeamsAttachments(params) {
|
|
|
1022
1063
|
filePathHint: candidate.fileHint ?? candidate.url,
|
|
1023
1064
|
maxBytes: params.maxBytes,
|
|
1024
1065
|
contentTypeHint: candidate.contentTypeHint,
|
|
1025
|
-
kind: candidate.mediaKind,
|
|
1066
|
+
kind: candidate.graphReference ? void 0 : candidate.mediaKind,
|
|
1026
1067
|
preserveFilenames: params.preserveFilenames,
|
|
1027
1068
|
fetchImpl: (input, init) => fetchWithAuthFallback({
|
|
1028
1069
|
url: (0, openclaw_plugin_sdk_request_url.resolveRequestUrl)(input),
|
|
@@ -1031,10 +1072,18 @@ async function downloadMSTeamsAttachments(params) {
|
|
|
1031
1072
|
requestInit: init,
|
|
1032
1073
|
resolveFn: params.resolveFn,
|
|
1033
1074
|
policy,
|
|
1034
|
-
deadline: params.deadline
|
|
1075
|
+
deadline: params.deadline,
|
|
1076
|
+
bearerToken: candidate.graphReference ? params.graphReferenceToken : void 0
|
|
1035
1077
|
})
|
|
1036
1078
|
});
|
|
1037
1079
|
} catch (err) {
|
|
1080
|
+
if (candidate.graphReference) {
|
|
1081
|
+
params.logger?.warn?.("msteams SharePoint reference download failed", {
|
|
1082
|
+
error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err),
|
|
1083
|
+
name: candidate.fileHint
|
|
1084
|
+
});
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1038
1087
|
const msg = (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err);
|
|
1039
1088
|
params.logger?.warn?.(`msteams attachment download failed host=${safeHostForLog(candidate.url)} error=${msg}`);
|
|
1040
1089
|
}
|
|
@@ -1220,9 +1269,6 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1220
1269
|
tokenError: true
|
|
1221
1270
|
};
|
|
1222
1271
|
}
|
|
1223
|
-
const fetchFn = params.fetchFn ?? fetch;
|
|
1224
|
-
const sharePointMedia = [];
|
|
1225
|
-
const downloadedReferenceUrls = /* @__PURE__ */ new Set();
|
|
1226
1272
|
let messageAttachments = [];
|
|
1227
1273
|
let referenceAttachments = [];
|
|
1228
1274
|
let messageStatus;
|
|
@@ -1251,72 +1297,13 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1251
1297
|
} catch (err) {
|
|
1252
1298
|
logGraphFailure("message fetch", err);
|
|
1253
1299
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
fileName: name
|
|
1262
|
-
}),
|
|
1263
|
-
...sourceId ? { sourceId } : {}
|
|
1264
|
-
};
|
|
1265
|
-
if (!shareUrl) {
|
|
1266
|
-
sharePointMedia.push(unavailableMedia);
|
|
1267
|
-
continue;
|
|
1268
|
-
}
|
|
1269
|
-
downloadedReferenceUrls.add(shareUrl);
|
|
1270
|
-
try {
|
|
1271
|
-
const sharesUrl = `${require_resolve_allowlist.GRAPH_ROOT}/shares/${require_resolve_allowlist.encodeGraphShareId(shareUrl)}/driveItem/content`;
|
|
1272
|
-
if (!(0, openclaw_plugin_sdk_ssrf_policy.isHttpsUrlAllowedByHostnameSuffixAllowlist)(sharesUrl, policy.allowHosts)) {
|
|
1273
|
-
params.logger?.debug?.("graph media sharepoint url not in allowHosts", {
|
|
1274
|
-
messageUrl,
|
|
1275
|
-
sharesUrl
|
|
1276
|
-
});
|
|
1277
|
-
sharePointMedia.push(unavailableMedia);
|
|
1278
|
-
continue;
|
|
1279
|
-
}
|
|
1280
|
-
const media = await downloadAndStoreMSTeamsRemoteMedia({
|
|
1281
|
-
url: sharesUrl,
|
|
1282
|
-
filePathHint: name,
|
|
1283
|
-
maxBytes: params.maxBytes,
|
|
1284
|
-
contentTypeHint: "application/octet-stream",
|
|
1285
|
-
preserveFilenames: params.preserveFilenames,
|
|
1286
|
-
fetchImpl: async (input, init) => {
|
|
1287
|
-
const requestUrl = (0, openclaw_plugin_sdk_request_url.resolveRequestUrl)(input);
|
|
1288
|
-
const headers = require_resolve_allowlist.ensureUserAgentHeader(init?.headers);
|
|
1289
|
-
require_resolve_allowlist.applyAuthorizationHeaderForUrl({
|
|
1290
|
-
headers,
|
|
1291
|
-
url: requestUrl,
|
|
1292
|
-
authAllowHosts: policy.authAllowHosts,
|
|
1293
|
-
bearerToken: accessToken
|
|
1294
|
-
});
|
|
1295
|
-
return await require_resolve_allowlist.safeFetchWithPolicy({
|
|
1296
|
-
url: requestUrl,
|
|
1297
|
-
policy,
|
|
1298
|
-
fetchFn,
|
|
1299
|
-
requestInit: {
|
|
1300
|
-
...init,
|
|
1301
|
-
headers
|
|
1302
|
-
},
|
|
1303
|
-
resolveFn: params.resolveFn,
|
|
1304
|
-
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
1305
|
-
});
|
|
1306
|
-
}
|
|
1307
|
-
});
|
|
1308
|
-
sharePointMedia.push(sourceId ? {
|
|
1309
|
-
...media,
|
|
1310
|
-
sourceId
|
|
1311
|
-
} : media);
|
|
1312
|
-
} catch (err) {
|
|
1313
|
-
sharePointMedia.push(unavailableMedia);
|
|
1314
|
-
params.logger?.warn?.("msteams SharePoint reference download failed", {
|
|
1315
|
-
error: (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err),
|
|
1316
|
-
name
|
|
1317
|
-
});
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1300
|
+
const sharePointMedia = await downloadMSTeamsAttachments({
|
|
1301
|
+
...params,
|
|
1302
|
+
attachments: referenceAttachments,
|
|
1303
|
+
graphReferenceToken: accessToken,
|
|
1304
|
+
allowHosts: policy.allowHosts,
|
|
1305
|
+
authAllowHosts: policy.authAllowHosts
|
|
1306
|
+
});
|
|
1320
1307
|
const hosted = await downloadGraphHostedContent({
|
|
1321
1308
|
accessToken,
|
|
1322
1309
|
messageUrl,
|
|
@@ -1327,7 +1314,8 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1327
1314
|
deadline: params.deadline
|
|
1328
1315
|
});
|
|
1329
1316
|
const normalizedAttachments = messageAttachments.map(normalizeGraphAttachment);
|
|
1330
|
-
const
|
|
1317
|
+
const referenceUrls = new Set(referenceAttachments.map((att) => att.contentUrl));
|
|
1318
|
+
const filteredAttachments = normalizedAttachments.filter((att) => att.contentType !== "reference" || !referenceUrls.has(att.contentUrl));
|
|
1331
1319
|
let attachmentMedia = [];
|
|
1332
1320
|
try {
|
|
1333
1321
|
attachmentMedia = await downloadMSTeamsAttachments({
|
|
@@ -1447,14 +1435,14 @@ async function resolveMSTeamsInboundMedia(params) {
|
|
|
1447
1435
|
authAllowHosts: params.authAllowHosts,
|
|
1448
1436
|
deadline: params.deadline
|
|
1449
1437
|
});
|
|
1450
|
-
|
|
1438
|
+
mediaList = mergeMSTeamsMediaFacts(mediaList, bfMedia.media, { positionallyAligned: false });
|
|
1451
1439
|
if (!hasMaterializedMedia(bfMedia.media)) log.debug?.("bot framework attachments fetch empty", {
|
|
1452
1440
|
conversationType,
|
|
1453
1441
|
attachmentCount: bfMedia.attachmentCount ?? attachmentIds.length
|
|
1454
1442
|
});
|
|
1455
1443
|
}
|
|
1456
1444
|
}
|
|
1457
|
-
if (shouldFetchGraphMessage && !
|
|
1445
|
+
if (shouldFetchGraphMessage && !isBotFrameworkPersonalChat) {
|
|
1458
1446
|
const graphTeamAadGroupId = conversationType.trim().toLowerCase() === "channel" && !teamAadGroupId ? await params.resolveTeamAadGroupId?.() : teamAadGroupId;
|
|
1459
1447
|
const messageUrl = buildMSTeamsGraphMessageUrl({
|
|
1460
1448
|
conversationType,
|
|
@@ -1480,7 +1468,7 @@ async function resolveMSTeamsInboundMedia(params) {
|
|
|
1480
1468
|
deadline: params.deadline,
|
|
1481
1469
|
logger: log
|
|
1482
1470
|
});
|
|
1483
|
-
|
|
1471
|
+
mediaList = mergeMSTeamsMediaFacts(mediaList, graphMedia.media, { positionallyAligned: false });
|
|
1484
1472
|
if (!hasMaterializedMedia(mediaList)) log.debug?.("graph media fetch empty", {
|
|
1485
1473
|
messageUrl,
|
|
1486
1474
|
hostedStatus: graphMedia.hostedStatus,
|
|
@@ -1517,9 +1505,10 @@ async function prepareMSTeamsInboundContent(params) {
|
|
|
1517
1505
|
return null;
|
|
1518
1506
|
}
|
|
1519
1507
|
let mediaList = [];
|
|
1508
|
+
const deadline = require_resolve_allowlist.createMSTeamsInboundMediaDeadline();
|
|
1520
1509
|
try {
|
|
1521
1510
|
mediaList = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
1522
|
-
deadline
|
|
1511
|
+
deadline,
|
|
1523
1512
|
label: "MS Teams inbound media",
|
|
1524
1513
|
work: () => resolveMSTeamsInboundMedia({
|
|
1525
1514
|
attachments: params.entry.attachments,
|
|
@@ -1533,7 +1522,7 @@ async function prepareMSTeamsInboundContent(params) {
|
|
|
1533
1522
|
conversationId: params.conversationId,
|
|
1534
1523
|
conversationMessageId: params.conversationMessageId,
|
|
1535
1524
|
teamAadGroupId: params.teamAadGroupId,
|
|
1536
|
-
resolveTeamAadGroupId: params.resolveTeamAadGroupId,
|
|
1525
|
+
resolveTeamAadGroupId: () => params.resolveTeamAadGroupId(deadline),
|
|
1537
1526
|
serviceUrl: activity.serviceUrl,
|
|
1538
1527
|
activity: {
|
|
1539
1528
|
id: activity.id,
|
|
@@ -1541,7 +1530,7 @@ async function prepareMSTeamsInboundContent(params) {
|
|
|
1541
1530
|
channelData: activity.channelData
|
|
1542
1531
|
},
|
|
1543
1532
|
log: params.log,
|
|
1544
|
-
deadline
|
|
1533
|
+
deadline
|
|
1545
1534
|
})
|
|
1546
1535
|
});
|
|
1547
1536
|
} catch (err) {
|
|
@@ -1567,6 +1556,30 @@ async function prepareMSTeamsInboundContent(params) {
|
|
|
1567
1556
|
};
|
|
1568
1557
|
}
|
|
1569
1558
|
//#endregion
|
|
1559
|
+
//#region extensions/msteams/src/informative-status.ts
|
|
1560
|
+
const INFORMATIVE_MAX_CHARS = 1e3;
|
|
1561
|
+
const INFORMATIVE_MAX_BYTES = 1024;
|
|
1562
|
+
const ELLIPSIS = "…";
|
|
1563
|
+
const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
1564
|
+
const utf8 = new TextEncoder();
|
|
1565
|
+
function flattenInformativeStatus(text) {
|
|
1566
|
+
const joined = text.split("\n").map((line) => line.trim().replace(/^[•-]\s+/u, "")).filter(Boolean).join(" · ");
|
|
1567
|
+
if (joined.length <= INFORMATIVE_MAX_CHARS && utf8.encode(joined).length <= INFORMATIVE_MAX_BYTES) return joined;
|
|
1568
|
+
const graphemes = Array.from(segmenter.segment(joined), (s) => s.segment);
|
|
1569
|
+
let chars = 1;
|
|
1570
|
+
let bytes = utf8.encode(ELLIPSIS).length;
|
|
1571
|
+
let start = graphemes.length;
|
|
1572
|
+
while (start > 0) {
|
|
1573
|
+
const next = graphemes[start - 1];
|
|
1574
|
+
const nextBytes = utf8.encode(next).length;
|
|
1575
|
+
if (chars + next.length > INFORMATIVE_MAX_CHARS || bytes + nextBytes > INFORMATIVE_MAX_BYTES) break;
|
|
1576
|
+
chars += next.length;
|
|
1577
|
+
bytes += nextBytes;
|
|
1578
|
+
start -= 1;
|
|
1579
|
+
}
|
|
1580
|
+
return ELLIPSIS + graphemes.slice(start).join("");
|
|
1581
|
+
}
|
|
1582
|
+
//#endregion
|
|
1570
1583
|
//#region extensions/msteams/src/reply-stream-controller.ts
|
|
1571
1584
|
function isStreamCancelledError(err) {
|
|
1572
1585
|
return err instanceof Error && err.name === "StreamCancelledError";
|
|
@@ -1581,19 +1594,16 @@ function createTeamsReplyStreamController(params) {
|
|
|
1581
1594
|
let nativeDeliveryClaimed = false;
|
|
1582
1595
|
let streamFinalizationPending = false;
|
|
1583
1596
|
let canceledLocally = false;
|
|
1584
|
-
let streamFailed = false;
|
|
1585
1597
|
let pendingFinalPayload;
|
|
1586
1598
|
let emittedText = "";
|
|
1587
1599
|
let acknowledgedText = "";
|
|
1588
1600
|
let acknowledgedLogicalText = "";
|
|
1589
1601
|
let acknowledgedStreamId;
|
|
1590
1602
|
let replacementFinalPending = false;
|
|
1591
|
-
let replacementEmitFailed = false;
|
|
1592
1603
|
let replacementSettlementPending = false;
|
|
1593
1604
|
let replacementTextAwaitingAcknowledgement;
|
|
1594
1605
|
let deferredReplacementEntries = [];
|
|
1595
1606
|
let queuedFinalActivity;
|
|
1596
|
-
let failedSegmentFallbackPrepared = false;
|
|
1597
1607
|
const streamEvents = stream?.events;
|
|
1598
1608
|
let streamChunkSubscription;
|
|
1599
1609
|
if (typeof streamEvents?.on === "function" && typeof streamEvents.off === "function") streamChunkSubscription = streamEvents.on("chunk", (activity) => {
|
|
@@ -1640,6 +1650,16 @@ function createTeamsReplyStreamController(params) {
|
|
|
1640
1650
|
...require_conversation_store_state.buildMSTeamsMessageActivity(text === void 0 ? void 0 : require_conversation_store_state.formatMSTeamsMarkdown(text, params.tableMode ?? "code")),
|
|
1641
1651
|
channelData: params.feedbackLoopEnabled ? { feedbackLoopEnabled: true } : {}
|
|
1642
1652
|
});
|
|
1653
|
+
const queueFinalReplacement = (activeStream, text) => {
|
|
1654
|
+
const activity = finalStreamActivity(text);
|
|
1655
|
+
replacementTextAwaitingAcknowledgement = {
|
|
1656
|
+
text: activity.text,
|
|
1657
|
+
logicalText: text
|
|
1658
|
+
};
|
|
1659
|
+
activeStream.emit(activity);
|
|
1660
|
+
queuedFinalActivity = activity;
|
|
1661
|
+
emittedText = text;
|
|
1662
|
+
};
|
|
1643
1663
|
const deferredReplacementLogicalContent = () => {
|
|
1644
1664
|
return deferredReplacementEntries.map((entry) => entry.payload.text).filter((text) => Boolean(text)).join("\n") || void 0;
|
|
1645
1665
|
};
|
|
@@ -1673,7 +1693,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
1673
1693
|
update: (text) => {
|
|
1674
1694
|
if (!stream || wasCanceled() || streamFinalizationPending) return false;
|
|
1675
1695
|
try {
|
|
1676
|
-
stream.update(text
|
|
1696
|
+
stream.update(flattenInformativeStatus(text));
|
|
1677
1697
|
return true;
|
|
1678
1698
|
} catch (err) {
|
|
1679
1699
|
if (isStreamCancelledError(err)) canceledLocally = true;
|
|
@@ -1698,10 +1718,6 @@ function createTeamsReplyStreamController(params) {
|
|
|
1698
1718
|
if (!delta) return;
|
|
1699
1719
|
if (previousRemainder.trim()) {
|
|
1700
1720
|
stream.clearText();
|
|
1701
|
-
if (streamFailed) {
|
|
1702
|
-
streamFinalizationPending = true;
|
|
1703
|
-
return;
|
|
1704
|
-
}
|
|
1705
1721
|
replacementFinalPending = true;
|
|
1706
1722
|
replacementSettlementPending = true;
|
|
1707
1723
|
pendingFinalPayload = { text: fullText };
|
|
@@ -1718,8 +1734,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
1718
1734
|
canceledLocally = true;
|
|
1719
1735
|
return;
|
|
1720
1736
|
}
|
|
1721
|
-
|
|
1722
|
-
params.log?.warn?.(`msteams stream emit failed, falling back to block delivery: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
|
|
1737
|
+
throw err;
|
|
1723
1738
|
}
|
|
1724
1739
|
},
|
|
1725
1740
|
pushItemEvent: progressDraft.pushItemEvent.bind(progressDraft),
|
|
@@ -1747,14 +1762,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
1747
1762
|
});
|
|
1748
1763
|
pendingFinalPayload = fallbackPayloadForSuppressedFinal(payload);
|
|
1749
1764
|
try {
|
|
1750
|
-
|
|
1751
|
-
replacementTextAwaitingAcknowledgement = {
|
|
1752
|
-
text: activity.text,
|
|
1753
|
-
logicalText: payload.text
|
|
1754
|
-
};
|
|
1755
|
-
stream.emit(activity);
|
|
1756
|
-
queuedFinalActivity = activity;
|
|
1757
|
-
emittedText = payload.text;
|
|
1765
|
+
queueFinalReplacement(stream, payload.text);
|
|
1758
1766
|
tokensEmitted = false;
|
|
1759
1767
|
return;
|
|
1760
1768
|
} catch (err) {
|
|
@@ -1766,18 +1774,9 @@ function createTeamsReplyStreamController(params) {
|
|
|
1766
1774
|
deferredReplacementEntries = [];
|
|
1767
1775
|
return;
|
|
1768
1776
|
}
|
|
1769
|
-
|
|
1770
|
-
replacementEmitFailed = true;
|
|
1771
|
-
params.log?.warn?.(`msteams stream replacement failed, falling back to block delivery: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
|
|
1772
|
-
return;
|
|
1777
|
+
throw err;
|
|
1773
1778
|
}
|
|
1774
1779
|
}
|
|
1775
|
-
if (streamFailed) {
|
|
1776
|
-
const fallback = failedSegmentFallbackPrepared ? payload : fallbackPayloadAfterAcknowledgedText(payload);
|
|
1777
|
-
failedSegmentFallbackPrepared = true;
|
|
1778
|
-
pendingFinalPayload = void 0;
|
|
1779
|
-
return fallback;
|
|
1780
|
-
}
|
|
1781
1780
|
if (streamMode === "progress" && payload.text && !nativeDispatchStarted) try {
|
|
1782
1781
|
stream.emit(payload.text);
|
|
1783
1782
|
emittedText = payload.text;
|
|
@@ -1788,7 +1787,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
1788
1787
|
canceledLocally = true;
|
|
1789
1788
|
return;
|
|
1790
1789
|
}
|
|
1791
|
-
|
|
1790
|
+
throw err;
|
|
1792
1791
|
}
|
|
1793
1792
|
if (tokensEmitted) {
|
|
1794
1793
|
const hasMedia = Boolean(payload.mediaUrl || payload.mediaUrls?.length);
|
|
@@ -1822,15 +1821,8 @@ function createTeamsReplyStreamController(params) {
|
|
|
1822
1821
|
kind: "replacement",
|
|
1823
1822
|
payload: pendingFinalPayload
|
|
1824
1823
|
});
|
|
1825
|
-
const activity = finalStreamActivity(pendingFinalPayload.text);
|
|
1826
|
-
replacementTextAwaitingAcknowledgement = {
|
|
1827
|
-
text: activity.text,
|
|
1828
|
-
logicalText: pendingFinalPayload.text
|
|
1829
|
-
};
|
|
1830
1824
|
logicalContent = deferredReplacementLogicalContent();
|
|
1831
|
-
stream.
|
|
1832
|
-
queuedFinalActivity = activity;
|
|
1833
|
-
emittedText = pendingFinalPayload.text;
|
|
1825
|
+
queueFinalReplacement(stream, pendingFinalPayload.text);
|
|
1834
1826
|
}
|
|
1835
1827
|
logicalContent ??= replacementSettlementPending ? deferredReplacementLogicalContent() : void 0;
|
|
1836
1828
|
const logicalText = pendingFinalPayload?.text ?? (emittedText || void 0);
|
|
@@ -1852,15 +1844,13 @@ function createTeamsReplyStreamController(params) {
|
|
|
1852
1844
|
}
|
|
1853
1845
|
const result = await stream.close();
|
|
1854
1846
|
if (!result) return finalizeWithoutReceipt(logicalContent, wasCanceled());
|
|
1855
|
-
const replacementFallback = replacementSettlementPending && replacementEmitFailed && pendingFinalPayload ? fallbackPayloadAfterAcknowledgedText(pendingFinalPayload) : void 0;
|
|
1856
1847
|
const messageId = require_conversation_store_state.extractMessageId(result) ?? acknowledgedStreamId;
|
|
1857
|
-
const postNativePayloads = replacementSettlementPending ? deferredReplacementPayloads(
|
|
1858
|
-
const nativeContent = replacementEmitFailed ? acknowledgedText || void 0 : content;
|
|
1848
|
+
const postNativePayloads = replacementSettlementPending ? deferredReplacementPayloads(void 0) : [];
|
|
1859
1849
|
return {
|
|
1860
|
-
visibleReplySent:
|
|
1861
|
-
...
|
|
1850
|
+
visibleReplySent: true,
|
|
1851
|
+
...content === void 0 ? {} : { content },
|
|
1862
1852
|
...logicalContent ? { logicalContent } : {},
|
|
1863
|
-
...messageId
|
|
1853
|
+
...messageId ? { messageId } : {},
|
|
1864
1854
|
...postNativePayloads.length > 0 ? { postNativePayloads } : {}
|
|
1865
1855
|
};
|
|
1866
1856
|
} catch (err) {
|
|
@@ -1868,15 +1858,12 @@ function createTeamsReplyStreamController(params) {
|
|
|
1868
1858
|
canceledLocally = true;
|
|
1869
1859
|
return acknowledgedNativeDelivery();
|
|
1870
1860
|
}
|
|
1871
|
-
streamFailed = true;
|
|
1872
1861
|
params.log?.warn?.(`msteams stream finalize failed: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
|
|
1873
1862
|
return finalizeWithoutReceipt(logicalContent);
|
|
1874
1863
|
} finally {
|
|
1875
1864
|
pendingFinalPayload = void 0;
|
|
1876
1865
|
streamFinalizationPending = false;
|
|
1877
|
-
failedSegmentFallbackPrepared = true;
|
|
1878
1866
|
queuedFinalActivity = void 0;
|
|
1879
|
-
replacementEmitFailed = false;
|
|
1880
1867
|
replacementFinalPending = false;
|
|
1881
1868
|
replacementSettlementPending = false;
|
|
1882
1869
|
replacementTextAwaitingAcknowledgement = void 0;
|
|
@@ -1888,7 +1875,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
1888
1875
|
return Boolean(stream);
|
|
1889
1876
|
},
|
|
1890
1877
|
isStreamActive() {
|
|
1891
|
-
return Boolean(stream) && tokensEmitted && !wasCanceled()
|
|
1878
|
+
return Boolean(stream) && tokensEmitted && !wasCanceled();
|
|
1892
1879
|
},
|
|
1893
1880
|
wasCanceled
|
|
1894
1881
|
};
|
|
@@ -1897,7 +1884,12 @@ function createTeamsReplyStreamController(params) {
|
|
|
1897
1884
|
//#region extensions/msteams/src/reply-dispatcher.ts
|
|
1898
1885
|
function createMSTeamsReplyDispatcher(params) {
|
|
1899
1886
|
const core = require_runtime.getMSTeamsRuntime();
|
|
1900
|
-
const msteamsCfg = params.cfg.
|
|
1887
|
+
const msteamsCfg = require_resolve_allowlist.resolveMSTeamsAccountConfig(params.cfg, params.accountId);
|
|
1888
|
+
const mediaMaxBytes = (0, openclaw_plugin_sdk_account_helpers.resolveChannelMediaMaxBytes)({
|
|
1889
|
+
cfg: params.cfg,
|
|
1890
|
+
resolveChannelLimitMb: () => msteamsCfg.mediaMaxMb
|
|
1891
|
+
});
|
|
1892
|
+
const feedbackLoopEnabled = msteamsCfg.feedbackEnabled !== false;
|
|
1901
1893
|
const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationRef.conversation?.conversationType);
|
|
1902
1894
|
const isTypingSupported = conversationType === "personal" || conversationType === "groupchat";
|
|
1903
1895
|
const TYPING_KEEPALIVE_INTERVAL_MS = 8e3;
|
|
@@ -1909,7 +1901,10 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1909
1901
|
await params.context.sendActivity({ type: "typing" });
|
|
1910
1902
|
},
|
|
1911
1903
|
onRevoked: async () => {
|
|
1912
|
-
await require_conversation_store_state.sendMSTeamsActivityWithReference(params.app, require_conversation_store_state.buildConversationReference(params.conversationRef), { type: "typing" }, {
|
|
1904
|
+
await require_conversation_store_state.sendMSTeamsActivityWithReference(params.app, require_conversation_store_state.buildConversationReference(params.conversationRef), { type: "typing" }, {
|
|
1905
|
+
accountId: params.accountId,
|
|
1906
|
+
serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg)
|
|
1907
|
+
});
|
|
1913
1908
|
},
|
|
1914
1909
|
onRevokedLog: () => {
|
|
1915
1910
|
params.log.debug?.("turn context revoked, sending typing via proactive messaging");
|
|
@@ -1935,16 +1930,12 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1935
1930
|
}
|
|
1936
1931
|
}
|
|
1937
1932
|
});
|
|
1938
|
-
const chunkMode = core.channel.text.resolveChunkMode(params.cfg, "msteams");
|
|
1933
|
+
const chunkMode = core.channel.text.resolveChunkMode(params.cfg, "msteams", params.accountId);
|
|
1939
1934
|
const tableMode = core.channel.text.resolveMarkdownTableMode({
|
|
1940
1935
|
cfg: params.cfg,
|
|
1941
|
-
channel: "msteams"
|
|
1942
|
-
|
|
1943
|
-
const mediaMaxBytes = (0, openclaw_plugin_sdk_account_helpers.resolveChannelMediaMaxBytes)({
|
|
1944
|
-
cfg: params.cfg,
|
|
1945
|
-
resolveChannelLimitMb: ({ cfg }) => cfg.channels?.msteams?.mediaMaxMb
|
|
1936
|
+
channel: "msteams",
|
|
1937
|
+
accountId: params.accountId
|
|
1946
1938
|
});
|
|
1947
|
-
const feedbackLoopEnabled = params.cfg.channels?.msteams?.feedbackEnabled !== false;
|
|
1948
1939
|
const hookRunner = (0, openclaw_plugin_sdk_plugin_runtime.getGlobalHookRunner)();
|
|
1949
1940
|
const streamController = createTeamsReplyStreamController({
|
|
1950
1941
|
tableMode,
|
|
@@ -2018,6 +2009,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2018
2009
|
const sentIds = [];
|
|
2019
2010
|
for (const msg of toSend) try {
|
|
2020
2011
|
const msgIds = await require_conversation_store_state.sendMSTeamsMessages({
|
|
2012
|
+
accountId: params.accountId,
|
|
2021
2013
|
replyStyle: params.replyStyle,
|
|
2022
2014
|
app: params.app,
|
|
2023
2015
|
conversationRef: params.conversationRef,
|
|
@@ -2048,9 +2040,14 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2048
2040
|
params.log.debug?.("individual message send failed, continuing with remaining blocks");
|
|
2049
2041
|
}
|
|
2050
2042
|
if (failed > 0) {
|
|
2043
|
+
const classification = require_channel_setup.classifyMSTeamsSendError(lastFailedError);
|
|
2044
|
+
const hint = require_channel_setup.formatMSTeamsSendErrorHint(classification);
|
|
2051
2045
|
params.log.warn?.(`failed to deliver ${failed} of ${total} message blocks`, {
|
|
2052
2046
|
failed,
|
|
2053
|
-
total
|
|
2047
|
+
total,
|
|
2048
|
+
error: require_channel_setup.formatUnknownError(lastFailedError),
|
|
2049
|
+
classification,
|
|
2050
|
+
...hint ? { hint } : {}
|
|
2054
2051
|
});
|
|
2055
2052
|
queueDeliveryFailureSystemEvent({
|
|
2056
2053
|
failed,
|
|
@@ -2205,7 +2202,7 @@ async function dispatchMSTeamsInboundTurn(params) {
|
|
|
2205
2202
|
const core = require_runtime.getMSTeamsRuntime();
|
|
2206
2203
|
const { cfg, runtime, app, tokenProvider, textLimit, log, logVerboseMessage, facts, admission, content, routing, thread, replyStyle, timestamp, contextVisibilityMode, conversationHistories, historyLimit } = params;
|
|
2207
2204
|
const { context, activity, rawBody, text, quoteInfo, conversationRef } = facts;
|
|
2208
|
-
const { senderId, senderName, isDirectMessage, allowNameMatching, groupPolicy, commandAuthorized, effectiveGroupAllowFrom } = admission;
|
|
2205
|
+
const { senderId, senderName, isDirectMessage, allowNameMatching, groupPolicy, commandAuthorized, effectiveGroupAllowFrom, msteamsCfg } = admission;
|
|
2209
2206
|
const { route } = routing;
|
|
2210
2207
|
const { agentBody, inboundMedia } = content;
|
|
2211
2208
|
const { teamAadGroupId, quoteBodyFull, quoteSenderId, quoteSenderName, threadContext } = thread;
|
|
@@ -2255,7 +2252,8 @@ async function dispatchMSTeamsInboundTurn(params) {
|
|
|
2255
2252
|
}) : true;
|
|
2256
2253
|
const nativeChannelId = isChannel && teamAadGroupId ? `${teamAadGroupId}/${graphChannelId}` : void 0;
|
|
2257
2254
|
const boundIngress = await resolveMSTeamsSenderAccess({
|
|
2258
|
-
cfg,
|
|
2255
|
+
cfg: route.accountId === params.listenerAccountId ? params.readConfig() : params.accountPolicyCfg,
|
|
2256
|
+
accountId: route.accountId,
|
|
2259
2257
|
activity,
|
|
2260
2258
|
hasControlCommand: admission.isControlCommand,
|
|
2261
2259
|
conversationThreadId: facts.threadId,
|
|
@@ -2267,6 +2265,13 @@ async function dispatchMSTeamsInboundTurn(params) {
|
|
|
2267
2265
|
inboundEventKind: "user_request"
|
|
2268
2266
|
}
|
|
2269
2267
|
});
|
|
2268
|
+
if (!boundIngress.senderAccess.allowed || boundIngress.hasConflictingConversationScope || boundIngress.msteamsCfg?.enabled === false || boundIngress.commandAccess.shouldBlockControlCommand) {
|
|
2269
|
+
log.info("dropping message (dispatch-time account authorization changed)");
|
|
2270
|
+
return {
|
|
2271
|
+
kind: "completed",
|
|
2272
|
+
finalResponses: 0
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2270
2275
|
const ctxPayload = core.channel.inbound.buildContext({
|
|
2271
2276
|
channelIngress: boundIngress.channelIngress,
|
|
2272
2277
|
channel: "msteams",
|
|
@@ -2353,10 +2358,10 @@ async function dispatchMSTeamsInboundTurn(params) {
|
|
|
2353
2358
|
replyStyle,
|
|
2354
2359
|
textLimit,
|
|
2355
2360
|
onSentMessageIds: (ids) => {
|
|
2356
|
-
for (const id of ids) require_conversation_store_state.recordMSTeamsSentMessage(conversationId, id);
|
|
2361
|
+
for (const id of ids) require_conversation_store_state.recordMSTeamsSentMessage(conversationId, id, { accountId: route.accountId });
|
|
2357
2362
|
},
|
|
2358
2363
|
tokenProvider,
|
|
2359
|
-
sharePointSiteId:
|
|
2364
|
+
sharePointSiteId: msteamsCfg?.sharePointSiteId
|
|
2360
2365
|
});
|
|
2361
2366
|
const senderTimezone = (activity.entities?.find((entity) => entity.type === "clientInfo"))?.timezone || conversationRef.timezone;
|
|
2362
2367
|
const turnConfig = senderTimezone && !cfg.agents?.defaults?.userTimezone ? {
|
|
@@ -2449,7 +2454,8 @@ async function prepareMSTeamsDebounceEntry(params) {
|
|
|
2449
2454
|
const replyToId = activity.replyToId ?? void 0;
|
|
2450
2455
|
const implicitMentionKinds = conversationId && replyToId && await require_conversation_store_state.wasMSTeamsMessageSentWithPersistence({
|
|
2451
2456
|
conversationId,
|
|
2452
|
-
messageId: replyToId
|
|
2457
|
+
messageId: replyToId,
|
|
2458
|
+
accountId: params.accountId
|
|
2453
2459
|
}) ? ["reply_to_bot"] : [];
|
|
2454
2460
|
return {
|
|
2455
2461
|
context: params.context,
|
|
@@ -2574,26 +2580,19 @@ function touchLru(map, key, value, max) {
|
|
|
2574
2580
|
function buildParentCacheKey(groupId, channelId, parentId) {
|
|
2575
2581
|
return `${groupId}\u0000${channelId}\u0000${parentId}`;
|
|
2576
2582
|
}
|
|
2577
|
-
function resolveParentCacheExpiresAt(nowRaw) {
|
|
2578
|
-
const nowMs = (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(nowRaw);
|
|
2579
|
-
return nowMs === void 0 ? void 0 : (0, openclaw_plugin_sdk_number_runtime.resolveExpiresAtMsFromDurationMs)(PARENT_CACHE_TTL_MS, { nowMs });
|
|
2580
|
-
}
|
|
2581
2583
|
async function fetchParentMessageCached(token, groupId, channelId, parentId, deadline) {
|
|
2582
2584
|
const key = buildParentCacheKey(groupId, channelId, parentId);
|
|
2583
|
-
const now = (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(Date.now());
|
|
2584
2585
|
const cached = parentCache.get(key);
|
|
2585
|
-
|
|
2586
|
-
if (cached && now !== void 0 && cachedExpiresAt !== void 0 && cachedExpiresAt > now) {
|
|
2586
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
2587
2587
|
parentCache.delete(key);
|
|
2588
2588
|
parentCache.set(key, cached);
|
|
2589
2589
|
return cached.message;
|
|
2590
2590
|
}
|
|
2591
2591
|
if (cached) parentCache.delete(key);
|
|
2592
2592
|
const message = await require_reaction_types.fetchChannelMessage(token, groupId, channelId, parentId, deadline);
|
|
2593
|
-
|
|
2594
|
-
if (expiresAt !== void 0) touchLru(parentCache, key, {
|
|
2593
|
+
touchLru(parentCache, key, {
|
|
2595
2594
|
message,
|
|
2596
|
-
expiresAt
|
|
2595
|
+
expiresAt: Date.now() + PARENT_CACHE_TTL_MS
|
|
2597
2596
|
}, PARENT_CACHE_MAX);
|
|
2598
2597
|
return message;
|
|
2599
2598
|
}
|
|
@@ -2625,6 +2624,7 @@ function prepareMSTeamsThreadRouting(params) {
|
|
|
2625
2624
|
const route = require_runtime.getMSTeamsRuntime().channel.routing.resolveAgentRoute({
|
|
2626
2625
|
cfg: params.cfg,
|
|
2627
2626
|
channel: "msteams",
|
|
2627
|
+
accountId: params.accountId,
|
|
2628
2628
|
teamId: params.teamId,
|
|
2629
2629
|
peer: {
|
|
2630
2630
|
kind: params.isDirectMessage ? "direct" : params.isChannel ? "channel" : "group",
|
|
@@ -2637,11 +2637,10 @@ function prepareMSTeamsThreadRouting(params) {
|
|
|
2637
2637
|
conversationMessageId: params.conversationMessageId,
|
|
2638
2638
|
replyToId: params.context.activity.replyToId
|
|
2639
2639
|
});
|
|
2640
|
-
const deadline = require_resolve_allowlist.createMSTeamsInboundDeadline();
|
|
2641
2640
|
let teamAadGroupId = params.context.activity.channelData?.team?.aadGroupId?.trim() || void 0;
|
|
2642
2641
|
const conversationTeamId = params.isChannel ? params.teamId : void 0;
|
|
2643
2642
|
let teamGroupIdPromise;
|
|
2644
|
-
const resolveTeamAadGroupId = async () => {
|
|
2643
|
+
const resolveTeamAadGroupId = async (deadline) => {
|
|
2645
2644
|
if (!conversationTeamId) return;
|
|
2646
2645
|
teamGroupIdPromise ??= resolveTeamGroupId({
|
|
2647
2646
|
conversationTeamId,
|
|
@@ -2660,7 +2659,6 @@ function prepareMSTeamsThreadRouting(params) {
|
|
|
2660
2659
|
return {
|
|
2661
2660
|
route,
|
|
2662
2661
|
threadRootId: params.conversationMessageId ?? params.context.activity.replyToId,
|
|
2663
|
-
deadline,
|
|
2664
2662
|
resolveTeamAadGroupId,
|
|
2665
2663
|
getTeamAadGroupId: () => teamAadGroupId
|
|
2666
2664
|
};
|
|
@@ -2668,8 +2666,9 @@ function prepareMSTeamsThreadRouting(params) {
|
|
|
2668
2666
|
async function resolveMSTeamsThreadContext(params) {
|
|
2669
2667
|
const core = require_runtime.getMSTeamsRuntime();
|
|
2670
2668
|
const activity = params.context.activity;
|
|
2671
|
-
const { route
|
|
2672
|
-
const
|
|
2669
|
+
const { route } = params.routing;
|
|
2670
|
+
const deadline = require_resolve_allowlist.createMSTeamsInboundEnrichmentDeadline();
|
|
2671
|
+
const teamAadGroupId = await params.routing.resolveTeamAadGroupId(deadline);
|
|
2673
2672
|
let quoteBodyFull;
|
|
2674
2673
|
let quoteSenderId;
|
|
2675
2674
|
let quoteSenderName;
|
|
@@ -2755,19 +2754,21 @@ async function resolveMSTeamsThreadContext(params) {
|
|
|
2755
2754
|
//#endregion
|
|
2756
2755
|
//#region extensions/msteams/src/monitor-handler/message-handler.ts
|
|
2757
2756
|
function createMSTeamsMessageHandler(deps) {
|
|
2758
|
-
const { cfg, runtime, appId, app, tokenProvider, textLimit, mediaMaxBytes, conversationStore, pollStore, log } = deps;
|
|
2757
|
+
const { cfg, accountPolicyCfg = cfg, accountId, runtime, appId, app, tokenProvider, textLimit, mediaMaxBytes, conversationStore, pollStore, log } = deps;
|
|
2759
2758
|
const core = require_runtime.getMSTeamsRuntime();
|
|
2760
2759
|
const logVerboseMessage = (message) => {
|
|
2761
2760
|
if (core.logging.shouldLogVerbose()) log.debug?.(message);
|
|
2762
2761
|
};
|
|
2763
|
-
const msteamsCfg = cfg.channels?.msteams;
|
|
2762
|
+
const msteamsCfg = cfg.channels?.msteams ? require_resolve_allowlist.resolveMSTeamsAccountConfig(cfg, accountId) : void 0;
|
|
2764
2763
|
const contextVisibilityMode = (0, openclaw_plugin_sdk_context_visibility_runtime.resolveChannelContextVisibilityMode)({
|
|
2765
2764
|
cfg,
|
|
2766
|
-
channel: "msteams"
|
|
2765
|
+
channel: "msteams",
|
|
2766
|
+
accountId
|
|
2767
2767
|
});
|
|
2768
2768
|
const historyLimit = (0, openclaw_plugin_sdk_number_runtime.resolvePromptHistoryLimit)(msteamsCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit);
|
|
2769
2769
|
const conversationHistories = /* @__PURE__ */ new Map();
|
|
2770
|
-
const
|
|
2770
|
+
const readFullConfig = (0, openclaw_plugin_sdk_runtime_config_snapshot.createRuntimeConfigReader)(accountPolicyCfg);
|
|
2771
|
+
const readConfig = deps.readConfig ?? createMSTeamsHandlerConfigReader(deps);
|
|
2771
2772
|
const resolveDebounceMs = () => core.channel.debounce.resolveInboundDebounceMs({
|
|
2772
2773
|
cfg: readConfig(),
|
|
2773
2774
|
channel: "msteams"
|
|
@@ -2809,7 +2810,8 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2809
2810
|
isBotOwnedThread = await require_conversation_store_state.wasMSTeamsMessageSentWithPersistence({
|
|
2810
2811
|
conversationId,
|
|
2811
2812
|
messageId: facts.threadId,
|
|
2812
|
-
botId: activity.recipient.id
|
|
2813
|
+
botId: activity.recipient.id,
|
|
2814
|
+
accountId
|
|
2813
2815
|
});
|
|
2814
2816
|
currentCfg = readConfig();
|
|
2815
2817
|
}
|
|
@@ -2818,6 +2820,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2818
2820
|
if (currentMSTeamsCfg?.enabled === false || currentMSTeamsCfg?.appId && currentMSTeamsCfg.appId !== appId) return;
|
|
2819
2821
|
const admission = await admitMSTeamsMessage({
|
|
2820
2822
|
cfg: currentCfg,
|
|
2823
|
+
accountId,
|
|
2821
2824
|
activity,
|
|
2822
2825
|
text,
|
|
2823
2826
|
conversationId,
|
|
@@ -2852,6 +2855,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2852
2855
|
}
|
|
2853
2856
|
const threadRouting = prepareMSTeamsThreadRouting({
|
|
2854
2857
|
cfg: currentCfg,
|
|
2858
|
+
accountId,
|
|
2855
2859
|
context,
|
|
2856
2860
|
isDirectMessage,
|
|
2857
2861
|
isChannel,
|
|
@@ -2861,7 +2865,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2861
2865
|
teamId,
|
|
2862
2866
|
log
|
|
2863
2867
|
});
|
|
2864
|
-
const { route
|
|
2868
|
+
const { route } = threadRouting;
|
|
2865
2869
|
const inboundLabel = isDirectMessage ? `Teams DM from ${senderName}` : `Teams message in ${conversationType} from ${senderName}`;
|
|
2866
2870
|
const enqueuePrimaryMessageSystemEvent = () => core.system.enqueueSystemEvent(inboundLabel, {
|
|
2867
2871
|
sessionKey: route.sessionKey,
|
|
@@ -2937,7 +2941,6 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2937
2941
|
mediaAllowHosts: currentMSTeamsCfg?.mediaAllowHosts,
|
|
2938
2942
|
mediaAuthAllowHosts: currentMSTeamsCfg?.mediaAuthAllowHosts,
|
|
2939
2943
|
graphMediaFallback: currentMSTeamsCfg?.graphMediaFallback,
|
|
2940
|
-
deadline: preprocessingDeadline,
|
|
2941
2944
|
log
|
|
2942
2945
|
});
|
|
2943
2946
|
if (!content) return;
|
|
@@ -2958,6 +2961,9 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2958
2961
|
});
|
|
2959
2962
|
await dispatchMSTeamsInboundTurn({
|
|
2960
2963
|
cfg: currentCfg,
|
|
2964
|
+
accountPolicyCfg: readFullConfig(),
|
|
2965
|
+
readConfig,
|
|
2966
|
+
listenerAccountId: accountId,
|
|
2961
2967
|
runtime,
|
|
2962
2968
|
app,
|
|
2963
2969
|
tokenProvider,
|
|
@@ -3044,6 +3050,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
3044
3050
|
return async function handleTeamsMessage(context, turnAdoptionLifecycle) {
|
|
3045
3051
|
const entry = await prepareMSTeamsDebounceEntry({
|
|
3046
3052
|
context,
|
|
3053
|
+
accountId,
|
|
3047
3054
|
turnAdoptionLifecycle
|
|
3048
3055
|
});
|
|
3049
3056
|
await inboundDebouncer.enqueue(entry);
|
|
@@ -3053,10 +3060,11 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
3053
3060
|
//#endregion
|
|
3054
3061
|
//#region extensions/msteams/src/monitor-handler/reaction-handler.ts
|
|
3055
3062
|
function createMSTeamsReactionHandler(deps) {
|
|
3056
|
-
const {
|
|
3063
|
+
const { accountId, log } = deps;
|
|
3064
|
+
const readConfig = deps.readConfig ?? createMSTeamsHandlerConfigReader(deps);
|
|
3057
3065
|
const core = require_runtime.getMSTeamsRuntime();
|
|
3058
|
-
const msteamsCfg = cfg.channels?.msteams;
|
|
3059
3066
|
return async function handleReaction(context, direction) {
|
|
3067
|
+
const cfg = readConfig();
|
|
3060
3068
|
const activity = context.activity;
|
|
3061
3069
|
const rawReactions = direction === "added" ? activity.reactionsAdded : activity.reactionsRemoved;
|
|
3062
3070
|
const reactions = Array.isArray(rawReactions) ? rawReactions : [];
|
|
@@ -3076,9 +3084,11 @@ function createMSTeamsReactionHandler(deps) {
|
|
|
3076
3084
|
const senderName = from.name ?? from.id;
|
|
3077
3085
|
const access = await resolveMSTeamsSenderAccess({
|
|
3078
3086
|
cfg,
|
|
3087
|
+
accountId,
|
|
3079
3088
|
activity
|
|
3080
3089
|
});
|
|
3081
|
-
const { isDirectMessage, channelGate } = access;
|
|
3090
|
+
const { isDirectMessage, channelGate, msteamsCfg } = access;
|
|
3091
|
+
if (msteamsCfg?.enabled === false || msteamsCfg?.appId && msteamsCfg.appId !== deps.appId) return;
|
|
3082
3092
|
if (access.hasConflictingConversationScope) {
|
|
3083
3093
|
log.info("dropping reaction (conflicting conversation scope)", { conversationId });
|
|
3084
3094
|
return;
|
|
@@ -3106,6 +3116,7 @@ function createMSTeamsReactionHandler(deps) {
|
|
|
3106
3116
|
const route = core.channel.routing.resolveAgentRoute({
|
|
3107
3117
|
cfg,
|
|
3108
3118
|
channel: "msteams",
|
|
3119
|
+
accountId,
|
|
3109
3120
|
peer: {
|
|
3110
3121
|
kind: isDirectMessage ? "direct" : isChannel ? "channel" : "group",
|
|
3111
3122
|
id: isDirectMessage ? senderId : conversationId
|
|
@@ -3177,8 +3188,12 @@ function buildGroupWelcomeText(botName) {
|
|
|
3177
3188
|
//#region extensions/msteams/src/monitor-handler.ts
|
|
3178
3189
|
async function isMSTeamsInvokeAuthorized(params) {
|
|
3179
3190
|
const { context, deps, invokeKind } = params;
|
|
3191
|
+
const cfg = deps.readConfig?.() ?? deps.cfg;
|
|
3192
|
+
const account = require_resolve_allowlist.resolveMSTeamsAccountConfig(cfg, deps.accountId);
|
|
3193
|
+
if (account.enabled === false || account.appId && account.appId !== deps.appId) return false;
|
|
3180
3194
|
const resolved = await resolveMSTeamsSenderAccess({
|
|
3181
|
-
cfg
|
|
3195
|
+
cfg,
|
|
3196
|
+
accountId: deps.accountId,
|
|
3182
3197
|
activity: context.activity
|
|
3183
3198
|
});
|
|
3184
3199
|
const { msteamsCfg, isDirectMessage, conversationId, senderId } = resolved;
|
|
@@ -3224,7 +3239,7 @@ function createMSTeamsActivityHandler(deps) {
|
|
|
3224
3239
|
const handleMembersAdded = async (ctx) => {
|
|
3225
3240
|
const membersAdded = ctx.activity?.membersAdded ?? [];
|
|
3226
3241
|
const botId = ctx.activity?.recipient?.id;
|
|
3227
|
-
const msteamsCfg = deps.cfg.channels?.msteams;
|
|
3242
|
+
const msteamsCfg = deps.cfg.channels?.msteams ? require_resolve_allowlist.resolveMSTeamsAccountConfig(deps.cfg, deps.accountId) : void 0;
|
|
3228
3243
|
for (const member of membersAdded) if (member.id === botId) {
|
|
3229
3244
|
const isPersonal = ((0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(ctx.activity?.conversation?.conversationType) ?? "personal") === "personal";
|
|
3230
3245
|
if (isPersonal && msteamsCfg?.welcomeCard !== false) {
|
|
@@ -3301,7 +3316,8 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3301
3316
|
deps.log.debug?.("ignoring feedback with unknown reaction", { reaction });
|
|
3302
3317
|
return false;
|
|
3303
3318
|
}
|
|
3304
|
-
const
|
|
3319
|
+
const cfg = deps.readConfig?.() ?? deps.cfg;
|
|
3320
|
+
const msteamsCfg = require_resolve_allowlist.resolveMSTeamsAccountConfig(cfg, deps.accountId);
|
|
3305
3321
|
if (msteamsCfg?.feedbackEnabled === false) {
|
|
3306
3322
|
deps.log.debug?.("feedback handling disabled");
|
|
3307
3323
|
return true;
|
|
@@ -3324,8 +3340,9 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3324
3340
|
const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
|
|
3325
3341
|
const isChannel = convType === "channel";
|
|
3326
3342
|
const route = require_runtime.getMSTeamsRuntime().channel.routing.resolveAgentRoute({
|
|
3327
|
-
cfg
|
|
3343
|
+
cfg,
|
|
3328
3344
|
channel: "msteams",
|
|
3345
|
+
accountId: deps.accountId,
|
|
3329
3346
|
peer: {
|
|
3330
3347
|
kind: isDirectMessage ? "direct" : isChannel ? "channel" : "group",
|
|
3331
3348
|
id: isDirectMessage ? senderId : conversationId
|
|
@@ -3356,7 +3373,7 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3356
3373
|
});
|
|
3357
3374
|
try {
|
|
3358
3375
|
await (0, openclaw_plugin_sdk_channel_inbound.recordChannelFeedbackEvent)({
|
|
3359
|
-
cfg
|
|
3376
|
+
cfg,
|
|
3360
3377
|
agentId: route.agentId,
|
|
3361
3378
|
sessionKey: route.sessionKey,
|
|
3362
3379
|
event: feedbackEvent
|
|
@@ -3383,8 +3400,9 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3383
3400
|
locale: activity.locale
|
|
3384
3401
|
};
|
|
3385
3402
|
if (isNegative && msteamsCfg?.feedbackReflection !== false) runFeedbackReflection({
|
|
3386
|
-
cfg
|
|
3403
|
+
cfg,
|
|
3387
3404
|
app: deps.app,
|
|
3405
|
+
accountId: deps.accountId,
|
|
3388
3406
|
conversationRef,
|
|
3389
3407
|
sessionKey: route.sessionKey,
|
|
3390
3408
|
agentId: route.agentId,
|
|
@@ -3405,7 +3423,7 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3405
3423
|
if (activity.type !== "invoke" || activity.name !== "fileConsent/invoke") return false;
|
|
3406
3424
|
const consentResponse = require_conversation_store_state.parseFileConsentInvoke(activity);
|
|
3407
3425
|
if (!consentResponse) {
|
|
3408
|
-
log.debug?.("invalid file consent invoke"
|
|
3426
|
+
log.debug?.("invalid file consent invoke");
|
|
3409
3427
|
return false;
|
|
3410
3428
|
}
|
|
3411
3429
|
const uploadId = typeof consentResponse.context?.uploadId === "string" ? consentResponse.context.uploadId : void 0;
|
|
@@ -3501,6 +3519,14 @@ async function runMSTeamsFileConsentInvokeHandler(context, log) {
|
|
|
3501
3519
|
}
|
|
3502
3520
|
}
|
|
3503
3521
|
//#endregion
|
|
3522
|
+
//#region extensions/msteams/src/monitor-account-stores.ts
|
|
3523
|
+
function createMSTeamsMonitorStores(accountId, overrides) {
|
|
3524
|
+
return {
|
|
3525
|
+
conversationStore: overrides.conversationStore ?? require_conversation_store_state.createMSTeamsConversationStoreState({ accountId }),
|
|
3526
|
+
pollStore: overrides.pollStore ?? require_conversation_store_state.createMSTeamsPollStoreState({ accountId })
|
|
3527
|
+
};
|
|
3528
|
+
}
|
|
3529
|
+
//#endregion
|
|
3504
3530
|
//#region extensions/msteams/src/monitor-status.ts
|
|
3505
3531
|
function publishMSTeamsBlocked(statusSink, lastError) {
|
|
3506
3532
|
statusSink?.((0, openclaw_plugin_sdk_gateway_runtime.channelBlockedPatch)(lastError, { running: true }));
|
|
@@ -3613,7 +3639,7 @@ function createMSTeamsIngress(options) {
|
|
|
3613
3639
|
const installedLiveContext = Boolean(liveContext) && !liveContexts.has(facts.eventId);
|
|
3614
3640
|
if (liveContext && installedLiveContext) liveContexts.set(facts.eventId, liveContext);
|
|
3615
3641
|
const uninstallLiveContext = () => {
|
|
3616
|
-
if (installedLiveContext
|
|
3642
|
+
if (installedLiveContext) liveContexts.delete(facts.eventId);
|
|
3617
3643
|
};
|
|
3618
3644
|
let result;
|
|
3619
3645
|
try {
|
|
@@ -3643,7 +3669,7 @@ function createMSTeamsIngress(options) {
|
|
|
3643
3669
|
}
|
|
3644
3670
|
//#endregion
|
|
3645
3671
|
//#region extensions/msteams/src/replay-context.ts
|
|
3646
|
-
function createMSTeamsReplayContext(activity, app, serviceUrlBoundary) {
|
|
3672
|
+
function createMSTeamsReplayContext(activity, app, serviceUrlBoundary, accountId) {
|
|
3647
3673
|
const rawConversationId = activity.conversation?.id ?? "";
|
|
3648
3674
|
const conversationId = require_resolve_allowlist.normalizeMSTeamsConversationId(rawConversationId);
|
|
3649
3675
|
const conversationType = activity.conversation?.conversationType ?? "personal";
|
|
@@ -3667,6 +3693,7 @@ function createMSTeamsReplayContext(activity, app, serviceUrlBoundary) {
|
|
|
3667
3693
|
...activity.from?.aadObjectId ? { aadObjectId: activity.from.aadObjectId } : {}
|
|
3668
3694
|
};
|
|
3669
3695
|
const proactiveOptions = {
|
|
3696
|
+
accountId,
|
|
3670
3697
|
...quoteActivityId ? { quoteActivityId } : {},
|
|
3671
3698
|
...threadActivityId ? { threadActivityId } : {},
|
|
3672
3699
|
serviceUrlBoundary
|
|
@@ -3699,15 +3726,15 @@ function createMSTeamsReplayContext(activity, app, serviceUrlBoundary) {
|
|
|
3699
3726
|
* falling back to the Bot Framework `activity.from.id`.
|
|
3700
3727
|
*/
|
|
3701
3728
|
const MSTEAMS_SSO_TOKENS_NAMESPACE = "sso-tokens";
|
|
3702
|
-
const SSO_TOKEN_MUTATION_KEY = "sso-tokens";
|
|
3703
3729
|
const MSTEAMS_MAX_SSO_TOKENS = 5e3;
|
|
3704
3730
|
const STORE_KEY_VERSION_PREFIX = "v2:";
|
|
3705
3731
|
function makeMSTeamsSsoTokenStoreKey(connectionName, userId) {
|
|
3706
3732
|
return `${STORE_KEY_VERSION_PREFIX}${(0, node_crypto.createHash)("sha256").update(JSON.stringify([connectionName, userId])).digest("hex")}`;
|
|
3707
3733
|
}
|
|
3708
|
-
function createMSTeamsSsoTokenStoreFs() {
|
|
3734
|
+
function createMSTeamsSsoTokenStoreFs(params) {
|
|
3735
|
+
const namespace = require_delegated_state.resolveMSTeamsAccountStateNamespace(MSTEAMS_SSO_TOKENS_NAMESPACE, params?.accountId);
|
|
3709
3736
|
const tokenStore = require_runtime.getMSTeamsRuntime().state.openKeyedStore({
|
|
3710
|
-
namespace
|
|
3737
|
+
namespace,
|
|
3711
3738
|
maxEntries: MSTEAMS_MAX_SSO_TOKENS
|
|
3712
3739
|
});
|
|
3713
3740
|
return {
|
|
@@ -3715,12 +3742,12 @@ function createMSTeamsSsoTokenStoreFs() {
|
|
|
3715
3742
|
return await tokenStore.lookup(makeMSTeamsSsoTokenStoreKey(connectionName, userId)) ?? null;
|
|
3716
3743
|
},
|
|
3717
3744
|
async save(token) {
|
|
3718
|
-
await
|
|
3719
|
-
await tokenStore.register(makeMSTeamsSsoTokenStoreKey(token.connectionName, token.userId),
|
|
3745
|
+
await require_delegated_state.withMSTeamsSqliteMutationLock(namespace, async () => {
|
|
3746
|
+
await tokenStore.register(makeMSTeamsSsoTokenStoreKey(token.connectionName, token.userId), require_delegated_state.toPluginJsonValue({ ...token }));
|
|
3720
3747
|
});
|
|
3721
3748
|
},
|
|
3722
3749
|
async remove({ connectionName, userId }) {
|
|
3723
|
-
return
|
|
3750
|
+
return require_delegated_state.withMSTeamsSqliteMutationLock(namespace, () => tokenStore.delete(makeMSTeamsSsoTokenStoreKey(connectionName, userId)));
|
|
3724
3751
|
}
|
|
3725
3752
|
};
|
|
3726
3753
|
}
|
|
@@ -3776,9 +3803,12 @@ function createMSTeamsWebhookHandler(expressApp, warn) {
|
|
|
3776
3803
|
async function monitorMSTeamsProvider(opts) {
|
|
3777
3804
|
const core = require_runtime.getMSTeamsRuntime();
|
|
3778
3805
|
const log = core.logging.getChildLogger({ name: "msteams" });
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3806
|
+
const account = require_resolve_allowlist.resolveMSTeamsRuntimeAccount(opts);
|
|
3807
|
+
const { accountId, credentials: creds } = account;
|
|
3808
|
+
const accountPolicyCfg = opts.cfg;
|
|
3809
|
+
let cfg = accountPolicyCfg;
|
|
3810
|
+
let msteamsCfg = account.config;
|
|
3811
|
+
if (opts.cfg.channels?.msteams?.enabled !== true || msteamsCfg.enabled === false) {
|
|
3782
3812
|
log.debug?.("msteams provider disabled");
|
|
3783
3813
|
publishMSTeamsBlocked(opts.statusSink, "Microsoft Teams provider is disabled");
|
|
3784
3814
|
return {
|
|
@@ -3786,7 +3816,6 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3786
3816
|
shutdown: async () => {}
|
|
3787
3817
|
};
|
|
3788
3818
|
}
|
|
3789
|
-
const creds = require_token_config.resolveMSTeamsCredentials(msteamsCfg);
|
|
3790
3819
|
if (!creds) {
|
|
3791
3820
|
log.error("msteams credentials not configured");
|
|
3792
3821
|
publishMSTeamsBlocked(opts.statusSink, "Microsoft Teams credentials are not configured");
|
|
@@ -3795,6 +3824,8 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3795
3824
|
shutdown: async () => {}
|
|
3796
3825
|
};
|
|
3797
3826
|
}
|
|
3827
|
+
const collisionIssue = require_channel.resolveMSTeamsWebhookCollisionIssue(accountPolicyCfg);
|
|
3828
|
+
if (collisionIssue) throw new Error(collisionIssue);
|
|
3798
3829
|
const appId = creds.appId;
|
|
3799
3830
|
const runtime = opts.runtime ?? {
|
|
3800
3831
|
log: console.log,
|
|
@@ -3803,6 +3834,11 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3803
3834
|
throw new Error(`exit ${code}`);
|
|
3804
3835
|
}
|
|
3805
3836
|
};
|
|
3837
|
+
cfg = require_resolve_allowlist.withAccountScopedMSTeamsConfig({
|
|
3838
|
+
cfg,
|
|
3839
|
+
accountId,
|
|
3840
|
+
accountConfig: msteamsCfg
|
|
3841
|
+
});
|
|
3806
3842
|
const configuredAllowFrom = msteamsCfg.allowFrom;
|
|
3807
3843
|
const configuredGroupAllowFrom = msteamsCfg.groupAllowFrom;
|
|
3808
3844
|
let allowFrom = require_resolve_allowlist.projectStableMSTeamsUserAllowlist(configuredAllowFrom);
|
|
@@ -3816,6 +3852,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3816
3852
|
const resolveAllowlistUsers = async (label, entries) => {
|
|
3817
3853
|
const resolved = await require_resolve_allowlist.resolveMSTeamsUserAllowlist({
|
|
3818
3854
|
cfg,
|
|
3855
|
+
accountId,
|
|
3819
3856
|
entries
|
|
3820
3857
|
});
|
|
3821
3858
|
const additions = [];
|
|
@@ -3853,6 +3890,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3853
3890
|
if (msteamsCfg.teams && Object.keys(msteamsCfg.teams).length > 0) {
|
|
3854
3891
|
const resolved = await require_resolve_allowlist.resolveMSTeamsTeamsConfig({
|
|
3855
3892
|
cfg,
|
|
3893
|
+
accountId,
|
|
3856
3894
|
teamIdMode: "bot-framework",
|
|
3857
3895
|
teams: msteamsCfg.teams
|
|
3858
3896
|
});
|
|
@@ -3872,15 +3910,17 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3872
3910
|
groupAllowFrom,
|
|
3873
3911
|
teams: teamsConfig
|
|
3874
3912
|
};
|
|
3875
|
-
cfg = {
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
}
|
|
3881
|
-
};
|
|
3913
|
+
cfg = require_resolve_allowlist.withAccountScopedMSTeamsConfig({
|
|
3914
|
+
cfg,
|
|
3915
|
+
accountId,
|
|
3916
|
+
accountConfig: msteamsCfg
|
|
3917
|
+
});
|
|
3882
3918
|
const legacyListener = require_channel.resolveMSTeamsLegacyWebhook(msteamsCfg);
|
|
3883
|
-
const pathIssue = require_channel.resolveMSTeamsWebhookPathIssue({
|
|
3919
|
+
const pathIssue = require_channel.resolveMSTeamsWebhookPathIssue({
|
|
3920
|
+
cfg: accountPolicyCfg,
|
|
3921
|
+
accountId,
|
|
3922
|
+
accountConfig: msteamsCfg
|
|
3923
|
+
});
|
|
3884
3924
|
if (pathIssue) {
|
|
3885
3925
|
if (!legacyListener) throw new Error(pathIssue);
|
|
3886
3926
|
log.warn?.(pathIssue);
|
|
@@ -3890,8 +3930,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3890
3930
|
cfg,
|
|
3891
3931
|
resolveChannelLimitMb: ({ cfg: channelCfg }) => channelCfg.channels?.msteams?.mediaMaxMb
|
|
3892
3932
|
}) ?? 8388608;
|
|
3893
|
-
const conversationStore
|
|
3894
|
-
const pollStore = opts.pollStore ?? require_conversation_store_state.createMSTeamsPollStoreState();
|
|
3933
|
+
const { conversationStore, pollStore } = createMSTeamsMonitorStores(accountId, opts);
|
|
3895
3934
|
log.info("starting provider on Gateway HTTP routes");
|
|
3896
3935
|
const express = await import("express");
|
|
3897
3936
|
const expressApp = express.default();
|
|
@@ -3920,7 +3959,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3920
3959
|
messagingEndpoint: configuredPath,
|
|
3921
3960
|
...ssoConnectionName ? { oauthDefaultConnectionName: ssoConnectionName } : {}
|
|
3922
3961
|
});
|
|
3923
|
-
if (configuredPath !== "/api/messages") {
|
|
3962
|
+
if (accountId === openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID && configuredPath !== "/api/messages") {
|
|
3924
3963
|
let warnedLegacyMessagesRoute = false;
|
|
3925
3964
|
expressApp.post("/api/messages", (req, res, next) => {
|
|
3926
3965
|
if (!warnedLegacyMessagesRoute) {
|
|
@@ -3933,12 +3972,14 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3933
3972
|
}
|
|
3934
3973
|
const tokenProvider = require_resolve_allowlist.createMSTeamsTokenProvider(app);
|
|
3935
3974
|
const ssoDeps = ssoConnectionName ? {
|
|
3936
|
-
tokenStore: createMSTeamsSsoTokenStoreFs(),
|
|
3975
|
+
tokenStore: createMSTeamsSsoTokenStoreFs({ accountId }),
|
|
3937
3976
|
connectionName: ssoConnectionName
|
|
3938
3977
|
} : void 0;
|
|
3939
3978
|
if (ssoDeps) log.debug?.("msteams sso enabled", { connectionName: ssoDeps.connectionName });
|
|
3940
3979
|
const handlerDeps = {
|
|
3941
3980
|
cfg,
|
|
3981
|
+
accountPolicyCfg,
|
|
3982
|
+
accountId,
|
|
3942
3983
|
runtime,
|
|
3943
3984
|
appId,
|
|
3944
3985
|
app,
|
|
@@ -3949,13 +3990,14 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3949
3990
|
pollStore,
|
|
3950
3991
|
log
|
|
3951
3992
|
};
|
|
3993
|
+
handlerDeps.readConfig = createMSTeamsHandlerConfigReader(handlerDeps);
|
|
3952
3994
|
const handleActivity = createMSTeamsActivityHandler(handlerDeps);
|
|
3953
3995
|
const ingress = createMSTeamsIngress({
|
|
3954
3996
|
accountId: appId,
|
|
3955
3997
|
runtime,
|
|
3956
3998
|
dispatch: async (activity, lifecycle, liveContext) => {
|
|
3957
3999
|
if (liveContext) liveContext.activity = activity;
|
|
3958
|
-
const context = liveContext ?? createMSTeamsReplayContext(activity, app, require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg));
|
|
4000
|
+
const context = liveContext ?? createMSTeamsReplayContext(activity, app, require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg), accountId);
|
|
3959
4001
|
return await handleActivity(context, lifecycle);
|
|
3960
4002
|
}
|
|
3961
4003
|
});
|
|
@@ -4065,6 +4107,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4065
4107
|
return;
|
|
4066
4108
|
}
|
|
4067
4109
|
await Promise.all(userIds.map((userId) => ssoDeps.tokenStore.save({
|
|
4110
|
+
...accountId === openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID ? {} : { accountId },
|
|
4068
4111
|
connectionName,
|
|
4069
4112
|
userId,
|
|
4070
4113
|
token: ctx.token.token,
|
|
@@ -4111,7 +4154,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4111
4154
|
auth: "plugin",
|
|
4112
4155
|
pluginId: "msteams",
|
|
4113
4156
|
source: "msteams-webhook",
|
|
4114
|
-
accountId
|
|
4157
|
+
accountId,
|
|
4115
4158
|
handler: webhook.handler,
|
|
4116
4159
|
legacyListener: legacyListener ? {
|
|
4117
4160
|
...legacyListener,
|
|
@@ -4124,12 +4167,12 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
4124
4167
|
throwOnFailure: true,
|
|
4125
4168
|
log: (message) => log.warn?.(message)
|
|
4126
4169
|
}));
|
|
4127
|
-
if (configuredPath !== "/api/messages") unregisterRoutes.push((0, openclaw_plugin_sdk_webhook_targets.registerPluginHttpRoute)({
|
|
4170
|
+
if (accountId === openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID && configuredPath !== "/api/messages") unregisterRoutes.push((0, openclaw_plugin_sdk_webhook_targets.registerPluginHttpRoute)({
|
|
4128
4171
|
path: "/api/messages",
|
|
4129
4172
|
auth: "plugin",
|
|
4130
4173
|
pluginId: "msteams",
|
|
4131
4174
|
source: "msteams-webhook-alias",
|
|
4132
|
-
accountId
|
|
4175
|
+
accountId,
|
|
4133
4176
|
handler: webhook.handler,
|
|
4134
4177
|
log: (message) => log.warn?.(message)
|
|
4135
4178
|
}));
|