@openclaw/msteams 2026.7.1 → 2026.7.2-beta.1
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/api.cjs +8 -0
- package/dist/{channel-Cf5LJ4aO.js → channel-BSyPdpGN.cjs} +641 -227
- package/dist/channel-config-api.cjs +3 -0
- package/dist/channel-plugin-api.cjs +3 -0
- package/dist/channel.runtime-DQTOzzet.cjs +1372 -0
- package/dist/{config-schema-BL4qQZiA.js → config-schema-BSNLJUta.cjs} +13 -3
- package/dist/{directory-contract-api.js → directory-contract-api.cjs} +8 -7
- package/dist/{doctor-contract-api.js → doctor-contract-api.cjs} +73 -57
- package/dist/index.cjs +21 -0
- package/dist/{probe-BnyZRZsD.js → messenger-DtS9gqkV.cjs} +369 -861
- package/dist/{oauth-BifWxWzq.js → oauth-C5E5powI.cjs} +21 -21
- package/dist/{polls-B_hs4aKm.js → polls-BhoI4VaN.cjs} +188 -38
- package/dist/{resolve-allowlist-Dnw4aNOu.js → resolve-allowlist-CyQ57MfH.cjs} +817 -140
- package/dist/rolldown-runtime-D6vf50IK.cjs +28 -0
- package/dist/runtime-api.cjs +207 -0
- package/dist/runtime-gZTAuFux.cjs +24 -0
- package/dist/secret-contract-api.cjs +31 -0
- package/dist/setup-entry.cjs +14 -0
- package/dist/setup-plugin-api.cjs +65 -0
- package/dist/{setup-surface-qW-ECHL-.js → setup-surface-BDTPN3dX.cjs} +123 -49
- package/dist/{src-DZFs5XtZ.js → src-CUMz2B6f.cjs} +672 -740
- package/dist/{sso-token-store-DrVbnCUF.js → sso-token-store-CktCz88k.cjs} +51 -10
- package/npm-shrinkwrap.json +3 -3
- package/openclaw.plugin.json +10 -31
- package/package.json +8 -7
- package/dist/api.js +0 -3
- package/dist/channel-config-api.js +0 -2
- package/dist/channel-plugin-api.js +0 -2
- package/dist/channel.runtime-Bwu6Tjhf.js +0 -664
- package/dist/index.js +0 -22
- package/dist/runtime-BS5AZrKK.js +0 -8
- package/dist/runtime-api.js +0 -20
- package/dist/secret-contract-api.js +0 -35
- package/dist/setup-entry.js +0 -15
- package/dist/setup-plugin-api.js +0 -64
|
@@ -1,35 +1,45 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
const require_rolldown_runtime = require("./rolldown-runtime-D6vf50IK.cjs");
|
|
2
|
+
const require_runtime = require("./runtime-gZTAuFux.cjs");
|
|
3
|
+
require("./runtime-api.cjs");
|
|
4
|
+
const require_resolve_allowlist = require("./resolve-allowlist-CyQ57MfH.cjs");
|
|
5
|
+
const require_channel = require("./channel-BSyPdpGN.cjs");
|
|
6
|
+
const require_setup_surface = require("./setup-surface-BDTPN3dX.cjs");
|
|
7
|
+
const require_polls = require("./polls-BhoI4VaN.cjs");
|
|
8
|
+
const require_sso_token_store = require("./sso-token-store-CktCz88k.cjs");
|
|
9
|
+
const require_messenger = require("./messenger-DtS9gqkV.cjs");
|
|
10
|
+
let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
|
|
11
|
+
let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
|
|
12
|
+
let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
|
|
13
|
+
let openclaw_plugin_sdk_media_runtime = require("openclaw/plugin-sdk/media-runtime");
|
|
14
|
+
let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
|
|
15
|
+
let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
|
|
16
|
+
let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-http");
|
|
17
|
+
let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-runtime");
|
|
18
|
+
let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text-utility-runtime");
|
|
19
|
+
let node_path = require("node:path");
|
|
20
|
+
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
21
|
+
let openclaw_plugin_sdk_security_runtime = require("openclaw/plugin-sdk/security-runtime");
|
|
22
|
+
let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
|
|
23
|
+
let node_crypto = require("node:crypto");
|
|
24
|
+
node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
|
|
25
|
+
let openclaw_plugin_sdk_channel_ingress_runtime = require("openclaw/plugin-sdk/channel-ingress-runtime");
|
|
26
|
+
let openclaw_plugin_sdk_context_visibility_runtime = require("openclaw/plugin-sdk/context-visibility-runtime");
|
|
27
|
+
let openclaw_plugin_sdk_reply_history = require("openclaw/plugin-sdk/reply-history");
|
|
28
|
+
let openclaw_plugin_sdk_dedupe_runtime = require("openclaw/plugin-sdk/dedupe-runtime");
|
|
29
|
+
let openclaw_plugin_sdk_collection_runtime = require("openclaw/plugin-sdk/collection-runtime");
|
|
30
|
+
let openclaw_plugin_sdk_channel_pairing = require("openclaw/plugin-sdk/channel-pairing");
|
|
31
|
+
let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
|
|
32
|
+
let openclaw_plugin_sdk_runtime_group_policy = require("openclaw/plugin-sdk/runtime-group-policy");
|
|
33
|
+
let openclaw_plugin_sdk_dangerous_name_runtime = require("openclaw/plugin-sdk/dangerous-name-runtime");
|
|
34
|
+
let openclaw_plugin_sdk_reply_payload = require("openclaw/plugin-sdk/reply-payload");
|
|
35
|
+
let openclaw_plugin_sdk_webhook_ingress = require("openclaw/plugin-sdk/webhook-ingress");
|
|
26
36
|
//#region extensions/msteams/src/feedback-reflection-prompt.ts
|
|
27
37
|
/** Max chars of the thumbed-down response to include in the reflection prompt. */
|
|
28
38
|
const MAX_RESPONSE_CHARS = 500;
|
|
29
39
|
function buildReflectionPrompt(params) {
|
|
30
40
|
const parts = ["A user indicated your previous response wasn't helpful."];
|
|
31
41
|
if (params.thumbedDownResponse) {
|
|
32
|
-
const truncated = params.thumbedDownResponse.length > MAX_RESPONSE_CHARS ? `${truncateUtf16Safe(params.thumbedDownResponse, MAX_RESPONSE_CHARS)}...` : params.thumbedDownResponse;
|
|
42
|
+
const truncated = params.thumbedDownResponse.length > MAX_RESPONSE_CHARS ? `${(0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(params.thumbedDownResponse, MAX_RESPONSE_CHARS)}...` : params.thumbedDownResponse;
|
|
33
43
|
parts.push(`\nYour response was:\n> ${truncated}`);
|
|
34
44
|
}
|
|
35
45
|
if (params.userComment) parts.push(`\nUser's comment: "${params.userComment}"`);
|
|
@@ -39,7 +49,7 @@ function buildReflectionPrompt(params) {
|
|
|
39
49
|
function parseBooleanLike(value) {
|
|
40
50
|
if (typeof value === "boolean") return value;
|
|
41
51
|
if (typeof value === "string") {
|
|
42
|
-
const normalized = normalizeOptionalLowercaseString(value);
|
|
52
|
+
const normalized = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(value);
|
|
43
53
|
if (normalized === "true" || normalized === "yes") return true;
|
|
44
54
|
if (normalized === "false" || normalized === "no") return false;
|
|
45
55
|
}
|
|
@@ -79,10 +89,10 @@ const MAX_COOLDOWN_ENTRIES = 500;
|
|
|
79
89
|
const LEARNINGS_NAMESPACE = "feedback-learnings";
|
|
80
90
|
const MAX_LEARNING_ENTRIES = 1e4;
|
|
81
91
|
function learningStoreKey(storePath, sessionKey) {
|
|
82
|
-
return
|
|
92
|
+
return node_crypto.default.createHash("sha256").update(`${storePath}\0${sessionKey}`, "utf8").digest("hex");
|
|
83
93
|
}
|
|
84
94
|
function openLearningStore() {
|
|
85
|
-
return getMSTeamsRuntime().state.openKeyedStore({
|
|
95
|
+
return require_runtime.getMSTeamsRuntime().state.openKeyedStore({
|
|
86
96
|
namespace: LEARNINGS_NAMESPACE,
|
|
87
97
|
maxEntries: MAX_LEARNING_ENTRIES
|
|
88
98
|
});
|
|
@@ -134,7 +144,7 @@ function buildFeedbackEvent(params) {
|
|
|
134
144
|
};
|
|
135
145
|
}
|
|
136
146
|
function buildReflectionContext(params) {
|
|
137
|
-
const core = getMSTeamsRuntime();
|
|
147
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
138
148
|
const envelopeOptions = core.channel.reply.resolveEnvelopeFormatOptions(params.cfg);
|
|
139
149
|
const body = core.channel.reply.formatAgentEnvelope({
|
|
140
150
|
channel: "Teams",
|
|
@@ -163,7 +173,7 @@ function buildReflectionContext(params) {
|
|
|
163
173
|
}) };
|
|
164
174
|
}
|
|
165
175
|
function createReflectionCaptureDispatcher(params) {
|
|
166
|
-
const core = getMSTeamsRuntime();
|
|
176
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
167
177
|
let response = "";
|
|
168
178
|
const { dispatcher, replyOptions } = core.channel.reply.createReplyDispatcherWithTyping({
|
|
169
179
|
deliver: async (payload) => {
|
|
@@ -176,7 +186,7 @@ function createReflectionCaptureDispatcher(params) {
|
|
|
176
186
|
},
|
|
177
187
|
humanDelay: core.channel.reply.resolveHumanDelayConfig(params.cfg, params.agentId),
|
|
178
188
|
onError: (err) => {
|
|
179
|
-
params.log.debug?.("reflection reply error", { error: formatUnknownError(err) });
|
|
189
|
+
params.log.debug?.("reflection reply error", { error: require_setup_surface.formatUnknownError(err) });
|
|
180
190
|
}
|
|
181
191
|
});
|
|
182
192
|
return {
|
|
@@ -186,11 +196,11 @@ function createReflectionCaptureDispatcher(params) {
|
|
|
186
196
|
};
|
|
187
197
|
}
|
|
188
198
|
async function sendReflectionFollowUp(params) {
|
|
189
|
-
const baseRef = buildConversationReference(params.conversationRef);
|
|
190
|
-
await sendMSTeamsActivityWithReference(params.app, baseRef, {
|
|
199
|
+
const baseRef = require_messenger.buildConversationReference(params.conversationRef);
|
|
200
|
+
await require_messenger.sendMSTeamsActivityWithReference(params.app, baseRef, {
|
|
191
201
|
type: "message",
|
|
192
202
|
text: params.userMessage
|
|
193
|
-
}, { serviceUrlBoundary: resolveMSTeamsSdkCloudOptions(params.cfg.channels?.msteams) });
|
|
203
|
+
}, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(params.cfg.channels?.msteams) });
|
|
194
204
|
}
|
|
195
205
|
/**
|
|
196
206
|
* Run a background reflection after negative feedback.
|
|
@@ -207,7 +217,7 @@ async function runFeedbackReflection(params) {
|
|
|
207
217
|
thumbedDownResponse: params.thumbedDownResponse,
|
|
208
218
|
userComment: params.userComment
|
|
209
219
|
});
|
|
210
|
-
const storePath = getMSTeamsRuntime().channel.session.resolveStorePath(cfg.session?.store, { agentId: params.agentId });
|
|
220
|
+
const storePath = require_runtime.getMSTeamsRuntime().channel.session.resolveStorePath(cfg.session?.store, { agentId: params.agentId });
|
|
211
221
|
const { ctxPayload } = buildReflectionContext({
|
|
212
222
|
cfg,
|
|
213
223
|
conversationId: params.conversationId,
|
|
@@ -220,7 +230,7 @@ async function runFeedbackReflection(params) {
|
|
|
220
230
|
log
|
|
221
231
|
});
|
|
222
232
|
try {
|
|
223
|
-
await dispatchReplyFromConfigWithSettledDispatcher
|
|
233
|
+
await (0, openclaw_plugin_sdk_channel_inbound.dispatchReplyFromConfigWithSettledDispatcher)({
|
|
224
234
|
ctxPayload,
|
|
225
235
|
cfg,
|
|
226
236
|
dispatcher: capture.dispatcher,
|
|
@@ -228,7 +238,7 @@ async function runFeedbackReflection(params) {
|
|
|
228
238
|
replyOptions: capture.replyOptions
|
|
229
239
|
});
|
|
230
240
|
} catch (err) {
|
|
231
|
-
log.error("reflection dispatch failed", { error: formatUnknownError(err) });
|
|
241
|
+
log.error("reflection dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
232
242
|
return;
|
|
233
243
|
}
|
|
234
244
|
const reflectionResponse = capture.readResponse().trim();
|
|
@@ -254,9 +264,9 @@ async function runFeedbackReflection(params) {
|
|
|
254
264
|
learning: parsedReflection.learning
|
|
255
265
|
});
|
|
256
266
|
} catch (err) {
|
|
257
|
-
log.debug?.("failed to store reflection learning", { error: formatUnknownError(err) });
|
|
267
|
+
log.debug?.("failed to store reflection learning", { error: require_setup_surface.formatUnknownError(err) });
|
|
258
268
|
}
|
|
259
|
-
const conversationType = normalizeOptionalLowercaseString(params.conversationRef.conversation?.conversationType);
|
|
269
|
+
const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationRef.conversation?.conversationType);
|
|
260
270
|
if (!(conversationType === "personal" && parsedReflection.followUp && Boolean(parsedReflection.userMessage))) {
|
|
261
271
|
if (parsedReflection.followUp && conversationType !== "personal") log.debug?.("skipping reflection follow-up outside direct message", {
|
|
262
272
|
sessionKey,
|
|
@@ -273,22 +283,22 @@ async function runFeedbackReflection(params) {
|
|
|
273
283
|
});
|
|
274
284
|
log.info("sent reflection follow-up", { sessionKey });
|
|
275
285
|
} catch (err) {
|
|
276
|
-
log.debug?.("failed to send reflection follow-up", { error: formatUnknownError(err) });
|
|
286
|
+
log.debug?.("failed to send reflection follow-up", { error: require_setup_surface.formatUnknownError(err) });
|
|
277
287
|
}
|
|
278
288
|
}
|
|
279
289
|
//#endregion
|
|
280
290
|
//#region extensions/msteams/src/adaptive-card-submit.ts
|
|
281
291
|
function extractAdaptiveCardSubmittedData(value) {
|
|
282
|
-
if (!isRecord(value)) return value;
|
|
283
|
-
const action = isRecord(value.action) ? value.action : void 0;
|
|
284
|
-
if (action && normalizeOptionalLowercaseString(action.type) === "action.submit" && "data" in action) return action.data;
|
|
292
|
+
if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value)) return value;
|
|
293
|
+
const action = (0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value.action) ? value.action : void 0;
|
|
294
|
+
if (action && (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(action.type) === "action.submit" && "data" in action) return action.data;
|
|
285
295
|
return value;
|
|
286
296
|
}
|
|
287
297
|
function readMSTeamsImBackValue(value) {
|
|
288
|
-
if (!isRecord(value)) return null;
|
|
289
|
-
const msteams = isRecord(value.msteams) ? value.msteams : void 0;
|
|
290
|
-
if (!msteams || normalizeOptionalLowercaseString(msteams.type) !== "imback") return null;
|
|
291
|
-
return normalizeOptionalString(msteams.value) ?? null;
|
|
298
|
+
if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value)) return null;
|
|
299
|
+
const msteams = (0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value.msteams) ? value.msteams : void 0;
|
|
300
|
+
if (!msteams || (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(msteams.type) !== "imback") return null;
|
|
301
|
+
return (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(msteams.value) ?? null;
|
|
292
302
|
}
|
|
293
303
|
function serializeMSTeamsAdaptiveCardActionValue(value) {
|
|
294
304
|
const submittedValue = extractAdaptiveCardSubmittedData(value);
|
|
@@ -321,28 +331,28 @@ const msteamsIngressIdentity = {
|
|
|
321
331
|
resolveEntryId: ({ entryIndex, fieldKey }) => `msteams-entry-${entryIndex + 1}:${fieldKey === "sender-name" ? "name" : "id"}`
|
|
322
332
|
};
|
|
323
333
|
function normalizeIngressValue(value) {
|
|
324
|
-
return normalizeOptionalLowercaseString(value) ?? null;
|
|
334
|
+
return (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(value) ?? null;
|
|
325
335
|
}
|
|
326
336
|
async function resolveMSTeamsSenderAccess(params) {
|
|
327
337
|
const activity = params.activity;
|
|
328
338
|
const msteamsCfg = params.cfg.channels?.msteams;
|
|
329
|
-
const conversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "unknown");
|
|
330
|
-
const convType = normalizeOptionalLowercaseString(activity.conversation?.conversationType);
|
|
339
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "unknown");
|
|
340
|
+
const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(activity.conversation?.conversationType);
|
|
331
341
|
const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
|
|
332
342
|
const senderId = activity.from?.aadObjectId ?? activity.from?.id ?? "unknown";
|
|
333
343
|
const senderName = activity.from?.name ?? activity.from?.id ?? senderId;
|
|
334
|
-
const pairing = createChannelPairingController({
|
|
335
|
-
core: getMSTeamsRuntime(),
|
|
344
|
+
const pairing = (0, openclaw_plugin_sdk_channel_pairing.createChannelPairingController)({
|
|
345
|
+
core: require_runtime.getMSTeamsRuntime(),
|
|
336
346
|
channel: "msteams",
|
|
337
|
-
accountId: DEFAULT_ACCOUNT_ID
|
|
347
|
+
accountId: openclaw_plugin_sdk_account_id.DEFAULT_ACCOUNT_ID
|
|
338
348
|
});
|
|
339
349
|
const dmPolicy = msteamsCfg?.dmPolicy ?? "pairing";
|
|
340
350
|
const configuredDmAllowFrom = msteamsCfg?.allowFrom ?? [];
|
|
341
351
|
const groupAllowFrom = msteamsCfg?.groupAllowFrom;
|
|
342
|
-
const defaultGroupPolicy = resolveDefaultGroupPolicy(params.cfg);
|
|
352
|
+
const defaultGroupPolicy = (0, openclaw_plugin_sdk_runtime_group_policy.resolveDefaultGroupPolicy)(params.cfg);
|
|
343
353
|
const groupPolicy = !isDirectMessage && msteamsCfg ? msteamsCfg.groupPolicy ?? defaultGroupPolicy ?? "allowlist" : "disabled";
|
|
344
|
-
const allowNameMatching = isDangerousNameMatchingEnabled(msteamsCfg);
|
|
345
|
-
const channelGate = resolveMSTeamsRouteConfig({
|
|
354
|
+
const allowNameMatching = (0, openclaw_plugin_sdk_dangerous_name_runtime.isDangerousNameMatchingEnabled)(msteamsCfg);
|
|
355
|
+
const channelGate = require_channel.resolveMSTeamsRouteConfig({
|
|
346
356
|
cfg: msteamsCfg,
|
|
347
357
|
teamId: activity.channelData?.team?.id,
|
|
348
358
|
teamName: activity.channelData?.team?.name,
|
|
@@ -351,7 +361,7 @@ async function resolveMSTeamsSenderAccess(params) {
|
|
|
351
361
|
allowNameMatching
|
|
352
362
|
});
|
|
353
363
|
return {
|
|
354
|
-
...await resolveStableChannelMessageIngress({
|
|
364
|
+
...await (0, openclaw_plugin_sdk_channel_ingress_runtime.resolveStableChannelMessageIngress)({
|
|
355
365
|
channelId: "msteams",
|
|
356
366
|
accountId: pairing.accountId,
|
|
357
367
|
identity: msteamsIngressIdentity,
|
|
@@ -366,7 +376,7 @@ async function resolveMSTeamsSenderAccess(params) {
|
|
|
366
376
|
id: conversationId,
|
|
367
377
|
parentId: activity.channelData?.team?.id
|
|
368
378
|
},
|
|
369
|
-
route: channelIngressRoutes(!isDirectMessage && channelGate.allowlistConfigured && {
|
|
379
|
+
route: (0, openclaw_plugin_sdk_channel_ingress_runtime.channelIngressRoutes)(!isDirectMessage && channelGate.allowlistConfigured && {
|
|
370
380
|
id: "msteams:team-channel",
|
|
371
381
|
kind: "nestedAllowlist",
|
|
372
382
|
allowed: channelGate.allowed,
|
|
@@ -426,8 +436,8 @@ function normalizeServiceUrl(serviceUrl) {
|
|
|
426
436
|
return serviceUrl.replace(/\/+$/, "");
|
|
427
437
|
}
|
|
428
438
|
function buildBotFrameworkAttachmentHeaders(params) {
|
|
429
|
-
const headers = ensureUserAgentHeader();
|
|
430
|
-
applyAuthorizationHeaderForUrl({
|
|
439
|
+
const headers = require_resolve_allowlist.ensureUserAgentHeader();
|
|
440
|
+
require_resolve_allowlist.applyAuthorizationHeaderForUrl({
|
|
431
441
|
headers,
|
|
432
442
|
url: params.url,
|
|
433
443
|
authAllowHosts: params.policy.authAllowHosts,
|
|
@@ -439,7 +449,7 @@ async function fetchBotFrameworkAttachmentInfo(params) {
|
|
|
439
449
|
const url = `${normalizeServiceUrl(params.serviceUrl)}/v3/attachments/${encodeURIComponent(params.attachmentId)}`;
|
|
440
450
|
let response;
|
|
441
451
|
try {
|
|
442
|
-
response = await safeFetchWithPolicy({
|
|
452
|
+
response = await require_resolve_allowlist.safeFetchWithPolicy({
|
|
443
453
|
url,
|
|
444
454
|
policy: params.policy,
|
|
445
455
|
fetchFn: params.fetchFn,
|
|
@@ -449,7 +459,8 @@ async function fetchBotFrameworkAttachmentInfo(params) {
|
|
|
449
459
|
url,
|
|
450
460
|
accessToken: params.accessToken,
|
|
451
461
|
policy: params.policy
|
|
452
|
-
}) }
|
|
462
|
+
}) },
|
|
463
|
+
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
453
464
|
});
|
|
454
465
|
} catch (err) {
|
|
455
466
|
params.logger?.warn?.("msteams botFramework attachmentInfo fetch failed", { error: err instanceof Error ? err.message : String(err) });
|
|
@@ -461,7 +472,7 @@ async function fetchBotFrameworkAttachmentInfo(params) {
|
|
|
461
472
|
return;
|
|
462
473
|
}
|
|
463
474
|
try {
|
|
464
|
-
return await readProviderJsonResponse(response, "msteams botFramework attachmentInfo");
|
|
475
|
+
return await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(response, "msteams botFramework attachmentInfo");
|
|
465
476
|
} catch (err) {
|
|
466
477
|
params.logger?.warn?.("msteams botFramework attachmentInfo parse failed", { error: err instanceof Error ? err.message : String(err) });
|
|
467
478
|
return;
|
|
@@ -471,7 +482,7 @@ async function saveBotFrameworkAttachmentView(params) {
|
|
|
471
482
|
const url = `${normalizeServiceUrl(params.serviceUrl)}/v3/attachments/${encodeURIComponent(params.attachmentId)}/views/${encodeURIComponent(params.viewId)}`;
|
|
472
483
|
let response;
|
|
473
484
|
try {
|
|
474
|
-
response = await safeFetchWithPolicy({
|
|
485
|
+
response = await require_resolve_allowlist.safeFetchWithPolicy({
|
|
475
486
|
url,
|
|
476
487
|
policy: params.policy,
|
|
477
488
|
fetchFn: params.fetchFn,
|
|
@@ -481,7 +492,8 @@ async function saveBotFrameworkAttachmentView(params) {
|
|
|
481
492
|
url,
|
|
482
493
|
accessToken: params.accessToken,
|
|
483
494
|
policy: params.policy
|
|
484
|
-
}) }
|
|
495
|
+
}) },
|
|
496
|
+
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
485
497
|
});
|
|
486
498
|
} catch (err) {
|
|
487
499
|
params.logger?.warn?.("msteams botFramework attachmentView fetch failed", { error: err instanceof Error ? err.message : String(err) });
|
|
@@ -494,7 +506,7 @@ async function saveBotFrameworkAttachmentView(params) {
|
|
|
494
506
|
}
|
|
495
507
|
let contentLength;
|
|
496
508
|
try {
|
|
497
|
-
contentLength = parseMediaContentLength(response.headers.get("content-length"));
|
|
509
|
+
contentLength = (0, openclaw_plugin_sdk_media_runtime.parseMediaContentLength)(response.headers.get("content-length"));
|
|
498
510
|
} catch (err) {
|
|
499
511
|
await response.body?.cancel();
|
|
500
512
|
params.logger?.warn?.("msteams botFramework attachmentView invalid content-length", { error: err instanceof Error ? err.message : String(err) });
|
|
@@ -505,7 +517,7 @@ async function saveBotFrameworkAttachmentView(params) {
|
|
|
505
517
|
return;
|
|
506
518
|
}
|
|
507
519
|
try {
|
|
508
|
-
return await getMSTeamsRuntime().channel.media.saveResponseMedia(response, {
|
|
520
|
+
return await require_runtime.getMSTeamsRuntime().channel.media.saveResponseMedia(response, {
|
|
509
521
|
sourceUrl: url,
|
|
510
522
|
filePathHint: params.fileNameHint,
|
|
511
523
|
maxBytes: params.maxBytes,
|
|
@@ -516,6 +528,8 @@ async function saveBotFrameworkAttachmentView(params) {
|
|
|
516
528
|
} catch (err) {
|
|
517
529
|
params.logger?.warn?.("msteams botFramework attachmentView save failed", { error: err instanceof Error ? err.message : String(err) });
|
|
518
530
|
return;
|
|
531
|
+
} finally {
|
|
532
|
+
await response.body?.cancel().catch(() => void 0);
|
|
519
533
|
}
|
|
520
534
|
}
|
|
521
535
|
/**
|
|
@@ -526,14 +540,19 @@ async function saveBotFrameworkAttachmentView(params) {
|
|
|
526
540
|
*/
|
|
527
541
|
async function downloadMSTeamsBotFrameworkAttachment(params) {
|
|
528
542
|
if (!params.serviceUrl || !params.attachmentId || !params.tokenProvider) return;
|
|
529
|
-
const
|
|
543
|
+
const tokenProvider = params.tokenProvider;
|
|
544
|
+
const policy = require_resolve_allowlist.resolveAttachmentFetchPolicy({
|
|
530
545
|
allowHosts: params.allowHosts,
|
|
531
546
|
authAllowHosts: params.authAllowHosts
|
|
532
547
|
});
|
|
533
|
-
if (!isUrlAllowed(`${normalizeServiceUrl(params.serviceUrl)}/v3/attachments/${encodeURIComponent(params.attachmentId)}`, policy.allowHosts)) return;
|
|
548
|
+
if (!require_resolve_allowlist.isUrlAllowed(`${normalizeServiceUrl(params.serviceUrl)}/v3/attachments/${encodeURIComponent(params.attachmentId)}`, policy.allowHosts)) return;
|
|
534
549
|
let accessToken;
|
|
535
550
|
try {
|
|
536
|
-
accessToken = await
|
|
551
|
+
accessToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
552
|
+
deadline: params.deadline,
|
|
553
|
+
label: "MS Teams Bot Framework token",
|
|
554
|
+
work: () => tokenProvider.getAccessToken(BOT_FRAMEWORK_SCOPE)
|
|
555
|
+
});
|
|
537
556
|
} catch (err) {
|
|
538
557
|
params.logger?.warn?.("msteams botFramework token acquisition failed", { error: err instanceof Error ? err.message : String(err) });
|
|
539
558
|
return;
|
|
@@ -547,7 +566,8 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
|
|
|
547
566
|
fetchFn: params.fetchFn,
|
|
548
567
|
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
549
568
|
resolveFn: params.resolveFn,
|
|
550
|
-
logger: params.logger
|
|
569
|
+
logger: params.logger,
|
|
570
|
+
deadline: params.deadline
|
|
551
571
|
});
|
|
552
572
|
if (!info) return;
|
|
553
573
|
const views = Array.isArray(info.views) ? info.views : [];
|
|
@@ -570,13 +590,14 @@ async function downloadMSTeamsBotFrameworkAttachment(params) {
|
|
|
570
590
|
fetchFn: params.fetchFn,
|
|
571
591
|
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
572
592
|
resolveFn: params.resolveFn,
|
|
573
|
-
logger: params.logger
|
|
593
|
+
logger: params.logger,
|
|
594
|
+
deadline: params.deadline
|
|
574
595
|
});
|
|
575
596
|
if (!saved) return;
|
|
576
597
|
return {
|
|
577
598
|
path: saved.path,
|
|
578
599
|
contentType: saved.contentType,
|
|
579
|
-
placeholder: inferPlaceholder({
|
|
600
|
+
placeholder: require_resolve_allowlist.inferPlaceholder({
|
|
580
601
|
contentType: saved.contentType,
|
|
581
602
|
fileName: fileNameHint
|
|
582
603
|
})
|
|
@@ -614,6 +635,7 @@ async function downloadMSTeamsBotFrameworkAttachments(params) {
|
|
|
614
635
|
fetchFn: params.fetchFn,
|
|
615
636
|
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
616
637
|
resolveFn: params.resolveFn,
|
|
638
|
+
deadline: params.deadline,
|
|
617
639
|
fileNameHint: params.fileNameHint,
|
|
618
640
|
contentTypeHint: params.contentTypeHint,
|
|
619
641
|
preserveFilenames: params.preserveFilenames,
|
|
@@ -639,13 +661,18 @@ async function downloadMSTeamsBotFrameworkAttachments(params) {
|
|
|
639
661
|
* sequence while keeping redirect and DNS pinning inside `safeFetchWithPolicy`.
|
|
640
662
|
*/
|
|
641
663
|
async function saveRemoteMediaDirect(params) {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
664
|
+
const response = await params.fetchImpl(params.url, { redirect: "follow" });
|
|
665
|
+
try {
|
|
666
|
+
return await (0, openclaw_plugin_sdk_media_runtime.saveResponseMedia)(response, {
|
|
667
|
+
sourceUrl: params.url,
|
|
668
|
+
filePathHint: params.filePathHint,
|
|
669
|
+
maxBytes: params.maxBytes,
|
|
670
|
+
fallbackContentType: params.contentTypeHint,
|
|
671
|
+
originalFilename: params.originalFilename
|
|
672
|
+
});
|
|
673
|
+
} finally {
|
|
674
|
+
await response.body?.cancel().catch(() => void 0);
|
|
675
|
+
}
|
|
649
676
|
}
|
|
650
677
|
async function downloadAndStoreMSTeamsRemoteMedia(params) {
|
|
651
678
|
const originalFilename = params.preserveFilenames ? params.filePathHint : void 0;
|
|
@@ -658,7 +685,7 @@ async function downloadAndStoreMSTeamsRemoteMedia(params) {
|
|
|
658
685
|
contentTypeHint: params.contentTypeHint,
|
|
659
686
|
originalFilename
|
|
660
687
|
});
|
|
661
|
-
else saved = await getMSTeamsRuntime().channel.media.saveRemoteMedia({
|
|
688
|
+
else saved = await require_runtime.getMSTeamsRuntime().channel.media.saveRemoteMedia({
|
|
662
689
|
url: params.url,
|
|
663
690
|
fetchImpl: params.fetchImpl,
|
|
664
691
|
filePathHint: params.filePathHint,
|
|
@@ -670,7 +697,7 @@ async function downloadAndStoreMSTeamsRemoteMedia(params) {
|
|
|
670
697
|
return {
|
|
671
698
|
path: saved.path,
|
|
672
699
|
contentType: saved.contentType,
|
|
673
|
-
placeholder: params.placeholder ?? inferPlaceholder({
|
|
700
|
+
placeholder: params.placeholder ?? require_resolve_allowlist.inferPlaceholder({
|
|
674
701
|
contentType: saved.contentType,
|
|
675
702
|
fileName: params.filePathHint
|
|
676
703
|
})
|
|
@@ -679,35 +706,35 @@ async function downloadAndStoreMSTeamsRemoteMedia(params) {
|
|
|
679
706
|
//#endregion
|
|
680
707
|
//#region extensions/msteams/src/attachments/download.ts
|
|
681
708
|
function resolveDownloadCandidate(att) {
|
|
682
|
-
const contentType = normalizeContentType(att.contentType);
|
|
683
|
-
const name = normalizeOptionalString(att.name) ?? "";
|
|
709
|
+
const contentType = require_resolve_allowlist.normalizeContentType(att.contentType);
|
|
710
|
+
const name = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.name) ?? "";
|
|
684
711
|
if (contentType === "application/vnd.microsoft.teams.file.download.info") {
|
|
685
|
-
if (!isRecord
|
|
686
|
-
const downloadUrl = normalizeOptionalString(att.content.downloadUrl) ?? "";
|
|
712
|
+
if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(att.content)) return null;
|
|
713
|
+
const downloadUrl = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.content.downloadUrl) ?? "";
|
|
687
714
|
if (!downloadUrl) return null;
|
|
688
|
-
const fileType = normalizeOptionalString(att.content.fileType) ?? "";
|
|
689
|
-
const uniqueId = normalizeOptionalString(att.content.uniqueId) ?? "";
|
|
690
|
-
const fileName = normalizeOptionalString(att.content.fileName) ?? "";
|
|
715
|
+
const fileType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.content.fileType) ?? "";
|
|
716
|
+
const uniqueId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.content.uniqueId) ?? "";
|
|
717
|
+
const fileName = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.content.fileName) ?? "";
|
|
691
718
|
const fileHint = name || fileName || (uniqueId && fileType ? `${uniqueId}.${fileType}` : "");
|
|
692
719
|
return {
|
|
693
720
|
url: downloadUrl,
|
|
694
721
|
fileHint: fileHint || void 0,
|
|
695
722
|
contentTypeHint: void 0,
|
|
696
|
-
placeholder: inferPlaceholder({
|
|
723
|
+
placeholder: require_resolve_allowlist.inferPlaceholder({
|
|
697
724
|
contentType,
|
|
698
725
|
fileName: fileHint,
|
|
699
726
|
fileType
|
|
700
727
|
})
|
|
701
728
|
};
|
|
702
729
|
}
|
|
703
|
-
const contentUrl = normalizeOptionalString(att.contentUrl) ?? "";
|
|
730
|
+
const contentUrl = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(att.contentUrl) ?? "";
|
|
704
731
|
if (!contentUrl) return null;
|
|
705
|
-
const sharesUrl = tryBuildGraphSharesUrlForSharedLink(contentUrl);
|
|
732
|
+
const sharesUrl = require_resolve_allowlist.tryBuildGraphSharesUrlForSharedLink(contentUrl);
|
|
706
733
|
return {
|
|
707
734
|
url: sharesUrl ?? contentUrl,
|
|
708
735
|
fileHint: name || void 0,
|
|
709
736
|
contentTypeHint: sharesUrl ? void 0 : contentType,
|
|
710
|
-
placeholder: inferPlaceholder({
|
|
737
|
+
placeholder: require_resolve_allowlist.inferPlaceholder({
|
|
711
738
|
contentType,
|
|
712
739
|
fileName: name
|
|
713
740
|
})
|
|
@@ -715,7 +742,7 @@ function resolveDownloadCandidate(att) {
|
|
|
715
742
|
}
|
|
716
743
|
function scopeCandidatesForUrl(url) {
|
|
717
744
|
try {
|
|
718
|
-
const host = normalizeLowercaseStringOrEmpty(new URL(url).hostname);
|
|
745
|
+
const host = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeLowercaseStringOrEmpty)(new URL(url).hostname);
|
|
719
746
|
return host.endsWith("graph.microsoft.com") || host.endsWith("sharepoint.com") || host.endsWith("1drv.ms") || host.includes("sharepoint") ? ["https://graph.microsoft.com", "https://api.botframework.com"] : ["https://api.botframework.com", "https://graph.microsoft.com"];
|
|
720
747
|
} catch {
|
|
721
748
|
return ["https://api.botframework.com", "https://graph.microsoft.com"];
|
|
@@ -725,33 +752,39 @@ function isRedirectStatus(status) {
|
|
|
725
752
|
return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
|
|
726
753
|
}
|
|
727
754
|
async function resolveInlineDataImageMime(inline) {
|
|
728
|
-
const mime = normalizeOptionalLowercaseString(await getMSTeamsRuntime().media.detectMime({
|
|
755
|
+
const mime = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(await require_runtime.getMSTeamsRuntime().media.detectMime({
|
|
729
756
|
buffer: inline.data,
|
|
730
757
|
headerMime: inline.contentType
|
|
731
758
|
}) ?? inline.contentType);
|
|
732
759
|
return mime?.startsWith("image/") ? mime : void 0;
|
|
733
760
|
}
|
|
734
761
|
async function fetchWithAuthFallback(params) {
|
|
735
|
-
const firstAttempt = await safeFetchWithPolicy({
|
|
762
|
+
const firstAttempt = await require_resolve_allowlist.safeFetchWithPolicy({
|
|
736
763
|
url: params.url,
|
|
737
764
|
policy: params.policy,
|
|
738
765
|
fetchFn: params.fetchFn,
|
|
739
766
|
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
740
767
|
requestInit: params.requestInit,
|
|
741
|
-
resolveFn: params.resolveFn
|
|
768
|
+
resolveFn: params.resolveFn,
|
|
769
|
+
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
742
770
|
});
|
|
743
771
|
if (firstAttempt.ok) return firstAttempt;
|
|
744
772
|
if (!params.tokenProvider) return firstAttempt;
|
|
773
|
+
const tokenProvider = params.tokenProvider;
|
|
745
774
|
if (firstAttempt.status !== 401 && firstAttempt.status !== 403) return firstAttempt;
|
|
746
|
-
if (!isUrlAllowed(params.url, params.policy.authAllowHosts)) return firstAttempt;
|
|
775
|
+
if (!require_resolve_allowlist.isUrlAllowed(params.url, params.policy.authAllowHosts)) return firstAttempt;
|
|
747
776
|
await firstAttempt.body?.cancel();
|
|
748
777
|
const scopes = scopeCandidatesForUrl(params.url);
|
|
749
778
|
const fetchFn = params.fetchFn ?? fetch;
|
|
750
779
|
for (const scope of scopes) try {
|
|
751
|
-
const token = await
|
|
780
|
+
const token = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
781
|
+
deadline: params.deadline,
|
|
782
|
+
label: "MS Teams attachment token",
|
|
783
|
+
work: () => tokenProvider.getAccessToken(scope)
|
|
784
|
+
});
|
|
752
785
|
const authHeaders = new Headers(params.requestInit?.headers);
|
|
753
786
|
authHeaders.set("Authorization", `Bearer ${token}`);
|
|
754
|
-
const authAttempt = await safeFetchWithPolicy({
|
|
787
|
+
const authAttempt = await require_resolve_allowlist.safeFetchWithPolicy({
|
|
755
788
|
url: params.url,
|
|
756
789
|
policy: params.policy,
|
|
757
790
|
fetchFn,
|
|
@@ -760,7 +793,8 @@ async function fetchWithAuthFallback(params) {
|
|
|
760
793
|
...params.requestInit,
|
|
761
794
|
headers: authHeaders
|
|
762
795
|
},
|
|
763
|
-
resolveFn: params.resolveFn
|
|
796
|
+
resolveFn: params.resolveFn,
|
|
797
|
+
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
764
798
|
});
|
|
765
799
|
if (authAttempt.ok) return authAttempt;
|
|
766
800
|
if (isRedirectStatus(authAttempt.status)) return authAttempt;
|
|
@@ -779,20 +813,20 @@ async function fetchWithAuthFallback(params) {
|
|
|
779
813
|
async function downloadMSTeamsAttachments(params) {
|
|
780
814
|
const list = Array.isArray(params.attachments) ? params.attachments : [];
|
|
781
815
|
if (list.length === 0) return [];
|
|
782
|
-
const policy = resolveAttachmentFetchPolicy({
|
|
816
|
+
const policy = require_resolve_allowlist.resolveAttachmentFetchPolicy({
|
|
783
817
|
allowHosts: params.allowHosts,
|
|
784
818
|
authAllowHosts: params.authAllowHosts
|
|
785
819
|
});
|
|
786
820
|
const allowHosts = policy.allowHosts;
|
|
787
|
-
const ssrfPolicy = resolveMediaSsrfPolicy(allowHosts);
|
|
788
|
-
const candidates = list.filter(isDownloadableAttachment).map(resolveDownloadCandidate).filter(Boolean);
|
|
789
|
-
const inlineCandidates = extractInlineImageCandidates(list, {
|
|
821
|
+
const ssrfPolicy = require_resolve_allowlist.resolveMediaSsrfPolicy(allowHosts);
|
|
822
|
+
const candidates = list.filter(require_resolve_allowlist.isDownloadableAttachment).map(resolveDownloadCandidate).filter(Boolean);
|
|
823
|
+
const inlineCandidates = require_resolve_allowlist.extractInlineImageCandidates(list, {
|
|
790
824
|
maxInlineBytes: params.maxBytes,
|
|
791
825
|
maxInlineTotalBytes: params.maxBytes
|
|
792
826
|
});
|
|
793
827
|
const seenUrls = /* @__PURE__ */ new Set();
|
|
794
828
|
for (const inline of inlineCandidates) if (inline.kind === "url") {
|
|
795
|
-
if (!isUrlAllowed(inline.url, allowHosts)) continue;
|
|
829
|
+
if (!require_resolve_allowlist.isUrlAllowed(inline.url, allowHosts)) continue;
|
|
796
830
|
if (seenUrls.has(inline.url)) continue;
|
|
797
831
|
seenUrls.add(inline.url);
|
|
798
832
|
candidates.push({
|
|
@@ -810,18 +844,18 @@ async function downloadMSTeamsAttachments(params) {
|
|
|
810
844
|
try {
|
|
811
845
|
const contentType = await resolveInlineDataImageMime(inline);
|
|
812
846
|
if (!contentType) continue;
|
|
813
|
-
const saved = await getMSTeamsRuntime().channel.media.saveMediaBuffer(inline.data, contentType, "inbound", params.maxBytes);
|
|
847
|
+
const saved = await require_runtime.getMSTeamsRuntime().channel.media.saveMediaBuffer(inline.data, contentType, "inbound", params.maxBytes);
|
|
814
848
|
out.push({
|
|
815
849
|
path: saved.path,
|
|
816
850
|
contentType: saved.contentType,
|
|
817
|
-
placeholder: inferPlaceholder({ contentType: saved.contentType ?? contentType })
|
|
851
|
+
placeholder: require_resolve_allowlist.inferPlaceholder({ contentType: saved.contentType ?? contentType })
|
|
818
852
|
});
|
|
819
853
|
} catch (err) {
|
|
820
854
|
params.logger?.warn?.("msteams inline attachment decode failed", { error: err instanceof Error ? err.message : String(err) });
|
|
821
855
|
}
|
|
822
856
|
}
|
|
823
857
|
for (const candidate of candidates) {
|
|
824
|
-
if (!isUrlAllowed(candidate.url, allowHosts)) continue;
|
|
858
|
+
if (!require_resolve_allowlist.isUrlAllowed(candidate.url, allowHosts)) continue;
|
|
825
859
|
try {
|
|
826
860
|
const media = await downloadAndStoreMSTeamsRemoteMedia({
|
|
827
861
|
url: candidate.url,
|
|
@@ -833,13 +867,14 @@ async function downloadMSTeamsAttachments(params) {
|
|
|
833
867
|
ssrfPolicy,
|
|
834
868
|
useDirectFetch: true,
|
|
835
869
|
fetchImpl: (input, init) => fetchWithAuthFallback({
|
|
836
|
-
url: resolveRequestUrl(input),
|
|
870
|
+
url: require_resolve_allowlist.resolveRequestUrl(input),
|
|
837
871
|
tokenProvider: params.tokenProvider,
|
|
838
872
|
fetchFn: params.fetchFn,
|
|
839
873
|
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
840
874
|
requestInit: init,
|
|
841
875
|
resolveFn: params.resolveFn,
|
|
842
|
-
policy
|
|
876
|
+
policy,
|
|
877
|
+
deadline: params.deadline
|
|
843
878
|
})
|
|
844
879
|
});
|
|
845
880
|
out.push(media);
|
|
@@ -859,44 +894,31 @@ function safeHostForLog(url) {
|
|
|
859
894
|
}
|
|
860
895
|
//#endregion
|
|
861
896
|
//#region extensions/msteams/src/attachments/graph.ts
|
|
862
|
-
function
|
|
863
|
-
const conversationType = normalizeLowercaseStringOrEmpty(params.conversationType ?? "");
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
const trimmed = normalizeOptionalString(value) ?? "";
|
|
867
|
-
if (trimmed) messageIdCandidates.add(trimmed);
|
|
868
|
-
};
|
|
869
|
-
pushCandidate(params.messageId);
|
|
870
|
-
pushCandidate(params.conversationMessageId);
|
|
871
|
-
pushCandidate(readNestedString(params.channelData, ["messageId"]));
|
|
872
|
-
pushCandidate(readNestedString(params.channelData, ["teamsMessageId"]));
|
|
873
|
-
const replyToId = normalizeOptionalString(params.replyToId) ?? "";
|
|
897
|
+
function buildMSTeamsGraphMessageUrl(params) {
|
|
898
|
+
const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeLowercaseStringOrEmpty)(params.conversationType ?? "");
|
|
899
|
+
const messageId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(params.messageId);
|
|
900
|
+
if (!messageId) return;
|
|
874
901
|
if (conversationType === "channel") {
|
|
875
|
-
const
|
|
876
|
-
const channelId =
|
|
877
|
-
if (!
|
|
878
|
-
const
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
urls.push(`${GRAPH_ROOT}/teams/${encodeURIComponent(teamId)}/channels/${encodeURIComponent(channelId)}/messages/${encodeURIComponent(replyToId)}/replies/${encodeURIComponent(candidate)}`);
|
|
882
|
-
}
|
|
883
|
-
if (messageIdCandidates.size === 0 && replyToId) messageIdCandidates.add(replyToId);
|
|
884
|
-
for (const candidate of messageIdCandidates) urls.push(`${GRAPH_ROOT}/teams/${encodeURIComponent(teamId)}/channels/${encodeURIComponent(channelId)}/messages/${encodeURIComponent(candidate)}`);
|
|
885
|
-
return uniqueStrings(urls);
|
|
902
|
+
const teamAadGroupId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(params.teamAadGroupId);
|
|
903
|
+
const channelId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(params.channelId);
|
|
904
|
+
if (!teamAadGroupId || !channelId) return;
|
|
905
|
+
const messageRoot = `${require_resolve_allowlist.GRAPH_ROOT}/teams/${encodeURIComponent(teamAadGroupId)}/channels/${encodeURIComponent(channelId)}/messages`;
|
|
906
|
+
const threadRootMessageId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(params.threadRootMessageId);
|
|
907
|
+
return threadRootMessageId && threadRootMessageId !== messageId ? `${messageRoot}/${encodeURIComponent(threadRootMessageId)}/replies/${encodeURIComponent(messageId)}` : `${messageRoot}/${encodeURIComponent(messageId)}`;
|
|
886
908
|
}
|
|
887
|
-
const chatId =
|
|
888
|
-
if (!chatId) return
|
|
889
|
-
|
|
890
|
-
return uniqueStrings(Array.from(messageIdCandidates).map((candidate) => `${GRAPH_ROOT}/chats/${encodeURIComponent(chatId)}/messages/${encodeURIComponent(candidate)}`));
|
|
909
|
+
const chatId = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(params.conversationId);
|
|
910
|
+
if (!chatId) return;
|
|
911
|
+
return `${require_resolve_allowlist.GRAPH_ROOT}/chats/${encodeURIComponent(chatId)}/messages/${encodeURIComponent(messageId)}`;
|
|
891
912
|
}
|
|
892
913
|
async function fetchGraphCollection(params) {
|
|
893
914
|
const fetchFn = params.fetchFn ?? fetch;
|
|
894
|
-
const { response, release } = await fetchWithSsrFGuard({
|
|
915
|
+
const { response, release } = await (0, openclaw_plugin_sdk_ssrf_runtime.fetchWithSsrFGuard)({
|
|
895
916
|
url: params.url,
|
|
896
917
|
fetchImpl: fetchFn,
|
|
897
|
-
init: { headers: ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
|
|
918
|
+
init: { headers: require_resolve_allowlist.ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
|
|
898
919
|
policy: params.ssrfPolicy,
|
|
899
|
-
auditContext: "msteams.graph.collection"
|
|
920
|
+
auditContext: "msteams.graph.collection",
|
|
921
|
+
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
900
922
|
});
|
|
901
923
|
try {
|
|
902
924
|
const status = response.status;
|
|
@@ -907,7 +929,7 @@ async function fetchGraphCollection(params) {
|
|
|
907
929
|
try {
|
|
908
930
|
return {
|
|
909
931
|
status,
|
|
910
|
-
items: await readProviderJsonArrayFieldResponse(response, "MS Teams Graph collection", "value")
|
|
932
|
+
items: await (0, openclaw_plugin_sdk_provider_http.readProviderJsonArrayFieldResponse)(response, "MS Teams Graph collection", "value")
|
|
911
933
|
};
|
|
912
934
|
} catch {
|
|
913
935
|
return {
|
|
@@ -925,7 +947,7 @@ function normalizeGraphAttachment(att) {
|
|
|
925
947
|
content = JSON.parse(content);
|
|
926
948
|
} catch {}
|
|
927
949
|
return {
|
|
928
|
-
contentType: normalizeContentType(att.contentType) ?? void 0,
|
|
950
|
+
contentType: require_resolve_allowlist.normalizeContentType(att.contentType) ?? void 0,
|
|
929
951
|
contentUrl: att.contentUrl ?? void 0,
|
|
930
952
|
name: att.name ?? void 0,
|
|
931
953
|
thumbnailUrl: att.thumbnailUrl ?? void 0,
|
|
@@ -937,12 +959,22 @@ function normalizeGraphAttachment(att) {
|
|
|
937
959
|
* Renamed from downloadGraphHostedImages to support all file types.
|
|
938
960
|
*/
|
|
939
961
|
async function downloadGraphHostedContent(params) {
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
962
|
+
let hosted;
|
|
963
|
+
try {
|
|
964
|
+
hosted = await fetchGraphCollection({
|
|
965
|
+
url: `${params.messageUrl}/hostedContents`,
|
|
966
|
+
accessToken: params.accessToken,
|
|
967
|
+
fetchFn: params.fetchFn,
|
|
968
|
+
ssrfPolicy: params.ssrfPolicy,
|
|
969
|
+
deadline: params.deadline
|
|
970
|
+
});
|
|
971
|
+
} catch (err) {
|
|
972
|
+
params.logger?.warn?.("msteams graph hostedContents fetch failed", { error: err instanceof Error ? err.message : String(err) });
|
|
973
|
+
return {
|
|
974
|
+
media: [],
|
|
975
|
+
count: 0
|
|
976
|
+
};
|
|
977
|
+
}
|
|
946
978
|
if (hosted.items.length === 0) return {
|
|
947
979
|
media: [],
|
|
948
980
|
status: hosted.status,
|
|
@@ -953,16 +985,17 @@ async function downloadGraphHostedContent(params) {
|
|
|
953
985
|
if (!item.id) continue;
|
|
954
986
|
try {
|
|
955
987
|
const valueUrl = `${params.messageUrl}/hostedContents/${encodeURIComponent(item.id)}/$value`;
|
|
956
|
-
const { response: valRes, release } = await fetchWithSsrFGuard({
|
|
988
|
+
const { response: valRes, release } = await (0, openclaw_plugin_sdk_ssrf_runtime.fetchWithSsrFGuard)({
|
|
957
989
|
url: valueUrl,
|
|
958
990
|
fetchImpl: params.fetchFn ?? fetch,
|
|
959
|
-
init: { headers: ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
|
|
991
|
+
init: { headers: require_resolve_allowlist.ensureUserAgentHeader({ Authorization: `Bearer ${params.accessToken}` }) },
|
|
960
992
|
policy: params.ssrfPolicy,
|
|
961
|
-
auditContext: "msteams.graph.hostedContent.value"
|
|
993
|
+
auditContext: "msteams.graph.hostedContent.value",
|
|
994
|
+
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
962
995
|
});
|
|
963
996
|
try {
|
|
964
997
|
if (!valRes.ok) continue;
|
|
965
|
-
const saved = await getMSTeamsRuntime().channel.media.saveResponseMedia(valRes, {
|
|
998
|
+
const saved = await require_runtime.getMSTeamsRuntime().channel.media.saveResponseMedia(valRes, {
|
|
966
999
|
sourceUrl: valueUrl,
|
|
967
1000
|
maxBytes: params.maxBytes,
|
|
968
1001
|
fallbackContentType: item.contentType ?? void 0,
|
|
@@ -971,7 +1004,7 @@ async function downloadGraphHostedContent(params) {
|
|
|
971
1004
|
out.push({
|
|
972
1005
|
path: saved.path,
|
|
973
1006
|
contentType: saved.contentType,
|
|
974
|
-
placeholder: inferPlaceholder({ contentType: saved.contentType })
|
|
1007
|
+
placeholder: require_resolve_allowlist.inferPlaceholder({ contentType: saved.contentType })
|
|
975
1008
|
});
|
|
976
1009
|
} finally {
|
|
977
1010
|
await release();
|
|
@@ -989,18 +1022,22 @@ async function downloadGraphHostedContent(params) {
|
|
|
989
1022
|
}
|
|
990
1023
|
async function downloadMSTeamsGraphMedia(params) {
|
|
991
1024
|
if (!params.messageUrl || !params.tokenProvider) return { media: [] };
|
|
992
|
-
const
|
|
1025
|
+
const tokenProvider = params.tokenProvider;
|
|
1026
|
+
const policy = require_resolve_allowlist.resolveAttachmentFetchPolicy({
|
|
993
1027
|
allowHosts: params.allowHosts,
|
|
994
1028
|
authAllowHosts: params.authAllowHosts
|
|
995
1029
|
});
|
|
996
|
-
const ssrfPolicy = resolveMediaSsrfPolicy(policy.allowHosts);
|
|
1030
|
+
const ssrfPolicy = require_resolve_allowlist.resolveMediaSsrfPolicy(policy.allowHosts);
|
|
997
1031
|
const messageUrl = params.messageUrl;
|
|
998
|
-
const debugLog = params.log ?? params.logger ?? void 0;
|
|
999
1032
|
let accessToken;
|
|
1000
1033
|
try {
|
|
1001
|
-
accessToken = await
|
|
1034
|
+
accessToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
1035
|
+
deadline: params.deadline,
|
|
1036
|
+
label: "MS Teams Graph media token",
|
|
1037
|
+
work: () => tokenProvider.getAccessToken("https://graph.microsoft.com")
|
|
1038
|
+
});
|
|
1002
1039
|
} catch (err) {
|
|
1003
|
-
|
|
1040
|
+
params.logger?.debug?.("graph media token acquisition failed", {
|
|
1004
1041
|
messageUrl,
|
|
1005
1042
|
error: err instanceof Error ? err.message : String(err)
|
|
1006
1043
|
});
|
|
@@ -1015,23 +1052,25 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1015
1052
|
const sharePointMedia = [];
|
|
1016
1053
|
const downloadedReferenceUrls = /* @__PURE__ */ new Set();
|
|
1017
1054
|
let messageAttachments = [];
|
|
1055
|
+
let referenceAttachments = [];
|
|
1018
1056
|
let messageStatus;
|
|
1019
1057
|
try {
|
|
1020
|
-
const { response: msgRes, release } = await fetchWithSsrFGuard({
|
|
1058
|
+
const { response: msgRes, release } = await (0, openclaw_plugin_sdk_ssrf_runtime.fetchWithSsrFGuard)({
|
|
1021
1059
|
url: messageUrl,
|
|
1022
1060
|
fetchImpl: fetchFn,
|
|
1023
|
-
init: { headers: ensureUserAgentHeader({ Authorization: `Bearer ${accessToken}` }) },
|
|
1061
|
+
init: { headers: require_resolve_allowlist.ensureUserAgentHeader({ Authorization: `Bearer ${accessToken}` }) },
|
|
1024
1062
|
policy: ssrfPolicy,
|
|
1025
|
-
auditContext: "msteams.graph.message"
|
|
1063
|
+
auditContext: "msteams.graph.message",
|
|
1064
|
+
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
1026
1065
|
});
|
|
1027
1066
|
try {
|
|
1028
1067
|
messageStatus = msgRes.status;
|
|
1029
1068
|
if (msgRes.ok) {
|
|
1030
1069
|
let msgData;
|
|
1031
1070
|
try {
|
|
1032
|
-
msgData = await readProviderJsonResponse(msgRes, "MS Teams Graph message");
|
|
1071
|
+
msgData = await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(msgRes, "MS Teams Graph message");
|
|
1033
1072
|
} catch (err) {
|
|
1034
|
-
|
|
1073
|
+
params.logger?.debug?.("graph media message parse failed", {
|
|
1035
1074
|
messageUrl,
|
|
1036
1075
|
error: err instanceof Error ? err.message : String(err)
|
|
1037
1076
|
});
|
|
@@ -1042,60 +1081,8 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1042
1081
|
msgData = {};
|
|
1043
1082
|
}
|
|
1044
1083
|
messageAttachments = Array.isArray(msgData.attachments) ? msgData.attachments : [];
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
const name = att.name ?? "file";
|
|
1048
|
-
const shareUrl = att.contentUrl ?? "";
|
|
1049
|
-
if (!shareUrl) continue;
|
|
1050
|
-
try {
|
|
1051
|
-
const sharesUrl = `${GRAPH_ROOT}/shares/${encodeGraphShareId(shareUrl)}/driveItem/content`;
|
|
1052
|
-
if (!isUrlAllowed(sharesUrl, policy.allowHosts)) {
|
|
1053
|
-
debugLog?.debug?.("graph media sharepoint url not in allowHosts", {
|
|
1054
|
-
messageUrl,
|
|
1055
|
-
sharesUrl
|
|
1056
|
-
});
|
|
1057
|
-
continue;
|
|
1058
|
-
}
|
|
1059
|
-
const media = await downloadAndStoreMSTeamsRemoteMedia({
|
|
1060
|
-
url: sharesUrl,
|
|
1061
|
-
filePathHint: name,
|
|
1062
|
-
maxBytes: params.maxBytes,
|
|
1063
|
-
contentTypeHint: "application/octet-stream",
|
|
1064
|
-
preserveFilenames: params.preserveFilenames,
|
|
1065
|
-
ssrfPolicy,
|
|
1066
|
-
useDirectFetch: true,
|
|
1067
|
-
fetchImpl: async (input, init) => {
|
|
1068
|
-
const requestUrl = resolveRequestUrl(input);
|
|
1069
|
-
const headers = ensureUserAgentHeader(init?.headers);
|
|
1070
|
-
applyAuthorizationHeaderForUrl({
|
|
1071
|
-
headers,
|
|
1072
|
-
url: requestUrl,
|
|
1073
|
-
authAllowHosts: policy.authAllowHosts,
|
|
1074
|
-
bearerToken: accessToken
|
|
1075
|
-
});
|
|
1076
|
-
return await safeFetchWithPolicy({
|
|
1077
|
-
url: requestUrl,
|
|
1078
|
-
policy,
|
|
1079
|
-
fetchFn,
|
|
1080
|
-
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
1081
|
-
requestInit: {
|
|
1082
|
-
...init,
|
|
1083
|
-
headers
|
|
1084
|
-
},
|
|
1085
|
-
resolveFn: params.resolveFn
|
|
1086
|
-
});
|
|
1087
|
-
}
|
|
1088
|
-
});
|
|
1089
|
-
sharePointMedia.push(media);
|
|
1090
|
-
downloadedReferenceUrls.add(shareUrl);
|
|
1091
|
-
} catch (err) {
|
|
1092
|
-
params.logger?.warn?.("msteams SharePoint reference download failed", {
|
|
1093
|
-
error: err instanceof Error ? err.message : String(err),
|
|
1094
|
-
name
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
} else debugLog?.debug?.("graph media message fetch not ok", {
|
|
1084
|
+
referenceAttachments = messageAttachments.filter((a) => a.contentType === "reference" && a.contentUrl && a.name);
|
|
1085
|
+
} else params.logger?.debug?.("graph media message fetch not ok", {
|
|
1099
1086
|
messageUrl,
|
|
1100
1087
|
status: messageStatus
|
|
1101
1088
|
});
|
|
@@ -1103,12 +1090,65 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1103
1090
|
await release();
|
|
1104
1091
|
}
|
|
1105
1092
|
} catch (err) {
|
|
1106
|
-
|
|
1093
|
+
params.logger?.debug?.("graph media message fetch failed", {
|
|
1107
1094
|
messageUrl,
|
|
1108
1095
|
error: err instanceof Error ? err.message : String(err)
|
|
1109
1096
|
});
|
|
1110
1097
|
params.logger?.warn?.("msteams graph message fetch failed", { error: err instanceof Error ? err.message : String(err) });
|
|
1111
1098
|
}
|
|
1099
|
+
for (const att of referenceAttachments) {
|
|
1100
|
+
const name = att.name ?? "file";
|
|
1101
|
+
const shareUrl = att.contentUrl ?? "";
|
|
1102
|
+
if (!shareUrl) continue;
|
|
1103
|
+
try {
|
|
1104
|
+
const sharesUrl = `${require_resolve_allowlist.GRAPH_ROOT}/shares/${require_resolve_allowlist.encodeGraphShareId(shareUrl)}/driveItem/content`;
|
|
1105
|
+
if (!require_resolve_allowlist.isUrlAllowed(sharesUrl, policy.allowHosts)) {
|
|
1106
|
+
params.logger?.debug?.("graph media sharepoint url not in allowHosts", {
|
|
1107
|
+
messageUrl,
|
|
1108
|
+
sharesUrl
|
|
1109
|
+
});
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
1112
|
+
const media = await downloadAndStoreMSTeamsRemoteMedia({
|
|
1113
|
+
url: sharesUrl,
|
|
1114
|
+
filePathHint: name,
|
|
1115
|
+
maxBytes: params.maxBytes,
|
|
1116
|
+
contentTypeHint: "application/octet-stream",
|
|
1117
|
+
preserveFilenames: params.preserveFilenames,
|
|
1118
|
+
ssrfPolicy,
|
|
1119
|
+
useDirectFetch: true,
|
|
1120
|
+
fetchImpl: async (input, init) => {
|
|
1121
|
+
const requestUrl = require_resolve_allowlist.resolveRequestUrl(input);
|
|
1122
|
+
const headers = require_resolve_allowlist.ensureUserAgentHeader(init?.headers);
|
|
1123
|
+
require_resolve_allowlist.applyAuthorizationHeaderForUrl({
|
|
1124
|
+
headers,
|
|
1125
|
+
url: requestUrl,
|
|
1126
|
+
authAllowHosts: policy.authAllowHosts,
|
|
1127
|
+
bearerToken: accessToken
|
|
1128
|
+
});
|
|
1129
|
+
return await require_resolve_allowlist.safeFetchWithPolicy({
|
|
1130
|
+
url: requestUrl,
|
|
1131
|
+
policy,
|
|
1132
|
+
fetchFn,
|
|
1133
|
+
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
1134
|
+
requestInit: {
|
|
1135
|
+
...init,
|
|
1136
|
+
headers
|
|
1137
|
+
},
|
|
1138
|
+
resolveFn: params.resolveFn,
|
|
1139
|
+
timeoutMs: require_resolve_allowlist.resolveMSTeamsRequestTimeoutMs(params.deadline)
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
sharePointMedia.push(media);
|
|
1144
|
+
downloadedReferenceUrls.add(shareUrl);
|
|
1145
|
+
} catch (err) {
|
|
1146
|
+
params.logger?.warn?.("msteams SharePoint reference download failed", {
|
|
1147
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1148
|
+
name
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1112
1152
|
const hosted = await downloadGraphHostedContent({
|
|
1113
1153
|
accessToken,
|
|
1114
1154
|
messageUrl,
|
|
@@ -1116,11 +1156,12 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1116
1156
|
fetchFn: params.fetchFn,
|
|
1117
1157
|
preserveFilenames: params.preserveFilenames,
|
|
1118
1158
|
ssrfPolicy,
|
|
1119
|
-
logger: params.logger
|
|
1159
|
+
logger: params.logger,
|
|
1160
|
+
deadline: params.deadline
|
|
1120
1161
|
});
|
|
1121
1162
|
const normalizedAttachments = messageAttachments.map(normalizeGraphAttachment);
|
|
1122
1163
|
const filteredAttachments = sharePointMedia.length > 0 ? normalizedAttachments.filter((att) => {
|
|
1123
|
-
if (normalizeOptionalLowercaseString(att.contentType) !== "reference") return true;
|
|
1164
|
+
if ((0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(att.contentType) !== "reference") return true;
|
|
1124
1165
|
const url = typeof att.contentUrl === "string" ? att.contentUrl : "";
|
|
1125
1166
|
if (!url) return true;
|
|
1126
1167
|
return !downloadedReferenceUrls.has(url);
|
|
@@ -1136,6 +1177,7 @@ async function downloadMSTeamsGraphMedia(params) {
|
|
|
1136
1177
|
fetchFn: params.fetchFn,
|
|
1137
1178
|
fetchFnSupportsDispatcher: params.fetchFnSupportsDispatcher,
|
|
1138
1179
|
resolveFn: params.resolveFn,
|
|
1180
|
+
deadline: params.deadline,
|
|
1139
1181
|
preserveFilenames: params.preserveFilenames,
|
|
1140
1182
|
logger: params.logger
|
|
1141
1183
|
});
|
|
@@ -1170,14 +1212,14 @@ function extractMSTeamsHtmlAttachmentIds(attachments) {
|
|
|
1170
1212
|
if (list.length === 0) return [];
|
|
1171
1213
|
const ids = /* @__PURE__ */ new Set();
|
|
1172
1214
|
for (const att of list) {
|
|
1173
|
-
const html = extractHtmlFromAttachment(att);
|
|
1215
|
+
const html = require_resolve_allowlist.extractHtmlFromAttachment(att);
|
|
1174
1216
|
if (!html) continue;
|
|
1175
|
-
ATTACHMENT_TAG_RE.lastIndex = 0;
|
|
1176
|
-
let match = ATTACHMENT_TAG_RE.exec(html);
|
|
1217
|
+
require_resolve_allowlist.ATTACHMENT_TAG_RE.lastIndex = 0;
|
|
1218
|
+
let match = require_resolve_allowlist.ATTACHMENT_TAG_RE.exec(html);
|
|
1177
1219
|
while (match) {
|
|
1178
1220
|
const id = match[1]?.trim();
|
|
1179
1221
|
if (id) ids.add(id);
|
|
1180
|
-
match = ATTACHMENT_TAG_RE.exec(html);
|
|
1222
|
+
match = require_resolve_allowlist.ATTACHMENT_TAG_RE.exec(html);
|
|
1181
1223
|
}
|
|
1182
1224
|
}
|
|
1183
1225
|
return Array.from(ids);
|
|
@@ -1193,26 +1235,26 @@ function summarizeMSTeamsHtmlAttachments(attachments) {
|
|
|
1193
1235
|
let attachmentTags = 0;
|
|
1194
1236
|
const attachmentIds = /* @__PURE__ */ new Set();
|
|
1195
1237
|
for (const att of list) {
|
|
1196
|
-
const html = extractHtmlFromAttachment(att);
|
|
1238
|
+
const html = require_resolve_allowlist.extractHtmlFromAttachment(att);
|
|
1197
1239
|
if (!html) continue;
|
|
1198
1240
|
htmlAttachments += 1;
|
|
1199
|
-
IMG_SRC_RE.lastIndex = 0;
|
|
1200
|
-
let match = IMG_SRC_RE.exec(html);
|
|
1241
|
+
require_resolve_allowlist.IMG_SRC_RE.lastIndex = 0;
|
|
1242
|
+
let match = require_resolve_allowlist.IMG_SRC_RE.exec(html);
|
|
1201
1243
|
while (match) {
|
|
1202
1244
|
imgTags += 1;
|
|
1203
1245
|
const src = match[1]?.trim();
|
|
1204
1246
|
if (src) if (src.startsWith("data:")) dataImages += 1;
|
|
1205
1247
|
else if (src.startsWith("cid:")) cidImages += 1;
|
|
1206
|
-
else srcHosts.add(safeHostForUrl(src));
|
|
1207
|
-
match = IMG_SRC_RE.exec(html);
|
|
1248
|
+
else srcHosts.add(require_resolve_allowlist.safeHostForUrl(src));
|
|
1249
|
+
match = require_resolve_allowlist.IMG_SRC_RE.exec(html);
|
|
1208
1250
|
}
|
|
1209
|
-
ATTACHMENT_TAG_RE.lastIndex = 0;
|
|
1210
|
-
let attachmentMatch = ATTACHMENT_TAG_RE.exec(html);
|
|
1251
|
+
require_resolve_allowlist.ATTACHMENT_TAG_RE.lastIndex = 0;
|
|
1252
|
+
let attachmentMatch = require_resolve_allowlist.ATTACHMENT_TAG_RE.exec(html);
|
|
1211
1253
|
while (attachmentMatch) {
|
|
1212
1254
|
attachmentTags += 1;
|
|
1213
1255
|
const id = attachmentMatch[1]?.trim();
|
|
1214
1256
|
if (id) attachmentIds.add(id);
|
|
1215
|
-
attachmentMatch = ATTACHMENT_TAG_RE.exec(html);
|
|
1257
|
+
attachmentMatch = require_resolve_allowlist.ATTACHMENT_TAG_RE.exec(html);
|
|
1216
1258
|
}
|
|
1217
1259
|
}
|
|
1218
1260
|
if (htmlAttachments === 0) return;
|
|
@@ -1227,18 +1269,18 @@ function summarizeMSTeamsHtmlAttachments(attachments) {
|
|
|
1227
1269
|
};
|
|
1228
1270
|
}
|
|
1229
1271
|
function isAdvertisedFileAttachment(attachment) {
|
|
1230
|
-
const contentType = normalizeContentType(attachment.contentType) ?? "";
|
|
1272
|
+
const contentType = require_resolve_allowlist.normalizeContentType(attachment.contentType) ?? "";
|
|
1231
1273
|
if (contentType.startsWith("text/html") || contentType.startsWith("application/vnd.microsoft.card.") || contentType.startsWith("application/vnd.microsoft.teams.card.")) return false;
|
|
1232
|
-
return Boolean(isDownloadableAttachment(attachment) || isLikelyImageAttachment(attachment) || attachment.name?.trim() || contentType);
|
|
1274
|
+
return Boolean(require_resolve_allowlist.isDownloadableAttachment(attachment) || require_resolve_allowlist.isLikelyImageAttachment(attachment) || attachment.name?.trim() || contentType);
|
|
1233
1275
|
}
|
|
1234
1276
|
function countDistinctInlineImages(attachments) {
|
|
1235
1277
|
const seenReferences = /* @__PURE__ */ new Set();
|
|
1236
1278
|
let count = 0;
|
|
1237
1279
|
for (const attachment of attachments) {
|
|
1238
|
-
const html = extractHtmlFromAttachment(attachment);
|
|
1280
|
+
const html = require_resolve_allowlist.extractHtmlFromAttachment(attachment);
|
|
1239
1281
|
if (!html) continue;
|
|
1240
|
-
IMG_SRC_RE.lastIndex = 0;
|
|
1241
|
-
let match = IMG_SRC_RE.exec(html);
|
|
1282
|
+
require_resolve_allowlist.IMG_SRC_RE.lastIndex = 0;
|
|
1283
|
+
let match = require_resolve_allowlist.IMG_SRC_RE.exec(html);
|
|
1242
1284
|
while (match) {
|
|
1243
1285
|
const src = match[1]?.trim();
|
|
1244
1286
|
if (src?.startsWith("data:")) count += 1;
|
|
@@ -1246,7 +1288,7 @@ function countDistinctInlineImages(attachments) {
|
|
|
1246
1288
|
seenReferences.add(src);
|
|
1247
1289
|
count += 1;
|
|
1248
1290
|
}
|
|
1249
|
-
match = IMG_SRC_RE.exec(html);
|
|
1291
|
+
match = require_resolve_allowlist.IMG_SRC_RE.exec(html);
|
|
1250
1292
|
}
|
|
1251
1293
|
}
|
|
1252
1294
|
return count;
|
|
@@ -1254,14 +1296,14 @@ function countDistinctInlineImages(attachments) {
|
|
|
1254
1296
|
function countDistinctInlineCandidates(attachments, limits) {
|
|
1255
1297
|
const seenUrls = /* @__PURE__ */ new Set();
|
|
1256
1298
|
let dataCount = 0;
|
|
1257
|
-
for (const candidate of extractInlineImageCandidates(attachments, limits)) if (candidate.kind === "data") dataCount += 1;
|
|
1299
|
+
for (const candidate of require_resolve_allowlist.extractInlineImageCandidates(attachments, limits)) if (candidate.kind === "data") dataCount += 1;
|
|
1258
1300
|
else seenUrls.add(candidate.url);
|
|
1259
1301
|
return dataCount + seenUrls.size;
|
|
1260
1302
|
}
|
|
1261
1303
|
function countUnrepresentedHtmlAttachmentIds(attachments) {
|
|
1262
1304
|
const representedIds = /* @__PURE__ */ new Set();
|
|
1263
1305
|
for (const attachment of attachments) {
|
|
1264
|
-
if ((normalizeContentType(attachment.contentType) ?? "").startsWith("text/html")) continue;
|
|
1306
|
+
if ((require_resolve_allowlist.normalizeContentType(attachment.contentType) ?? "").startsWith("text/html")) continue;
|
|
1265
1307
|
const id = attachment.id?.trim();
|
|
1266
1308
|
if (id) representedIds.add(id);
|
|
1267
1309
|
}
|
|
@@ -1282,7 +1324,7 @@ function resolveMSTeamsInboundAttachmentPresentation(attachments, limits) {
|
|
|
1282
1324
|
placeholder: "",
|
|
1283
1325
|
expectedMediaCount: 0
|
|
1284
1326
|
};
|
|
1285
|
-
const totalImages = fileAttachments.filter(isLikelyImageAttachment).length + inlinePlaceholderCount;
|
|
1327
|
+
const totalImages = fileAttachments.filter(require_resolve_allowlist.isLikelyImageAttachment).length + inlinePlaceholderCount;
|
|
1286
1328
|
if (totalImages > 0) return {
|
|
1287
1329
|
placeholder: `<media:image>${totalImages > 1 ? ` (${totalImages} images)` : ""}`,
|
|
1288
1330
|
expectedMediaCount
|
|
@@ -1295,100 +1337,7 @@ function resolveMSTeamsInboundAttachmentPresentation(attachments, limits) {
|
|
|
1295
1337
|
//#endregion
|
|
1296
1338
|
//#region extensions/msteams/src/attachments/payload.ts
|
|
1297
1339
|
function buildMSTeamsMediaPayload(mediaList) {
|
|
1298
|
-
return buildMediaPayload(mediaList, { preserveMediaTypeCardinality: true });
|
|
1299
|
-
}
|
|
1300
|
-
//#endregion
|
|
1301
|
-
//#region extensions/msteams/src/graph-thread.ts
|
|
1302
|
-
const teamGroupIdCache = /* @__PURE__ */ new Map();
|
|
1303
|
-
const CACHE_TTL_MS = 600 * 1e3;
|
|
1304
|
-
function resolveTeamGroupIdCacheExpiresAt(nowRaw = Date.now()) {
|
|
1305
|
-
const now = asDateTimestampMs(nowRaw);
|
|
1306
|
-
return now === void 0 ? void 0 : resolveExpiresAtMsFromDurationMs(CACHE_TTL_MS, { nowMs: now });
|
|
1307
|
-
}
|
|
1308
|
-
/**
|
|
1309
|
-
* Strip HTML tags from Teams message content, preserving @mention display names.
|
|
1310
|
-
* Teams wraps mentions in <at>Name</at> tags.
|
|
1311
|
-
*/
|
|
1312
|
-
function stripHtmlFromTeamsMessage(html) {
|
|
1313
|
-
let text = html.replace(/<at[^>]*>(.*?)<\/at>/gi, "@$1");
|
|
1314
|
-
text = text.replace(/<[^>]*>/g, " ");
|
|
1315
|
-
text = text.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, "\"").replace(/'/g, "'").replace(/ /g, " ").replace(/&/g, "&");
|
|
1316
|
-
return text.replace(/\s+/g, " ").trim();
|
|
1317
|
-
}
|
|
1318
|
-
/**
|
|
1319
|
-
* Resolve the Azure AD group GUID for a Teams conversation team ID.
|
|
1320
|
-
* Results are cached with a TTL to avoid repeated Graph API calls.
|
|
1321
|
-
*/
|
|
1322
|
-
async function resolveTeamGroupId(token, conversationTeamId) {
|
|
1323
|
-
const cached = teamGroupIdCache.get(conversationTeamId);
|
|
1324
|
-
if (cached) {
|
|
1325
|
-
const now = asDateTimestampMs(Date.now());
|
|
1326
|
-
const expiresAt = asDateTimestampMs(cached.expiresAt);
|
|
1327
|
-
if (now !== void 0 && expiresAt !== void 0 && expiresAt > now) return cached.groupId;
|
|
1328
|
-
teamGroupIdCache.delete(conversationTeamId);
|
|
1329
|
-
}
|
|
1330
|
-
try {
|
|
1331
|
-
const groupId = (await fetchGraphJson({
|
|
1332
|
-
token,
|
|
1333
|
-
path: `/teams/${encodeURIComponent(conversationTeamId)}?$select=id`
|
|
1334
|
-
})).id ?? conversationTeamId;
|
|
1335
|
-
const expiresAt = resolveTeamGroupIdCacheExpiresAt();
|
|
1336
|
-
if (expiresAt !== void 0) teamGroupIdCache.set(conversationTeamId, {
|
|
1337
|
-
groupId,
|
|
1338
|
-
expiresAt
|
|
1339
|
-
});
|
|
1340
|
-
return groupId;
|
|
1341
|
-
} catch {
|
|
1342
|
-
return conversationTeamId;
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
/**
|
|
1346
|
-
* Fetch a single channel message (the parent/root of a thread).
|
|
1347
|
-
* Returns undefined on error so callers can degrade gracefully.
|
|
1348
|
-
*/
|
|
1349
|
-
async function fetchChannelMessage(token, groupId, channelId, messageId) {
|
|
1350
|
-
const path = `/teams/${encodeURIComponent(groupId)}/channels/${encodeURIComponent(channelId)}/messages/${encodeURIComponent(messageId)}?$select=id,from,body,createdDateTime`;
|
|
1351
|
-
try {
|
|
1352
|
-
return await fetchGraphJson({
|
|
1353
|
-
token,
|
|
1354
|
-
path
|
|
1355
|
-
});
|
|
1356
|
-
} catch {
|
|
1357
|
-
return;
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
/**
|
|
1361
|
-
* Fetch thread replies for a channel message, ordered chronologically.
|
|
1362
|
-
*
|
|
1363
|
-
* **Limitation:** The Graph API replies endpoint (`/messages/{id}/replies`) does not
|
|
1364
|
-
* support `$orderby`, so results are always returned in ascending (oldest-first) order.
|
|
1365
|
-
* Combined with the `$top` cap of 50, this means only the **oldest 50 replies** are
|
|
1366
|
-
* returned for long threads — newer replies are silently omitted. There is currently no
|
|
1367
|
-
* Graph API workaround for this; pagination via `@odata.nextLink` can retrieve more
|
|
1368
|
-
* replies but still in ascending order only.
|
|
1369
|
-
*/
|
|
1370
|
-
async function fetchThreadReplies(token, groupId, channelId, messageId, limit = 50) {
|
|
1371
|
-
return (await fetchGraphJson({
|
|
1372
|
-
token,
|
|
1373
|
-
path: `/teams/${encodeURIComponent(groupId)}/channels/${encodeURIComponent(channelId)}/messages/${encodeURIComponent(messageId)}/replies?$top=${Math.min(Math.max(limit, 1), 50)}&$select=id,from,body,createdDateTime`
|
|
1374
|
-
})).value ?? [];
|
|
1375
|
-
}
|
|
1376
|
-
/**
|
|
1377
|
-
* Format thread messages into a context string for the agent.
|
|
1378
|
-
* Skips the current message (by id) and blank messages.
|
|
1379
|
-
*/
|
|
1380
|
-
function formatThreadContext(messages, currentMessageId) {
|
|
1381
|
-
const lines = [];
|
|
1382
|
-
for (const msg of messages) {
|
|
1383
|
-
if (msg.id && msg.id === currentMessageId) continue;
|
|
1384
|
-
const sender = msg.from?.user?.displayName ?? msg.from?.application?.displayName ?? "unknown";
|
|
1385
|
-
const contentType = msg.body?.contentType ?? "text";
|
|
1386
|
-
const rawContent = msg.body?.content ?? "";
|
|
1387
|
-
const content = contentType === "html" ? stripHtmlFromTeamsMessage(rawContent) : rawContent.trim();
|
|
1388
|
-
if (!content) continue;
|
|
1389
|
-
lines.push(`${sender}: ${content}`);
|
|
1390
|
-
}
|
|
1391
|
-
return lines.join("\n");
|
|
1340
|
+
return (0, openclaw_plugin_sdk_reply_payload.buildMediaPayload)(mediaList, { preserveMediaTypeCardinality: true });
|
|
1392
1341
|
}
|
|
1393
1342
|
//#endregion
|
|
1394
1343
|
//#region extensions/msteams/src/thread-parent-context.ts
|
|
@@ -1409,8 +1358,8 @@ function buildParentCacheKey(groupId, channelId, parentId) {
|
|
|
1409
1358
|
return `${groupId}\u0000${channelId}\u0000${parentId}`;
|
|
1410
1359
|
}
|
|
1411
1360
|
function resolveParentCacheExpiresAt(nowRaw) {
|
|
1412
|
-
const nowMs = asDateTimestampMs(nowRaw);
|
|
1413
|
-
return nowMs === void 0 ? void 0 : resolveExpiresAtMsFromDurationMs(PARENT_CACHE_TTL_MS, { nowMs });
|
|
1361
|
+
const nowMs = (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(nowRaw);
|
|
1362
|
+
return nowMs === void 0 ? void 0 : (0, openclaw_plugin_sdk_number_runtime.resolveExpiresAtMsFromDurationMs)(PARENT_CACHE_TTL_MS, { nowMs });
|
|
1414
1363
|
}
|
|
1415
1364
|
/**
|
|
1416
1365
|
* Fetch a channel parent message with an LRU+TTL cache.
|
|
@@ -1418,11 +1367,11 @@ function resolveParentCacheExpiresAt(nowRaw) {
|
|
|
1418
1367
|
* Uses the injected `fetchParent` (defaults to `fetchChannelMessage`) so
|
|
1419
1368
|
* tests can swap in a stub without mocking the Graph transport.
|
|
1420
1369
|
*/
|
|
1421
|
-
async function fetchParentMessageCached(token, groupId, channelId, parentId, fetchParent = fetchChannelMessage) {
|
|
1370
|
+
async function fetchParentMessageCached(token, groupId, channelId, parentId, fetchParent = require_messenger.fetchChannelMessage) {
|
|
1422
1371
|
const key = buildParentCacheKey(groupId, channelId, parentId);
|
|
1423
|
-
const now = asDateTimestampMs(Date.now());
|
|
1372
|
+
const now = (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(Date.now());
|
|
1424
1373
|
const cached = parentCache.get(key);
|
|
1425
|
-
const cachedExpiresAt = cached ? asDateTimestampMs(cached.expiresAt) : void 0;
|
|
1374
|
+
const cachedExpiresAt = cached ? (0, openclaw_plugin_sdk_number_runtime.asDateTimestampMs)(cached.expiresAt) : void 0;
|
|
1426
1375
|
if (cached && now !== void 0 && cachedExpiresAt !== void 0 && cachedExpiresAt > now) {
|
|
1427
1376
|
parentCache.delete(key);
|
|
1428
1377
|
parentCache.set(key, cached);
|
|
@@ -1448,11 +1397,11 @@ function summarizeParentMessage(message) {
|
|
|
1448
1397
|
const sender = message.from?.user?.displayName ?? message.from?.application?.displayName ?? "unknown";
|
|
1449
1398
|
const contentType = message.body?.contentType ?? "text";
|
|
1450
1399
|
const raw = message.body?.content ?? "";
|
|
1451
|
-
const text = contentType === "html" ? stripHtmlFromTeamsMessage(raw) : raw.replace(/\s+/g, " ").trim();
|
|
1400
|
+
const text = contentType === "html" ? require_messenger.stripHtmlFromTeamsMessage(raw) : raw.replace(/\s+/g, " ").trim();
|
|
1452
1401
|
if (!text) return;
|
|
1453
1402
|
return {
|
|
1454
1403
|
sender,
|
|
1455
|
-
text: text.length > PARENT_TEXT_MAX_CHARS ? `${truncateUtf16Safe(text, PARENT_TEXT_MAX_CHARS - 1)}…` : text
|
|
1404
|
+
text: text.length > PARENT_TEXT_MAX_CHARS ? `${(0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(text, PARENT_TEXT_MAX_CHARS - 1)}…` : text
|
|
1456
1405
|
};
|
|
1457
1406
|
}
|
|
1458
1407
|
/**
|
|
@@ -1501,10 +1450,10 @@ function isStreamCancelledError(err) {
|
|
|
1501
1450
|
* block message. We bypass the controller in that case.
|
|
1502
1451
|
*/
|
|
1503
1452
|
function createTeamsReplyStreamController(params) {
|
|
1504
|
-
const isPersonal = normalizeOptionalLowercaseString(params.conversationType) === "personal";
|
|
1505
|
-
const streamMode = resolveChannelPreviewStreamMode(params.msteamsConfig, "partial");
|
|
1453
|
+
const isPersonal = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationType) === "personal";
|
|
1454
|
+
const streamMode = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelPreviewStreamMode)(params.msteamsConfig, "partial");
|
|
1506
1455
|
const shouldUseNativeStream = isPersonal && (streamMode === "partial" || streamMode === "progress");
|
|
1507
|
-
const shouldStreamPreviewToolProgress = streamMode === "progress" && resolveChannelStreamingPreviewToolProgress(params.msteamsConfig);
|
|
1456
|
+
const shouldStreamPreviewToolProgress = streamMode === "progress" && (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingPreviewToolProgress)(params.msteamsConfig);
|
|
1508
1457
|
const stream = shouldUseNativeStream ? params.context.stream : void 0;
|
|
1509
1458
|
let tokensEmitted = false;
|
|
1510
1459
|
let streamFinalizationPending = false;
|
|
@@ -1513,7 +1462,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
1513
1462
|
let lastInformativeText = "";
|
|
1514
1463
|
let progressLines = [];
|
|
1515
1464
|
let pendingFinalPayload;
|
|
1516
|
-
let
|
|
1465
|
+
let emittedText = "";
|
|
1517
1466
|
const wasCanceled = () => canceledLocally || Boolean(stream?.canceled);
|
|
1518
1467
|
const fallbackPayloadForSuppressedFinal = (payload) => {
|
|
1519
1468
|
return Boolean(payload.mediaUrl || payload.mediaUrls?.length) ? {
|
|
@@ -1528,8 +1477,8 @@ function createTeamsReplyStreamController(params) {
|
|
|
1528
1477
|
* default) and prepends collected tool-progress lines when configured.
|
|
1529
1478
|
*/
|
|
1530
1479
|
const renderInformativeUpdate = () => {
|
|
1531
|
-
if (!stream || wasCanceled()) return;
|
|
1532
|
-
const informativeText = formatChannelProgressDraftText({
|
|
1480
|
+
if (!stream || wasCanceled() || streamFinalizationPending) return;
|
|
1481
|
+
const informativeText = (0, openclaw_plugin_sdk_channel_outbound.formatChannelProgressDraftText)({
|
|
1533
1482
|
entry: params.msteamsConfig,
|
|
1534
1483
|
lines: shouldStreamPreviewToolProgress ? progressLines : [],
|
|
1535
1484
|
seed: params.progressSeed,
|
|
@@ -1547,17 +1496,26 @@ function createTeamsReplyStreamController(params) {
|
|
|
1547
1496
|
params.log?.debug?.(`stream informative update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1548
1497
|
}
|
|
1549
1498
|
};
|
|
1550
|
-
const progressDraftGate = createChannelProgressDraftGate({ onStart: renderInformativeUpdate });
|
|
1499
|
+
const progressDraftGate = (0, openclaw_plugin_sdk_channel_outbound.createChannelProgressDraftGate)({ onStart: renderInformativeUpdate });
|
|
1551
1500
|
return {
|
|
1552
1501
|
async onReplyStart() {},
|
|
1553
1502
|
onPartialReply(payload) {
|
|
1554
1503
|
if (!stream || !payload.text || wasCanceled() || streamMode !== "partial" || streamFinalizationPending) return;
|
|
1555
1504
|
const fullText = payload.text;
|
|
1556
|
-
|
|
1557
|
-
|
|
1505
|
+
let prefixLength = 0;
|
|
1506
|
+
while (prefixLength < emittedText.length && prefixLength < fullText.length && emittedText[prefixLength] === fullText[prefixLength]) prefixLength += 1;
|
|
1507
|
+
const previousRemainder = emittedText.slice(prefixLength);
|
|
1508
|
+
const delta = fullText.slice(prefixLength);
|
|
1509
|
+
if (!delta) return;
|
|
1510
|
+
if (previousRemainder.trim()) {
|
|
1511
|
+
stream.clearText();
|
|
1512
|
+
streamFailed = true;
|
|
1513
|
+
streamFinalizationPending = true;
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1558
1516
|
try {
|
|
1559
1517
|
stream.emit(delta);
|
|
1560
|
-
|
|
1518
|
+
emittedText = fullText;
|
|
1561
1519
|
tokensEmitted = true;
|
|
1562
1520
|
} catch (err) {
|
|
1563
1521
|
if (isStreamCancelledError(err)) {
|
|
@@ -1575,7 +1533,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
1575
1533
|
*/
|
|
1576
1534
|
async noteProgressWork(options) {
|
|
1577
1535
|
if (!stream || streamMode !== "progress") return;
|
|
1578
|
-
if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
|
|
1536
|
+
if (options?.toolName !== void 0 && !(0, openclaw_plugin_sdk_channel_outbound.isChannelProgressDraftWorkToolName)(options.toolName)) return;
|
|
1579
1537
|
const hadStarted = progressDraftGate.hasStarted;
|
|
1580
1538
|
const progressActive = await progressDraftGate.noteWork();
|
|
1581
1539
|
if ((hadStarted || progressActive) && progressDraftGate.hasStarted) renderInformativeUpdate();
|
|
@@ -1587,10 +1545,10 @@ function createTeamsReplyStreamController(params) {
|
|
|
1587
1545
|
*/
|
|
1588
1546
|
async pushProgressLine(line, options) {
|
|
1589
1547
|
if (!stream || streamMode !== "progress") return;
|
|
1590
|
-
if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
|
|
1548
|
+
if (options?.toolName !== void 0 && !(0, openclaw_plugin_sdk_channel_outbound.isChannelProgressDraftWorkToolName)(options.toolName)) return;
|
|
1591
1549
|
if (shouldStreamPreviewToolProgress) {
|
|
1592
|
-
const normalized = normalizeChannelProgressDraftLineIdentity(line);
|
|
1593
|
-
if (normalized) progressLines = mergeChannelProgressDraftLine(progressLines, typeof line === "object" && line !== void 0 ? line : normalized, { maxLines: resolveChannelProgressDraftMaxLines(params.msteamsConfig) });
|
|
1550
|
+
const normalized = (0, openclaw_plugin_sdk_channel_outbound.normalizeChannelProgressDraftLineIdentity)(line);
|
|
1551
|
+
if (normalized) progressLines = (0, openclaw_plugin_sdk_channel_outbound.mergeChannelProgressDraftLine)(progressLines, typeof line === "object" && line !== void 0 ? line : normalized, { maxLines: (0, openclaw_plugin_sdk_channel_outbound.resolveChannelProgressDraftMaxLines)(params.msteamsConfig) });
|
|
1594
1552
|
}
|
|
1595
1553
|
const hadStarted = progressDraftGate.hasStarted;
|
|
1596
1554
|
const progressActive = await progressDraftGate.noteWork();
|
|
@@ -1627,6 +1585,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
1627
1585
|
return payload;
|
|
1628
1586
|
},
|
|
1629
1587
|
async finalize() {
|
|
1588
|
+
progressDraftGate.cancel();
|
|
1630
1589
|
if (!stream || !streamFinalizationPending || wasCanceled()) return;
|
|
1631
1590
|
const finalEntities = [{
|
|
1632
1591
|
type: "https://schema.org/Message",
|
|
@@ -1678,9 +1637,9 @@ function createTeamsReplyStreamController(params) {
|
|
|
1678
1637
|
//#endregion
|
|
1679
1638
|
//#region extensions/msteams/src/reply-dispatcher.ts
|
|
1680
1639
|
function createMSTeamsReplyDispatcher(params) {
|
|
1681
|
-
const core = getMSTeamsRuntime();
|
|
1640
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
1682
1641
|
const msteamsCfg = params.cfg.channels?.msteams;
|
|
1683
|
-
const conversationType = normalizeOptionalLowercaseString(params.conversationRef.conversation?.conversationType);
|
|
1642
|
+
const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationRef.conversation?.conversationType);
|
|
1684
1643
|
const isTypingSupported = conversationType === "personal" || conversationType === "groupchat";
|
|
1685
1644
|
/**
|
|
1686
1645
|
* Keepalive cadence for the typing indicator while the bot is running
|
|
@@ -1701,13 +1660,13 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1701
1660
|
const streamActiveRef = { current: () => false };
|
|
1702
1661
|
const streamCanceledRef = { current: () => false };
|
|
1703
1662
|
const rawSendTypingIndicator = async () => {
|
|
1704
|
-
await withRevokedProxyFallback({
|
|
1663
|
+
await require_messenger.withRevokedProxyFallback({
|
|
1705
1664
|
run: async () => {
|
|
1706
1665
|
await params.context.sendActivity({ type: "typing" });
|
|
1707
1666
|
},
|
|
1708
1667
|
onRevoked: async () => {
|
|
1709
|
-
const baseRef = buildConversationReference(params.conversationRef);
|
|
1710
|
-
await sendMSTeamsActivityWithReference(params.app, baseRef, { type: "typing" }, { serviceUrlBoundary: resolveMSTeamsSdkCloudOptions(msteamsCfg) });
|
|
1668
|
+
const baseRef = require_messenger.buildConversationReference(params.conversationRef);
|
|
1669
|
+
await require_messenger.sendMSTeamsActivityWithReference(params.app, baseRef, { type: "typing" }, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg) });
|
|
1711
1670
|
},
|
|
1712
1671
|
onRevokedLog: () => {
|
|
1713
1672
|
params.log.debug?.("turn context revoked, sending typing via proactive messaging");
|
|
@@ -1719,7 +1678,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1719
1678
|
if (streamCanceledRef.current()) return;
|
|
1720
1679
|
await rawSendTypingIndicator();
|
|
1721
1680
|
} : async () => {};
|
|
1722
|
-
const { onModelSelected, typingCallbacks, ...replyPipeline } = createChannelMessageReplyPipeline({
|
|
1681
|
+
const { onModelSelected, typingCallbacks, ...replyPipeline } = (0, openclaw_plugin_sdk_channel_outbound.createChannelMessageReplyPipeline)({
|
|
1723
1682
|
cfg: params.cfg,
|
|
1724
1683
|
agentId: params.agentId,
|
|
1725
1684
|
channel: "msteams",
|
|
@@ -1729,7 +1688,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1729
1688
|
keepaliveIntervalMs: TYPING_KEEPALIVE_INTERVAL_MS,
|
|
1730
1689
|
maxDurationMs: TYPING_KEEPALIVE_MAX_DURATION_MS,
|
|
1731
1690
|
onStartError: (err) => {
|
|
1732
|
-
logTypingFailure({
|
|
1691
|
+
(0, openclaw_plugin_sdk_channel_outbound.logTypingFailure)({
|
|
1733
1692
|
log: (message) => params.log.debug?.(message),
|
|
1734
1693
|
channel: "msteams",
|
|
1735
1694
|
action: "start",
|
|
@@ -1743,7 +1702,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1743
1702
|
cfg: params.cfg,
|
|
1744
1703
|
channel: "msteams"
|
|
1745
1704
|
});
|
|
1746
|
-
const mediaMaxBytes = resolveChannelMediaMaxBytes({
|
|
1705
|
+
const mediaMaxBytes = (0, openclaw_plugin_sdk_media_runtime.resolveChannelMediaMaxBytes)({
|
|
1747
1706
|
cfg: params.cfg,
|
|
1748
1707
|
resolveChannelLimitMb: ({ cfg }) => cfg.channels?.msteams?.mediaMaxMb
|
|
1749
1708
|
});
|
|
@@ -1758,13 +1717,13 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1758
1717
|
});
|
|
1759
1718
|
streamActiveRef.current = () => streamController.isStreamActive();
|
|
1760
1719
|
streamCanceledRef.current = () => streamController.wasCanceled();
|
|
1761
|
-
const teamsStreamMode = resolveChannelPreviewStreamMode(msteamsCfg, "partial");
|
|
1762
|
-
const blockStreamingResolved = teamsStreamMode === "block" ? true : resolveChannelStreamingBlockEnabled(msteamsCfg);
|
|
1720
|
+
const teamsStreamMode = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelPreviewStreamMode)(msteamsCfg, "partial");
|
|
1721
|
+
const blockStreamingResolved = teamsStreamMode === "block" ? true : (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingBlockEnabled)(msteamsCfg);
|
|
1763
1722
|
const blockStreamingEnabled = blockStreamingResolved ?? false;
|
|
1764
1723
|
const typingIndicatorEnabled = typeof msteamsCfg?.typingIndicator === "boolean" ? msteamsCfg.typingIndicator : true;
|
|
1765
1724
|
const pendingMessages = [];
|
|
1766
1725
|
const sendMessages = async (messages) => {
|
|
1767
|
-
return sendMSTeamsMessages({
|
|
1726
|
+
return require_messenger.sendMSTeamsMessages({
|
|
1768
1727
|
replyStyle: params.replyStyle,
|
|
1769
1728
|
app: params.app,
|
|
1770
1729
|
appId: params.appId,
|
|
@@ -1782,12 +1741,12 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1782
1741
|
sharePointSiteId: params.sharePointSiteId,
|
|
1783
1742
|
mediaMaxBytes,
|
|
1784
1743
|
feedbackLoopEnabled,
|
|
1785
|
-
serviceUrlBoundary: resolveMSTeamsSdkCloudOptions(msteamsCfg)
|
|
1744
|
+
serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg)
|
|
1786
1745
|
});
|
|
1787
1746
|
};
|
|
1788
1747
|
const queueDeliveryFailureSystemEvent = (failure) => {
|
|
1789
|
-
const classification = classifyMSTeamsSendError(failure.error);
|
|
1790
|
-
const errorText = formatUnknownError(failure.error);
|
|
1748
|
+
const classification = require_setup_surface.classifyMSTeamsSendError(failure.error);
|
|
1749
|
+
const errorText = require_setup_surface.formatUnknownError(failure.error);
|
|
1791
1750
|
const failedAll = failure.failed >= failure.total;
|
|
1792
1751
|
const sentences = [
|
|
1793
1752
|
`Microsoft Teams delivery failed: ${failedAll ? "the previous reply was not delivered" : `${failure.failed} of ${failure.total} message blocks were not delivered`}.`,
|
|
@@ -1802,7 +1761,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1802
1761
|
});
|
|
1803
1762
|
};
|
|
1804
1763
|
const queueReplyPayload = (payload) => {
|
|
1805
|
-
const messages = renderReplyPayloadsToMessages([payload], {
|
|
1764
|
+
const messages = require_messenger.renderReplyPayloadsToMessages([payload], {
|
|
1806
1765
|
textChunkLimit: params.textLimit,
|
|
1807
1766
|
chunkText: true,
|
|
1808
1767
|
mediaMode: "split",
|
|
@@ -1859,9 +1818,9 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1859
1818
|
if (blockStreamingEnabled) await flushPendingMessages();
|
|
1860
1819
|
},
|
|
1861
1820
|
onError: (err, info) => {
|
|
1862
|
-
const errMsg = formatUnknownError(err);
|
|
1863
|
-
const classification = classifyMSTeamsSendError(err);
|
|
1864
|
-
const hint = formatMSTeamsSendErrorHint(classification);
|
|
1821
|
+
const errMsg = require_setup_surface.formatUnknownError(err);
|
|
1822
|
+
const classification = require_setup_surface.classifyMSTeamsSendError(err);
|
|
1823
|
+
const hint = require_setup_surface.formatMSTeamsSendErrorHint(classification);
|
|
1865
1824
|
params.runtime.error?.(`msteams ${info.kind} reply failed: ${errMsg}${hint ? ` (${hint})` : ""}`);
|
|
1866
1825
|
params.log.error("reply failed", {
|
|
1867
1826
|
kind: info.kind,
|
|
@@ -1873,9 +1832,9 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1873
1832
|
});
|
|
1874
1833
|
const markDispatchIdle = () => {
|
|
1875
1834
|
return flushPendingMessages().catch((err) => {
|
|
1876
|
-
const errMsg = formatUnknownError(err);
|
|
1877
|
-
const classification = classifyMSTeamsSendError(err);
|
|
1878
|
-
const hint = formatMSTeamsSendErrorHint(classification);
|
|
1835
|
+
const errMsg = require_setup_surface.formatUnknownError(err);
|
|
1836
|
+
const classification = require_setup_surface.classifyMSTeamsSendError(err);
|
|
1837
|
+
const hint = require_setup_surface.formatMSTeamsSendErrorHint(classification);
|
|
1879
1838
|
params.runtime.error?.(`msteams flush reply failed: ${errMsg}${hint ? ` (${hint})` : ""}`);
|
|
1880
1839
|
params.log.error("flush reply failed", {
|
|
1881
1840
|
error: errMsg,
|
|
@@ -1884,7 +1843,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1884
1843
|
});
|
|
1885
1844
|
}).then(async () => {
|
|
1886
1845
|
const fallbackPayload = await streamController.finalize().catch((err) => {
|
|
1887
|
-
params.log.debug?.("stream finalize failed", { error: formatUnknownError(err) });
|
|
1846
|
+
params.log.debug?.("stream finalize failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
1888
1847
|
});
|
|
1889
1848
|
if (fallbackPayload) {
|
|
1890
1849
|
queueReplyPayload(fallbackPayload);
|
|
@@ -1894,8 +1853,8 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1894
1853
|
baseMarkDispatchIdle();
|
|
1895
1854
|
});
|
|
1896
1855
|
};
|
|
1897
|
-
const previewToolProgressEnabled = resolveChannelStreamingPreviewToolProgress(msteamsCfg);
|
|
1898
|
-
const suppressDefaultToolProgressMessages = resolveChannelStreamingSuppressDefaultToolProgressMessages(msteamsCfg);
|
|
1856
|
+
const previewToolProgressEnabled = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingPreviewToolProgress)(msteamsCfg);
|
|
1857
|
+
const suppressDefaultToolProgressMessages = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingSuppressDefaultToolProgressMessages)(msteamsCfg);
|
|
1899
1858
|
const shouldSuppressDefaultToolProgressMessages = teamsStreamMode === "progress" && suppressDefaultToolProgressMessages && previewToolProgressEnabled;
|
|
1900
1859
|
const progressCallbacks = streamController.hasStream() ? {
|
|
1901
1860
|
onReasoningStream: async (payload) => {
|
|
@@ -1905,7 +1864,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1905
1864
|
await streamController.pushProgressLine(text);
|
|
1906
1865
|
return;
|
|
1907
1866
|
}
|
|
1908
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLine({
|
|
1867
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
|
|
1909
1868
|
event: "item",
|
|
1910
1869
|
itemId: "reasoning",
|
|
1911
1870
|
itemKind: "analysis",
|
|
@@ -1916,7 +1875,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1916
1875
|
onToolStart: async (payload) => {
|
|
1917
1876
|
const name = typeof payload?.name === "string" ? payload.name : void 0;
|
|
1918
1877
|
const detailMode = typeof payload?.detailMode === "string" ? payload.detailMode : void 0;
|
|
1919
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLineForEntry(msteamsCfg, {
|
|
1878
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
|
|
1920
1879
|
event: "tool",
|
|
1921
1880
|
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
1922
1881
|
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
@@ -1926,7 +1885,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1926
1885
|
}, detailMode === "explain" || detailMode === "raw" ? { detailMode } : void 0), name ? { toolName: name } : void 0);
|
|
1927
1886
|
},
|
|
1928
1887
|
onItemEvent: async (payload) => {
|
|
1929
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLineForEntry(msteamsCfg, {
|
|
1888
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
|
|
1930
1889
|
event: "item",
|
|
1931
1890
|
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
1932
1891
|
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
@@ -1942,7 +1901,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1942
1901
|
},
|
|
1943
1902
|
onPlanUpdate: async (payload) => {
|
|
1944
1903
|
if (payload?.phase !== "update") return;
|
|
1945
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLine({
|
|
1904
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
|
|
1946
1905
|
event: "plan",
|
|
1947
1906
|
phase: payload.phase,
|
|
1948
1907
|
...typeof payload?.title === "string" ? { title: payload.title } : {},
|
|
@@ -1952,7 +1911,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1952
1911
|
},
|
|
1953
1912
|
onApprovalEvent: async (payload) => {
|
|
1954
1913
|
if (payload?.phase !== "requested") return;
|
|
1955
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLine({
|
|
1914
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
|
|
1956
1915
|
event: "approval",
|
|
1957
1916
|
phase: payload.phase,
|
|
1958
1917
|
...typeof payload?.title === "string" ? { title: payload.title } : {},
|
|
@@ -1963,7 +1922,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1963
1922
|
},
|
|
1964
1923
|
onCommandOutput: async (payload) => {
|
|
1965
1924
|
if (payload?.phase !== "end") return;
|
|
1966
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLine({
|
|
1925
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
|
|
1967
1926
|
event: "command-output",
|
|
1968
1927
|
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
1969
1928
|
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
@@ -1976,7 +1935,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1976
1935
|
},
|
|
1977
1936
|
onPatchSummary: async (payload) => {
|
|
1978
1937
|
if (payload?.phase !== "end") return;
|
|
1979
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLine({
|
|
1938
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
|
|
1980
1939
|
event: "patch",
|
|
1981
1940
|
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
1982
1941
|
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
@@ -2005,17 +1964,17 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2005
1964
|
}
|
|
2006
1965
|
//#endregion
|
|
2007
1966
|
//#region extensions/msteams/src/sent-message-cache.ts
|
|
2008
|
-
const sentMessages = createPersistentDedupeCache({
|
|
1967
|
+
const sentMessages = (0, openclaw_plugin_sdk_dedupe_runtime.createPersistentDedupeCache)({
|
|
2009
1968
|
globalKey: Symbol.for("openclaw.msteamsSentMessages"),
|
|
2010
1969
|
ttlMs: 1440 * 60 * 1e3,
|
|
2011
1970
|
maxSize: 2e4,
|
|
2012
1971
|
persistent: {
|
|
2013
1972
|
namespace: "msteams.sent-messages",
|
|
2014
1973
|
maxEntries: 1e3,
|
|
2015
|
-
openStore: (options) => getOptionalMSTeamsRuntime()?.state.openKeyedStore(options),
|
|
1974
|
+
openStore: (options) => require_runtime.getOptionalMSTeamsRuntime()?.state.openKeyedStore(options),
|
|
2016
1975
|
logError: (error) => {
|
|
2017
1976
|
try {
|
|
2018
|
-
getOptionalMSTeamsRuntime()?.logging.getChildLogger({
|
|
1977
|
+
require_runtime.getOptionalMSTeamsRuntime()?.logging.getChildLogger({
|
|
2019
1978
|
plugin: "msteams",
|
|
2020
1979
|
feature: "sent-message-state"
|
|
2021
1980
|
}).warn("Microsoft Teams persistent sent-message state failed", { error: String(error) });
|
|
@@ -2037,18 +1996,50 @@ async function wasMSTeamsMessageSentWithPersistence(params) {
|
|
|
2037
1996
|
return await sentMessages.lookup(makeKey(params.conversationId, params.messageId));
|
|
2038
1997
|
}
|
|
2039
1998
|
//#endregion
|
|
1999
|
+
//#region extensions/msteams/src/team-identity.ts
|
|
2000
|
+
const teamGroupIdCache = /* @__PURE__ */ new Map();
|
|
2001
|
+
const TEAM_GROUP_ID_CACHE_MAX_ENTRIES = 500;
|
|
2002
|
+
function cacheTeamGroupId(conversationTeamId, groupId) {
|
|
2003
|
+
teamGroupIdCache.set(conversationTeamId, groupId);
|
|
2004
|
+
(0, openclaw_plugin_sdk_collection_runtime.pruneMapToMaxSize)(teamGroupIdCache, TEAM_GROUP_ID_CACHE_MAX_ENTRIES);
|
|
2005
|
+
}
|
|
2006
|
+
/** Resolve the Graph team GUID without ever treating a Bot Framework team ID as equivalent. */
|
|
2007
|
+
async function resolveTeamGroupId(params) {
|
|
2008
|
+
const activityGroupId = params.aadGroupId?.trim();
|
|
2009
|
+
if (activityGroupId) {
|
|
2010
|
+
cacheTeamGroupId(params.conversationTeamId, activityGroupId);
|
|
2011
|
+
return activityGroupId;
|
|
2012
|
+
}
|
|
2013
|
+
const cached = teamGroupIdCache.get(params.conversationTeamId);
|
|
2014
|
+
if (cached) return cached;
|
|
2015
|
+
const getTeamDetails = params.getTeamDetails;
|
|
2016
|
+
if (!getTeamDetails) return;
|
|
2017
|
+
const groupId = (await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2018
|
+
deadline: params.deadline,
|
|
2019
|
+
label: "MS Teams team details",
|
|
2020
|
+
work: () => getTeamDetails(params.conversationTeamId)
|
|
2021
|
+
})).aadGroupId?.trim();
|
|
2022
|
+
if (!groupId) return;
|
|
2023
|
+
cacheTeamGroupId(params.conversationTeamId, groupId);
|
|
2024
|
+
return groupId;
|
|
2025
|
+
}
|
|
2026
|
+
//#endregion
|
|
2040
2027
|
//#region extensions/msteams/src/monitor-handler/inbound-media.ts
|
|
2028
|
+
function shouldAttemptMSTeamsGraphMediaFallback(params) {
|
|
2029
|
+
const conversationType = params.conversationType.trim().toLowerCase();
|
|
2030
|
+
return params.graphMediaFallback === true && (conversationType === "channel" || conversationType === "groupchat") && (params.htmlSummary?.htmlAttachments ?? 0) > 0;
|
|
2031
|
+
}
|
|
2041
2032
|
function resolveMSTeamsInboundMediaBody(params) {
|
|
2042
2033
|
const unavailableCount = Math.max(0, params.expectedMediaCount - params.mediaCount);
|
|
2043
2034
|
if (unavailableCount === 0) return params.body;
|
|
2044
|
-
return formatInboundMediaUnavailableText({
|
|
2035
|
+
return (0, openclaw_plugin_sdk_channel_inbound.formatInboundMediaUnavailableText)({
|
|
2045
2036
|
body: params.mediaCount > 0 && params.body === params.mediaPlaceholder ? params.materializedMediaPlaceholder : params.body,
|
|
2046
2037
|
mediaPlaceholder: params.mediaCount === 0 ? params.mediaPlaceholder : void 0,
|
|
2047
2038
|
notice: `[msteams ${unavailableCount > 1 ? `${unavailableCount} attachments` : "attachment"} unavailable]`
|
|
2048
2039
|
});
|
|
2049
2040
|
}
|
|
2050
2041
|
async function resolveMSTeamsInboundMedia(params) {
|
|
2051
|
-
const { attachments, htmlSummary, maxBytes, tokenProvider, allowHosts, conversationType, conversationId, conversationMessageId, serviceUrl, activity, log, preserveFilenames } = params;
|
|
2042
|
+
const { attachments, htmlSummary, maxBytes, tokenProvider, allowHosts, conversationType, conversationId, conversationMessageId, teamAadGroupId, serviceUrl, activity, log, preserveFilenames } = params;
|
|
2052
2043
|
let mediaList = await downloadMSTeamsAttachments({
|
|
2053
2044
|
attachments,
|
|
2054
2045
|
maxBytes,
|
|
@@ -2056,12 +2047,20 @@ async function resolveMSTeamsInboundMedia(params) {
|
|
|
2056
2047
|
allowHosts,
|
|
2057
2048
|
authAllowHosts: params.authAllowHosts,
|
|
2058
2049
|
preserveFilenames,
|
|
2050
|
+
deadline: params.deadline,
|
|
2059
2051
|
logger: log
|
|
2060
2052
|
});
|
|
2061
2053
|
if (mediaList.length === 0) {
|
|
2062
2054
|
const attachmentIds = extractMSTeamsHtmlAttachmentIds(attachments);
|
|
2063
2055
|
const hasHtmlFileAttachment = attachmentIds.length > 0;
|
|
2064
|
-
|
|
2056
|
+
const hasChannelOrGroupHtml = shouldAttemptMSTeamsGraphMediaFallback({
|
|
2057
|
+
conversationType,
|
|
2058
|
+
htmlSummary,
|
|
2059
|
+
graphMediaFallback: params.graphMediaFallback
|
|
2060
|
+
});
|
|
2061
|
+
const shouldFetchGraphMessage = hasHtmlFileAttachment || hasChannelOrGroupHtml;
|
|
2062
|
+
const isBotFrameworkPersonalChat = isBotFrameworkPersonalChatId(conversationId);
|
|
2063
|
+
if (hasHtmlFileAttachment && isBotFrameworkPersonalChat) if (!serviceUrl) log.debug?.("bot framework attachment skipped (missing serviceUrl)", {
|
|
2065
2064
|
conversationType,
|
|
2066
2065
|
conversationId
|
|
2067
2066
|
});
|
|
@@ -2073,7 +2072,8 @@ async function resolveMSTeamsInboundMedia(params) {
|
|
|
2073
2072
|
maxBytes,
|
|
2074
2073
|
allowHosts,
|
|
2075
2074
|
authAllowHosts: params.authAllowHosts,
|
|
2076
|
-
preserveFilenames
|
|
2075
|
+
preserveFilenames,
|
|
2076
|
+
deadline: params.deadline
|
|
2077
2077
|
});
|
|
2078
2078
|
if (bfMedia.media.length > 0) mediaList = bfMedia.media;
|
|
2079
2079
|
else log.debug?.("bot framework attachments fetch empty", {
|
|
@@ -2081,50 +2081,41 @@ async function resolveMSTeamsInboundMedia(params) {
|
|
|
2081
2081
|
attachmentCount: bfMedia.attachmentCount ?? attachmentIds.length
|
|
2082
2082
|
});
|
|
2083
2083
|
}
|
|
2084
|
-
if (
|
|
2085
|
-
const
|
|
2084
|
+
if (shouldFetchGraphMessage && mediaList.length === 0 && !isBotFrameworkPersonalChat) {
|
|
2085
|
+
const graphTeamAadGroupId = conversationType.trim().toLowerCase() === "channel" && !teamAadGroupId ? await params.resolveTeamAadGroupId?.() : teamAadGroupId;
|
|
2086
|
+
const messageUrl = buildMSTeamsGraphMessageUrl({
|
|
2086
2087
|
conversationType,
|
|
2087
2088
|
conversationId,
|
|
2088
2089
|
messageId: activity.id ?? void 0,
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2090
|
+
threadRootMessageId: conversationMessageId ?? activity.replyToId,
|
|
2091
|
+
teamAadGroupId: graphTeamAadGroupId,
|
|
2092
|
+
channelId: activity.channelData?.channel?.id
|
|
2092
2093
|
});
|
|
2093
|
-
if (
|
|
2094
|
+
if (!messageUrl) log.debug?.("graph message url unavailable", {
|
|
2094
2095
|
conversationType,
|
|
2095
2096
|
hasChannelData: Boolean(activity.channelData),
|
|
2096
2097
|
messageId: activity.id ?? void 0,
|
|
2097
2098
|
replyToId: activity.replyToId ?? void 0
|
|
2098
2099
|
});
|
|
2099
2100
|
else {
|
|
2100
|
-
const
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
});
|
|
2112
|
-
attempts.push({
|
|
2113
|
-
url: messageUrl,
|
|
2114
|
-
hostedStatus: graphMedia.hostedStatus,
|
|
2115
|
-
attachmentStatus: graphMedia.attachmentStatus,
|
|
2116
|
-
hostedCount: graphMedia.hostedCount,
|
|
2117
|
-
attachmentCount: graphMedia.attachmentCount,
|
|
2118
|
-
tokenError: graphMedia.tokenError
|
|
2119
|
-
});
|
|
2120
|
-
if (graphMedia.media.length > 0) {
|
|
2121
|
-
mediaList = graphMedia.media;
|
|
2122
|
-
break;
|
|
2123
|
-
}
|
|
2124
|
-
if (graphMedia.tokenError) break;
|
|
2125
|
-
}
|
|
2101
|
+
const graphMedia = await downloadMSTeamsGraphMedia({
|
|
2102
|
+
messageUrl,
|
|
2103
|
+
tokenProvider,
|
|
2104
|
+
maxBytes,
|
|
2105
|
+
allowHosts,
|
|
2106
|
+
authAllowHosts: params.authAllowHosts,
|
|
2107
|
+
preserveFilenames,
|
|
2108
|
+
deadline: params.deadline,
|
|
2109
|
+
logger: log
|
|
2110
|
+
});
|
|
2111
|
+
if (graphMedia.media.length > 0) mediaList = graphMedia.media;
|
|
2126
2112
|
if (mediaList.length === 0) log.debug?.("graph media fetch empty", {
|
|
2127
|
-
|
|
2113
|
+
messageUrl,
|
|
2114
|
+
hostedStatus: graphMedia.hostedStatus,
|
|
2115
|
+
attachmentStatus: graphMedia.attachmentStatus,
|
|
2116
|
+
hostedCount: graphMedia.hostedCount,
|
|
2117
|
+
attachmentCount: graphMedia.attachmentCount,
|
|
2118
|
+
tokenError: graphMedia.tokenError,
|
|
2128
2119
|
attachmentIdCount: attachmentIds.length
|
|
2129
2120
|
});
|
|
2130
2121
|
}
|
|
@@ -2143,9 +2134,7 @@ async function resolveMSTeamsInboundMedia(params) {
|
|
|
2143
2134
|
//#region extensions/msteams/src/monitor-handler/message-handler.ts
|
|
2144
2135
|
function extractTextFromHtmlAttachments(attachments) {
|
|
2145
2136
|
for (const attachment of attachments) {
|
|
2146
|
-
|
|
2147
|
-
const content = attachment.content;
|
|
2148
|
-
const raw = typeof content === "string" ? content : isRecord$1(content) && typeof content.text === "string" ? content.text : isRecord$1(content) && typeof content.body === "string" ? content.body : "";
|
|
2137
|
+
const raw = require_resolve_allowlist.extractHtmlFromAttachment(attachment);
|
|
2149
2138
|
if (!raw) continue;
|
|
2150
2139
|
const text = raw.replace(/<at[^>]*>.*?<\/at>/gis, " ").replace(/<a\b[^>]*href=["']([^"']+)["'][^>]*>(.*?)<\/a>/gis, "$2 $1").replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<[^>]+>/g, " ").replace(/ /gi, " ").replace(/&/gi, "&").replace(/\s+/g, " ").trim();
|
|
2151
2140
|
if (text) return text;
|
|
@@ -2176,7 +2165,7 @@ function buildStoredConversationReference(params) {
|
|
|
2176
2165
|
const clientInfo = activity.entities?.find((e) => e.type === "clientInfo");
|
|
2177
2166
|
const tenantId = activity.channelData?.tenant?.id ?? conversation?.tenantId;
|
|
2178
2167
|
const aadObjectId = from?.aadObjectId;
|
|
2179
|
-
const serviceUrl = tryNormalizeBotFrameworkServiceUrl(activity.serviceUrl);
|
|
2168
|
+
const serviceUrl = require_resolve_allowlist.tryNormalizeBotFrameworkServiceUrl(activity.serviceUrl);
|
|
2180
2169
|
return {
|
|
2181
2170
|
activityId: activity.id,
|
|
2182
2171
|
user: from ? {
|
|
@@ -2185,10 +2174,6 @@ function buildStoredConversationReference(params) {
|
|
|
2185
2174
|
aadObjectId: from.aadObjectId
|
|
2186
2175
|
} : void 0,
|
|
2187
2176
|
agent,
|
|
2188
|
-
bot: agent ? {
|
|
2189
|
-
id: agent.id,
|
|
2190
|
-
name: agent.name
|
|
2191
|
-
} : void 0,
|
|
2192
2177
|
conversation: {
|
|
2193
2178
|
id: conversationId,
|
|
2194
2179
|
conversationType,
|
|
@@ -2206,16 +2191,16 @@ function buildStoredConversationReference(params) {
|
|
|
2206
2191
|
}
|
|
2207
2192
|
function createMSTeamsMessageHandler(deps) {
|
|
2208
2193
|
const { cfg, runtime, appId, app, tokenProvider, textLimit, mediaMaxBytes, conversationStore, pollStore, log } = deps;
|
|
2209
|
-
const core = getMSTeamsRuntime();
|
|
2194
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
2210
2195
|
const logVerboseMessage = (message) => {
|
|
2211
2196
|
if (core.logging.shouldLogVerbose()) log.debug?.(message);
|
|
2212
2197
|
};
|
|
2213
2198
|
const msteamsCfg = cfg.channels?.msteams;
|
|
2214
|
-
const contextVisibilityMode = resolveChannelContextVisibilityMode({
|
|
2199
|
+
const contextVisibilityMode = (0, openclaw_plugin_sdk_context_visibility_runtime.resolveChannelContextVisibilityMode)({
|
|
2215
2200
|
cfg,
|
|
2216
2201
|
channel: "msteams"
|
|
2217
2202
|
});
|
|
2218
|
-
const historyLimit = Math.max(0, msteamsCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT);
|
|
2203
|
+
const historyLimit = Math.max(0, msteamsCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? openclaw_plugin_sdk_reply_history.DEFAULT_GROUP_HISTORY_LIMIT);
|
|
2219
2204
|
const conversationHistories = /* @__PURE__ */ new Map();
|
|
2220
2205
|
const inboundDebounceMs = core.channel.debounce.resolveInboundDebounceMs({
|
|
2221
2206
|
cfg,
|
|
@@ -2233,7 +2218,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2233
2218
|
});
|
|
2234
2219
|
const attachmentPlaceholder = attachmentPresentation.placeholder;
|
|
2235
2220
|
const rawBody = text || attachmentPlaceholder;
|
|
2236
|
-
const quoteInfo = extractMSTeamsQuoteInfo(attachments);
|
|
2221
|
+
const quoteInfo = require_channel.extractMSTeamsQuoteInfo(attachments);
|
|
2237
2222
|
let quoteSenderId;
|
|
2238
2223
|
let quoteSenderName;
|
|
2239
2224
|
const from = activity.from;
|
|
@@ -2241,8 +2226,8 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2241
2226
|
const attachmentTypes = attachments.map((att) => typeof att.contentType === "string" ? att.contentType : void 0).filter(Boolean).slice(0, 3);
|
|
2242
2227
|
const htmlSummary = summarizeMSTeamsHtmlAttachments(attachments);
|
|
2243
2228
|
log.info("received message", {
|
|
2244
|
-
rawText: truncateUtf16Safe(rawText, 50),
|
|
2245
|
-
text: truncateUtf16Safe(text, 50),
|
|
2229
|
+
rawText: (0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(rawText, 50),
|
|
2230
|
+
text: (0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(text, 50),
|
|
2246
2231
|
attachments: attachments.length,
|
|
2247
2232
|
attachmentTypes,
|
|
2248
2233
|
from: from?.id,
|
|
@@ -2254,10 +2239,11 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2254
2239
|
return;
|
|
2255
2240
|
}
|
|
2256
2241
|
const rawConversationId = conversation?.id ?? "";
|
|
2257
|
-
const conversationId = normalizeMSTeamsConversationId(rawConversationId);
|
|
2258
|
-
const conversationMessageId = extractMSTeamsConversationMessageId(rawConversationId);
|
|
2242
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(rawConversationId);
|
|
2243
|
+
const conversationMessageId = require_channel.extractMSTeamsConversationMessageId(rawConversationId);
|
|
2259
2244
|
const conversationType = conversation?.conversationType ?? "personal";
|
|
2260
2245
|
const teamId = activity.channelData?.team?.id;
|
|
2246
|
+
const graphChannelId = activity.channelData?.channel?.id?.trim() || conversationId;
|
|
2261
2247
|
const conversationRef = buildStoredConversationReference({
|
|
2262
2248
|
activity,
|
|
2263
2249
|
conversationId,
|
|
@@ -2288,7 +2274,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2288
2274
|
log.debug?.("dropping dm (dms disabled)");
|
|
2289
2275
|
return;
|
|
2290
2276
|
}
|
|
2291
|
-
const allowMatch = resolveMSTeamsAllowlistMatch({
|
|
2277
|
+
const allowMatch = require_channel.resolveMSTeamsAllowlistMatch({
|
|
2292
2278
|
allowFrom: effectiveDmAllowFrom,
|
|
2293
2279
|
senderId,
|
|
2294
2280
|
senderName,
|
|
@@ -2296,7 +2282,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2296
2282
|
});
|
|
2297
2283
|
if (senderAccess.decision === "pairing") {
|
|
2298
2284
|
conversationStore.upsert(conversationId, conversationRef).catch((err) => {
|
|
2299
|
-
log.debug?.("failed to save conversation reference", { error: formatUnknownError(err) });
|
|
2285
|
+
log.debug?.("failed to save conversation reference", { error: require_setup_surface.formatUnknownError(err) });
|
|
2300
2286
|
});
|
|
2301
2287
|
if (await pairing.upsertPairingRequest({
|
|
2302
2288
|
id: senderId,
|
|
@@ -2309,7 +2295,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2309
2295
|
log.debug?.("dropping dm (not allowlisted)", {
|
|
2310
2296
|
sender: senderId,
|
|
2311
2297
|
label: senderName,
|
|
2312
|
-
allowlistMatch: formatAllowlistMatchMeta(allowMatch)
|
|
2298
|
+
allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
|
|
2313
2299
|
});
|
|
2314
2300
|
log.info("dropping dm (not allowlisted)", {
|
|
2315
2301
|
sender: senderId,
|
|
@@ -2320,7 +2306,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2320
2306
|
dmPolicy,
|
|
2321
2307
|
groupPolicy
|
|
2322
2308
|
}),
|
|
2323
|
-
allowlistMatch: formatAllowlistMatchMeta(allowMatch)
|
|
2309
|
+
allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
|
|
2324
2310
|
});
|
|
2325
2311
|
return;
|
|
2326
2312
|
}
|
|
@@ -2353,7 +2339,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2353
2339
|
return;
|
|
2354
2340
|
}
|
|
2355
2341
|
if (!senderAccess.allowed && senderAccess.reasonCode === "group_policy_not_allowlisted") {
|
|
2356
|
-
const allowMatch = resolveMSTeamsAllowlistMatch({
|
|
2342
|
+
const allowMatch = require_channel.resolveMSTeamsAllowlistMatch({
|
|
2357
2343
|
allowFrom: effectiveGroupAllowFrom,
|
|
2358
2344
|
senderId,
|
|
2359
2345
|
senderName,
|
|
@@ -2362,18 +2348,18 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2362
2348
|
log.debug?.("dropping group message (not in groupAllowFrom)", {
|
|
2363
2349
|
sender: senderId,
|
|
2364
2350
|
label: senderName,
|
|
2365
|
-
allowlistMatch: formatAllowlistMatchMeta(allowMatch)
|
|
2351
|
+
allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
|
|
2366
2352
|
});
|
|
2367
2353
|
log.info("dropping group message (not in groupAllowFrom)", {
|
|
2368
2354
|
sender: senderId,
|
|
2369
2355
|
label: senderName,
|
|
2370
|
-
allowlistMatch: formatAllowlistMatchMeta(allowMatch)
|
|
2356
|
+
allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
|
|
2371
2357
|
});
|
|
2372
2358
|
return;
|
|
2373
2359
|
}
|
|
2374
2360
|
}
|
|
2375
2361
|
if (commandAccess.shouldBlockControlCommand) {
|
|
2376
|
-
logInboundDrop({
|
|
2362
|
+
(0, openclaw_plugin_sdk_channel_inbound.logInboundDrop)({
|
|
2377
2363
|
log: logVerboseMessage,
|
|
2378
2364
|
channel: "msteams",
|
|
2379
2365
|
reason: "control command (unauthorized)",
|
|
@@ -2382,9 +2368,9 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2382
2368
|
return;
|
|
2383
2369
|
}
|
|
2384
2370
|
conversationStore.upsert(conversationId, conversationRef).catch((err) => {
|
|
2385
|
-
log.debug?.("failed to save conversation reference", { error: formatUnknownError(err) });
|
|
2371
|
+
log.debug?.("failed to save conversation reference", { error: require_setup_surface.formatUnknownError(err) });
|
|
2386
2372
|
});
|
|
2387
|
-
const pollVote = extractMSTeamsPollVote(activity);
|
|
2373
|
+
const pollVote = require_polls.extractMSTeamsPollVote(activity);
|
|
2388
2374
|
if (pollVote) {
|
|
2389
2375
|
try {
|
|
2390
2376
|
if (!await pollStore.recordVote({
|
|
@@ -2400,12 +2386,17 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2400
2386
|
} catch (err) {
|
|
2401
2387
|
log.error("failed to record poll vote", {
|
|
2402
2388
|
pollId: pollVote.pollId,
|
|
2403
|
-
error: formatUnknownError(err)
|
|
2389
|
+
error: require_setup_surface.formatUnknownError(err)
|
|
2404
2390
|
});
|
|
2405
2391
|
}
|
|
2406
2392
|
return;
|
|
2407
2393
|
}
|
|
2408
|
-
|
|
2394
|
+
const mayRecoverGraphMedia = Boolean(htmlSummary?.attachmentIds.length) || shouldAttemptMSTeamsGraphMediaFallback({
|
|
2395
|
+
conversationType,
|
|
2396
|
+
htmlSummary: htmlSummary ?? void 0,
|
|
2397
|
+
graphMediaFallback: msteamsCfg?.graphMediaFallback
|
|
2398
|
+
});
|
|
2399
|
+
if (!rawBody && !mayRecoverGraphMedia) {
|
|
2409
2400
|
log.debug?.("skipping empty message after stripping mentions");
|
|
2410
2401
|
return;
|
|
2411
2402
|
}
|
|
@@ -2420,13 +2411,13 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2420
2411
|
id: isDirectMessage ? senderId : conversationId
|
|
2421
2412
|
}
|
|
2422
2413
|
});
|
|
2423
|
-
route.sessionKey = resolveMSTeamsRouteSessionKey({
|
|
2414
|
+
route.sessionKey = require_channel.resolveMSTeamsRouteSessionKey({
|
|
2424
2415
|
baseSessionKey: route.sessionKey,
|
|
2425
2416
|
isChannel,
|
|
2426
2417
|
conversationMessageId,
|
|
2427
2418
|
replyToId: activity.replyToId
|
|
2428
2419
|
});
|
|
2429
|
-
const preview = sliceUtf16Safe(rawBody.replace(/\s+/g, " "), 0, 160);
|
|
2420
|
+
const preview = (0, openclaw_plugin_sdk_text_utility_runtime.sliceUtf16Safe)(rawBody.replace(/\s+/g, " "), 0, 160);
|
|
2430
2421
|
const inboundLabel = isDirectMessage ? `Teams DM from ${senderName}` : `Teams message in ${conversationType} from ${senderName}`;
|
|
2431
2422
|
const enqueuePrimaryMessageSystemEvent = () => core.system.enqueueSystemEvent(inboundLabel, {
|
|
2432
2423
|
sessionKey: route.sessionKey,
|
|
@@ -2434,14 +2425,14 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2434
2425
|
});
|
|
2435
2426
|
const channelId = conversationId;
|
|
2436
2427
|
const { teamConfig, channelConfig } = channelGate;
|
|
2437
|
-
const { requireMention, replyStyle } = resolveMSTeamsReplyPolicy({
|
|
2428
|
+
const { requireMention, replyStyle } = require_channel.resolveMSTeamsReplyPolicy({
|
|
2438
2429
|
isDirectMessage,
|
|
2439
2430
|
globalConfig: msteamsCfg,
|
|
2440
2431
|
teamConfig,
|
|
2441
2432
|
channelConfig
|
|
2442
2433
|
});
|
|
2443
|
-
const timestamp = parseMSTeamsActivityTimestamp(activity.timestamp);
|
|
2444
|
-
const mentionDecision = resolveInboundMentionDecision({
|
|
2434
|
+
const timestamp = require_channel.parseMSTeamsActivityTimestamp(activity.timestamp);
|
|
2435
|
+
const mentionDecision = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundMentionDecision)({
|
|
2445
2436
|
facts: {
|
|
2446
2437
|
canDetectMention: true,
|
|
2447
2438
|
wasMentioned: params.wasMentioned,
|
|
@@ -2464,124 +2455,163 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2464
2455
|
requireMention,
|
|
2465
2456
|
mentioned
|
|
2466
2457
|
});
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2458
|
+
if (rawBody) {
|
|
2459
|
+
enqueuePrimaryMessageSystemEvent();
|
|
2460
|
+
(0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).record({
|
|
2461
|
+
historyKey: conversationId,
|
|
2462
|
+
limit: historyLimit,
|
|
2463
|
+
entry: {
|
|
2464
|
+
sender: senderName,
|
|
2465
|
+
body: rawBody,
|
|
2466
|
+
timestamp: timestamp?.getTime(),
|
|
2467
|
+
messageId: activity.id ?? void 0
|
|
2468
|
+
}
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2478
2471
|
return;
|
|
2479
2472
|
}
|
|
2480
2473
|
}
|
|
2481
|
-
|
|
2482
|
-
let
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2474
|
+
const preprocessingDeadline = require_resolve_allowlist.createMSTeamsInboundDeadline();
|
|
2475
|
+
let teamAadGroupId = activity.channelData?.team?.aadGroupId?.trim() || void 0;
|
|
2476
|
+
const conversationTeamId = isChannel ? teamId : void 0;
|
|
2477
|
+
let teamGroupIdPromise;
|
|
2478
|
+
const resolveChannelTeamGroupId = async () => {
|
|
2479
|
+
if (!conversationTeamId) return;
|
|
2480
|
+
teamGroupIdPromise ??= resolveTeamGroupId({
|
|
2481
|
+
conversationTeamId,
|
|
2482
|
+
aadGroupId: teamAadGroupId,
|
|
2483
|
+
getTeamDetails: context.getTeamDetails,
|
|
2484
|
+
deadline: preprocessingDeadline
|
|
2485
|
+
}).catch((err) => {
|
|
2486
|
+
log.debug?.("failed to resolve Teams AAD group ID", {
|
|
2487
|
+
teamId: conversationTeamId,
|
|
2488
|
+
error: require_setup_surface.formatUnknownError(err)
|
|
2496
2489
|
});
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2490
|
+
});
|
|
2491
|
+
teamAadGroupId = await teamGroupIdPromise;
|
|
2492
|
+
return teamAadGroupId;
|
|
2493
|
+
};
|
|
2494
|
+
let mediaList = [];
|
|
2495
|
+
try {
|
|
2496
|
+
mediaList = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2497
|
+
deadline: preprocessingDeadline,
|
|
2498
|
+
label: "MS Teams inbound media",
|
|
2499
|
+
work: () => resolveMSTeamsInboundMedia({
|
|
2500
|
+
attachments,
|
|
2501
|
+
htmlSummary: htmlSummary ?? void 0,
|
|
2502
|
+
maxBytes: mediaMaxBytes,
|
|
2503
|
+
tokenProvider,
|
|
2504
|
+
allowHosts: msteamsCfg?.mediaAllowHosts,
|
|
2505
|
+
authAllowHosts: msteamsCfg?.mediaAuthAllowHosts,
|
|
2506
|
+
graphMediaFallback: msteamsCfg?.graphMediaFallback,
|
|
2507
|
+
conversationType,
|
|
2508
|
+
conversationId,
|
|
2509
|
+
conversationMessageId: conversationMessageId ?? void 0,
|
|
2510
|
+
teamAadGroupId,
|
|
2511
|
+
resolveTeamAadGroupId: resolveChannelTeamGroupId,
|
|
2512
|
+
serviceUrl: activity.serviceUrl,
|
|
2513
|
+
activity: {
|
|
2514
|
+
id: activity.id,
|
|
2515
|
+
replyToId: activity.replyToId,
|
|
2516
|
+
channelData: activity.channelData
|
|
2517
|
+
},
|
|
2518
|
+
log,
|
|
2519
|
+
deadline: preprocessingDeadline,
|
|
2520
|
+
preserveFilenames: cfg.media?.preserveFilenames
|
|
2521
|
+
})
|
|
2522
|
+
});
|
|
2523
|
+
} catch (err) {
|
|
2524
|
+
log.debug?.("failed to resolve inbound Teams media", { error: require_setup_surface.formatUnknownError(err) });
|
|
2507
2525
|
}
|
|
2508
|
-
const mediaList = await resolveMSTeamsInboundMedia({
|
|
2509
|
-
attachments,
|
|
2510
|
-
htmlSummary: htmlSummary ?? void 0,
|
|
2511
|
-
maxBytes: mediaMaxBytes,
|
|
2512
|
-
tokenProvider,
|
|
2513
|
-
allowHosts: msteamsCfg?.mediaAllowHosts,
|
|
2514
|
-
authAllowHosts: msteamsCfg?.mediaAuthAllowHosts,
|
|
2515
|
-
conversationType,
|
|
2516
|
-
conversationId: graphConversationId,
|
|
2517
|
-
conversationMessageId: conversationMessageId ?? void 0,
|
|
2518
|
-
serviceUrl: activity.serviceUrl,
|
|
2519
|
-
activity: {
|
|
2520
|
-
id: activity.id,
|
|
2521
|
-
replyToId: activity.replyToId,
|
|
2522
|
-
channelData: activity.channelData
|
|
2523
|
-
},
|
|
2524
|
-
log,
|
|
2525
|
-
preserveFilenames: cfg.media?.preserveFilenames
|
|
2526
|
-
});
|
|
2527
2526
|
const mediaPayload = buildMSTeamsMediaPayload(mediaList);
|
|
2528
2527
|
const materializedMediaPlaceholder = resolveMSTeamsInboundAttachmentPresentation(mediaList.map((media) => ({
|
|
2529
2528
|
contentType: media.contentType,
|
|
2530
2529
|
name: media.path
|
|
2531
2530
|
}))).placeholder;
|
|
2532
2531
|
const agentBody = resolveMSTeamsInboundMediaBody({
|
|
2533
|
-
body: rawBody,
|
|
2532
|
+
body: rawBody || materializedMediaPlaceholder,
|
|
2534
2533
|
mediaPlaceholder: attachmentPlaceholder,
|
|
2535
2534
|
materializedMediaPlaceholder,
|
|
2536
2535
|
expectedMediaCount: attachmentPresentation.expectedMediaCount,
|
|
2537
2536
|
mediaCount: mediaList.length
|
|
2538
2537
|
});
|
|
2538
|
+
if (!agentBody) {
|
|
2539
|
+
log.debug?.("skipping empty message after Graph media recovery");
|
|
2540
|
+
return;
|
|
2541
|
+
}
|
|
2542
|
+
enqueuePrimaryMessageSystemEvent();
|
|
2543
|
+
teamAadGroupId = await resolveChannelTeamGroupId();
|
|
2544
|
+
let quoteBodyFull;
|
|
2545
|
+
const quoteMessageId = quoteInfo?.id;
|
|
2546
|
+
if (quoteMessageId && isDirectMessage && conversationId.startsWith("19:")) try {
|
|
2547
|
+
const graphToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2548
|
+
deadline: preprocessingDeadline,
|
|
2549
|
+
label: "MS Teams quote token",
|
|
2550
|
+
work: () => tokenProvider.getAccessToken("https://graph.microsoft.com")
|
|
2551
|
+
});
|
|
2552
|
+
quoteBodyFull = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2553
|
+
deadline: preprocessingDeadline,
|
|
2554
|
+
label: "MS Teams quote lookup",
|
|
2555
|
+
work: () => require_messenger.fetchChatMessageText(graphToken, conversationId, quoteMessageId, preprocessingDeadline)
|
|
2556
|
+
});
|
|
2557
|
+
} catch (err) {
|
|
2558
|
+
log.debug?.("failed to fetch full quoted message text", { error: require_setup_surface.formatUnknownError(err) });
|
|
2559
|
+
}
|
|
2539
2560
|
let threadContext;
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
const
|
|
2561
|
+
const threadParentId = activity.replyToId;
|
|
2562
|
+
const channelGroupId = teamAadGroupId;
|
|
2563
|
+
if (threadParentId && isChannel && channelGroupId) try {
|
|
2564
|
+
const graphToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2565
|
+
deadline: preprocessingDeadline,
|
|
2566
|
+
label: "MS Teams thread token",
|
|
2567
|
+
work: () => tokenProvider.getAccessToken("https://graph.microsoft.com")
|
|
2568
|
+
});
|
|
2569
|
+
const [parentResult, repliesResult] = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2570
|
+
deadline: preprocessingDeadline,
|
|
2571
|
+
label: "MS Teams thread history",
|
|
2572
|
+
work: () => Promise.allSettled([fetchParentMessageCached(graphToken, channelGroupId, conversationId, threadParentId, (token, groupId, requestedChannelId, messageId) => require_messenger.fetchChannelMessage(token, groupId, requestedChannelId, messageId, preprocessingDeadline)), require_messenger.fetchThreadReplies(graphToken, channelGroupId, conversationId, threadParentId, 50, preprocessingDeadline)])
|
|
2573
|
+
});
|
|
2544
2574
|
const parentMsg = parentResult.status === "fulfilled" ? parentResult.value : void 0;
|
|
2545
2575
|
const replies = repliesResult.status === "fulfilled" ? repliesResult.value : [];
|
|
2546
|
-
if (parentResult.status === "rejected") log.debug?.("failed to fetch parent message", { error: formatUnknownError(parentResult.reason) });
|
|
2547
|
-
if (repliesResult.status === "rejected") log.debug?.("failed to fetch thread replies", { error: formatUnknownError(repliesResult.reason) });
|
|
2548
|
-
const isThreadSenderAllowed = (msg) => groupPolicy === "allowlist" ? resolveMSTeamsAllowlistMatch({
|
|
2576
|
+
if (parentResult.status === "rejected") log.debug?.("failed to fetch parent message", { error: require_setup_surface.formatUnknownError(parentResult.reason) });
|
|
2577
|
+
if (repliesResult.status === "rejected") log.debug?.("failed to fetch thread replies", { error: require_setup_surface.formatUnknownError(repliesResult.reason) });
|
|
2578
|
+
const isThreadSenderAllowed = (msg) => groupPolicy === "allowlist" ? require_channel.resolveMSTeamsAllowlistMatch({
|
|
2549
2579
|
allowFrom: effectiveGroupAllowFrom,
|
|
2550
2580
|
senderId: msg.from?.user?.id ?? "",
|
|
2551
2581
|
senderName: msg.from?.user?.displayName,
|
|
2552
2582
|
allowNameMatching
|
|
2553
2583
|
}).allowed : true;
|
|
2554
2584
|
const parentSummary = summarizeParentMessage(parentMsg);
|
|
2555
|
-
const visibleParentMessages = parentMsg ? filterSupplementalContextItems({
|
|
2585
|
+
const visibleParentMessages = parentMsg ? (0, openclaw_plugin_sdk_context_visibility_runtime.filterSupplementalContextItems)({
|
|
2556
2586
|
items: [parentMsg],
|
|
2557
2587
|
mode: contextVisibilityMode,
|
|
2558
2588
|
kind: "thread",
|
|
2559
2589
|
isSenderAllowed: isThreadSenderAllowed
|
|
2560
2590
|
}).items : [];
|
|
2561
|
-
if (parentSummary && visibleParentMessages.length > 0 && shouldInjectParentContext(route.sessionKey,
|
|
2591
|
+
if (parentSummary && visibleParentMessages.length > 0 && shouldInjectParentContext(route.sessionKey, threadParentId)) {
|
|
2562
2592
|
core.system.enqueueSystemEvent(formatParentContextEvent(parentSummary), {
|
|
2563
2593
|
sessionKey: route.sessionKey,
|
|
2564
|
-
contextKey: `msteams:thread-parent:${conversationId}:${
|
|
2594
|
+
contextKey: `msteams:thread-parent:${conversationId}:${threadParentId}`
|
|
2565
2595
|
});
|
|
2566
|
-
markParentContextInjected(route.sessionKey,
|
|
2596
|
+
markParentContextInjected(route.sessionKey, threadParentId);
|
|
2567
2597
|
}
|
|
2568
2598
|
const allMessages = parentMsg ? [parentMsg, ...replies] : replies;
|
|
2569
2599
|
quoteSenderId = parentMsg?.from?.user?.id ?? parentMsg?.from?.application?.id ?? void 0;
|
|
2570
2600
|
quoteSenderName = parentMsg?.from?.user?.displayName ?? parentMsg?.from?.application?.displayName ?? quoteInfo?.sender;
|
|
2571
|
-
const { items: threadMessages } = filterSupplementalContextItems({
|
|
2601
|
+
const { items: threadMessages } = (0, openclaw_plugin_sdk_context_visibility_runtime.filterSupplementalContextItems)({
|
|
2572
2602
|
items: allMessages,
|
|
2573
2603
|
mode: contextVisibilityMode,
|
|
2574
2604
|
kind: "thread",
|
|
2575
2605
|
isSenderAllowed: isThreadSenderAllowed
|
|
2576
2606
|
});
|
|
2577
|
-
const formatted = formatThreadContext(threadMessages, activity.id);
|
|
2607
|
+
const formatted = require_messenger.formatThreadContext(threadMessages, activity.id);
|
|
2578
2608
|
if (formatted) threadContext = formatted;
|
|
2579
2609
|
} catch (err) {
|
|
2580
|
-
log.debug?.("failed to fetch thread history", { error: formatUnknownError(err) });
|
|
2610
|
+
log.debug?.("failed to fetch thread history", { error: require_setup_surface.formatUnknownError(err) });
|
|
2581
2611
|
}
|
|
2582
2612
|
quoteSenderName ??= quoteInfo?.sender;
|
|
2583
2613
|
const envelopeFrom = isDirectMessage ? senderName : conversationType;
|
|
2584
|
-
const { storePath, envelopeOptions, previousTimestamp } = resolveInboundSessionEnvelopeContext({
|
|
2614
|
+
const { storePath, envelopeOptions, previousTimestamp } = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundSessionEnvelopeContext)({
|
|
2585
2615
|
cfg,
|
|
2586
2616
|
agentId: route.agentId,
|
|
2587
2617
|
sessionKey: route.sessionKey
|
|
@@ -2596,7 +2626,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2596
2626
|
});
|
|
2597
2627
|
const isRoomish = !isDirectMessage;
|
|
2598
2628
|
const historyKey = isRoomish ? conversationId : void 0;
|
|
2599
|
-
if (isRoomish && historyKey) combinedBody = createChannelHistoryWindow({ historyMap: conversationHistories }).buildPendingContext({
|
|
2629
|
+
if (isRoomish && historyKey) combinedBody = (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).buildPendingContext({
|
|
2600
2630
|
historyKey,
|
|
2601
2631
|
limit: historyLimit,
|
|
2602
2632
|
currentMessage: combinedBody,
|
|
@@ -2608,26 +2638,26 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2608
2638
|
envelope: envelopeOptions
|
|
2609
2639
|
})
|
|
2610
2640
|
});
|
|
2611
|
-
const inboundHistory = isRoomish && historyKey && historyLimit > 0 ? createChannelHistoryWindow({ historyMap: conversationHistories }).buildInboundHistory({
|
|
2641
|
+
const inboundHistory = isRoomish && historyKey && historyLimit > 0 ? (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).buildInboundHistory({
|
|
2612
2642
|
historyKey,
|
|
2613
2643
|
limit: historyLimit
|
|
2614
2644
|
}) : void 0;
|
|
2615
2645
|
const commandBody = text.trim();
|
|
2616
|
-
const quoteSenderAllowed = quoteInfo && quoteInfo.sender ? !isChannel || groupPolicy !== "allowlist" ? true : resolveMSTeamsAllowlistMatch({
|
|
2646
|
+
const quoteSenderAllowed = quoteInfo && quoteInfo.sender ? !isChannel || groupPolicy !== "allowlist" ? true : require_channel.resolveMSTeamsAllowlistMatch({
|
|
2617
2647
|
allowFrom: effectiveGroupAllowFrom,
|
|
2618
2648
|
senderId: quoteSenderId ?? "",
|
|
2619
2649
|
senderName: quoteSenderName,
|
|
2620
2650
|
allowNameMatching
|
|
2621
2651
|
}).allowed : true;
|
|
2622
2652
|
const bodyForAgent = threadContext ? `[Thread history]\n${threadContext}\n[/Thread history]\n\n${agentBody}` : agentBody;
|
|
2623
|
-
const nativeChannelId = isChannel &&
|
|
2624
|
-
const ctxPayload = buildChannelInboundEventContext({
|
|
2653
|
+
const nativeChannelId = isChannel && teamAadGroupId ? `${teamAadGroupId}/${graphChannelId}` : void 0;
|
|
2654
|
+
const ctxPayload = (0, openclaw_plugin_sdk_channel_inbound.buildChannelInboundEventContext)({
|
|
2625
2655
|
channel: "msteams",
|
|
2626
2656
|
finalize: core.channel.reply.finalizeInboundContext,
|
|
2627
2657
|
contextVisibility: contextVisibilityMode,
|
|
2628
2658
|
supplemental: { quote: quoteInfo ? {
|
|
2629
|
-
id: activity.replyToId ?? void 0,
|
|
2630
|
-
body: quoteInfo.body,
|
|
2659
|
+
id: quoteInfo.id ?? activity.replyToId ?? void 0,
|
|
2660
|
+
body: quoteBodyFull ?? quoteInfo.body,
|
|
2631
2661
|
sender: quoteInfo.sender,
|
|
2632
2662
|
senderAllowed: quoteSenderAllowed,
|
|
2633
2663
|
isQuote: true
|
|
@@ -2725,7 +2755,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2725
2755
|
ctxPayload,
|
|
2726
2756
|
recordInboundSession: core.channel.session.recordInboundSession,
|
|
2727
2757
|
record: { onRecordError: (err) => {
|
|
2728
|
-
logVerboseMessage(`msteams: failed updating session meta: ${formatUnknownError(err)}`);
|
|
2758
|
+
logVerboseMessage(`msteams: failed updating session meta: ${require_setup_surface.formatUnknownError(err)}`);
|
|
2729
2759
|
} },
|
|
2730
2760
|
history: {
|
|
2731
2761
|
isGroup: isRoomish,
|
|
@@ -2737,7 +2767,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2737
2767
|
dispatcher,
|
|
2738
2768
|
onSettled: () => markDispatchIdle()
|
|
2739
2769
|
}),
|
|
2740
|
-
runDispatch: () => dispatchReplyFromConfigWithSettledDispatcher({
|
|
2770
|
+
runDispatch: () => (0, openclaw_plugin_sdk_channel_inbound.dispatchReplyFromConfigWithSettledDispatcher)({
|
|
2741
2771
|
cfg,
|
|
2742
2772
|
ctxPayload,
|
|
2743
2773
|
dispatcher,
|
|
@@ -2750,8 +2780,8 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2750
2780
|
});
|
|
2751
2781
|
const dispatchResult = turnResult.dispatched ? turnResult.dispatchResult : void 0;
|
|
2752
2782
|
const queuedFinal = dispatchResult?.queuedFinal ?? false;
|
|
2753
|
-
const counts = resolveInboundReplyDispatchCounts(dispatchResult);
|
|
2754
|
-
const hasFinalResponse = hasFinalInboundReplyDispatch(dispatchResult);
|
|
2783
|
+
const counts = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundReplyDispatchCounts)(dispatchResult);
|
|
2784
|
+
const hasFinalResponse = (0, openclaw_plugin_sdk_channel_inbound.hasFinalInboundReplyDispatch)(dispatchResult);
|
|
2755
2785
|
log.info("dispatch complete", {
|
|
2756
2786
|
queuedFinal,
|
|
2757
2787
|
counts
|
|
@@ -2760,8 +2790,8 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2760
2790
|
const finalCount = counts.final;
|
|
2761
2791
|
logVerboseMessage(`msteams: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${teamsTo}`);
|
|
2762
2792
|
} catch (err) {
|
|
2763
|
-
log.error("dispatch failed", { error: formatUnknownError(err) });
|
|
2764
|
-
runtime.error(`msteams dispatch failed: ${formatUnknownError(err)}`);
|
|
2793
|
+
log.error("dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
2794
|
+
runtime.error(`msteams dispatch failed: ${require_setup_surface.formatUnknownError(err)}`);
|
|
2765
2795
|
try {
|
|
2766
2796
|
await context.sendActivity("⚠️ Something went wrong. Please try again.");
|
|
2767
2797
|
} catch {}
|
|
@@ -2770,7 +2800,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2770
2800
|
const inboundDebouncer = core.channel.debounce.createInboundDebouncer({
|
|
2771
2801
|
debounceMs: inboundDebounceMs,
|
|
2772
2802
|
buildKey: (entry) => {
|
|
2773
|
-
const conversationId = normalizeMSTeamsConversationId(entry.context.activity.conversation?.id ?? "");
|
|
2803
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(entry.context.activity.conversation?.id ?? "");
|
|
2774
2804
|
const senderId = entry.context.activity.from?.aadObjectId ?? entry.context.activity.from?.id ?? "";
|
|
2775
2805
|
if (!senderId || !conversationId) return null;
|
|
2776
2806
|
return `msteams:${appId}:${conversationId}:${senderId}`;
|
|
@@ -2802,7 +2832,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2802
2832
|
});
|
|
2803
2833
|
},
|
|
2804
2834
|
onError: (err) => {
|
|
2805
|
-
runtime.error(`msteams debounce flush failed: ${formatUnknownError(err)}`);
|
|
2835
|
+
runtime.error(`msteams debounce flush failed: ${require_setup_surface.formatUnknownError(err)}`);
|
|
2806
2836
|
}
|
|
2807
2837
|
});
|
|
2808
2838
|
return async function handleTeamsMessage(context) {
|
|
@@ -2811,9 +2841,9 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2811
2841
|
const rawText = activity.text?.trim() ?? "";
|
|
2812
2842
|
const htmlText = extractTextFromHtmlAttachments(attachments);
|
|
2813
2843
|
const valueText = rawText || htmlText ? "" : serializeMSTeamsAdaptiveCardActionValue(activity.value);
|
|
2814
|
-
const text = stripMSTeamsMentionTags(rawText || htmlText || valueText || "");
|
|
2815
|
-
const wasMentioned = wasMSTeamsBotMentioned(activity);
|
|
2816
|
-
const conversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
2844
|
+
const text = require_channel.stripMSTeamsMentionTags(rawText || htmlText || valueText || "");
|
|
2845
|
+
const wasMentioned = require_channel.wasMSTeamsBotMentioned(activity);
|
|
2846
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
2817
2847
|
const replyToId = activity.replyToId ?? void 0;
|
|
2818
2848
|
const implicitMentionKinds = conversationId && replyToId && await wasMSTeamsMessageSentWithPersistence({
|
|
2819
2849
|
conversationId,
|
|
@@ -2831,29 +2861,13 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2831
2861
|
}
|
|
2832
2862
|
//#endregion
|
|
2833
2863
|
//#region extensions/msteams/src/monitor-handler/reaction-handler.ts
|
|
2834
|
-
/** Teams reaction type names → Unicode emoji. */
|
|
2835
|
-
const TEAMS_REACTION_EMOJI = {
|
|
2836
|
-
like: "👍",
|
|
2837
|
-
heart: "❤️",
|
|
2838
|
-
laugh: "😆",
|
|
2839
|
-
surprised: "😮",
|
|
2840
|
-
sad: "😢",
|
|
2841
|
-
angry: "😡"
|
|
2842
|
-
};
|
|
2843
|
-
/**
|
|
2844
|
-
* Map a Teams reaction type string to a Unicode emoji.
|
|
2845
|
-
* Falls back to the raw type if not recognized.
|
|
2846
|
-
*/
|
|
2847
|
-
function mapReactionEmoji(reactionType) {
|
|
2848
|
-
return TEAMS_REACTION_EMOJI[reactionType] ?? reactionType;
|
|
2849
|
-
}
|
|
2850
2864
|
/**
|
|
2851
2865
|
* Create a handler for MS Teams reaction activities (reactionsAdded / reactionsRemoved).
|
|
2852
2866
|
* The returned function accepts a turn context and a direction string.
|
|
2853
2867
|
*/
|
|
2854
2868
|
function createMSTeamsReactionHandler(deps) {
|
|
2855
2869
|
const { cfg, log } = deps;
|
|
2856
|
-
const core = getMSTeamsRuntime();
|
|
2870
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
2857
2871
|
const msteamsCfg = cfg.channels?.msteams;
|
|
2858
2872
|
return async function handleReaction(context, direction) {
|
|
2859
2873
|
const activity = context.activity;
|
|
@@ -2867,7 +2881,7 @@ function createMSTeamsReactionHandler(deps) {
|
|
|
2867
2881
|
log.debug?.("reaction activity missing from.id; skipping");
|
|
2868
2882
|
return;
|
|
2869
2883
|
}
|
|
2870
|
-
const conversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
2884
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
2871
2885
|
const conversationType = activity.conversation?.conversationType ?? "personal";
|
|
2872
2886
|
const isGroupChat = conversationType === "groupChat" || activity.conversation?.isGroup === true;
|
|
2873
2887
|
const isChannel = conversationType === "channel";
|
|
@@ -2900,7 +2914,7 @@ function createMSTeamsReactionHandler(deps) {
|
|
|
2900
2914
|
const targetMessageId = activity.replyToId ?? "unknown";
|
|
2901
2915
|
for (const reaction of reactions) {
|
|
2902
2916
|
const reactionType = reaction.type ?? "unknown";
|
|
2903
|
-
const emoji =
|
|
2917
|
+
const emoji = require_messenger.resolveMSTeamsReactionEmoji(reactionType);
|
|
2904
2918
|
const label = direction === "added" ? `Teams reaction ${emoji} added by ${senderName} on message ${targetMessageId}` : `Teams reaction ${emoji} removed by ${senderName} from message ${targetMessageId}`;
|
|
2905
2919
|
log.info(`reaction ${direction}`, {
|
|
2906
2920
|
sender: senderId,
|
|
@@ -3059,7 +3073,7 @@ function registerMSTeamsHandlers(handler, deps) {
|
|
|
3059
3073
|
try {
|
|
3060
3074
|
await handleTeamsMessage(context);
|
|
3061
3075
|
} catch (err) {
|
|
3062
|
-
deps.runtime.error(`msteams handler failed: ${formatUnknownError(err)}`);
|
|
3076
|
+
deps.runtime.error(`msteams handler failed: ${require_setup_surface.formatUnknownError(err)}`);
|
|
3063
3077
|
}
|
|
3064
3078
|
await next();
|
|
3065
3079
|
});
|
|
@@ -3069,7 +3083,7 @@ function registerMSTeamsHandlers(handler, deps) {
|
|
|
3069
3083
|
const botId = ctx.activity?.recipient?.id;
|
|
3070
3084
|
const msteamsCfg = deps.cfg.channels?.msteams;
|
|
3071
3085
|
for (const member of membersAdded) if (member.id === botId) {
|
|
3072
|
-
const isPersonal = (normalizeOptionalLowercaseString(ctx.activity?.conversation?.conversationType) ?? "personal") === "personal";
|
|
3086
|
+
const isPersonal = ((0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(ctx.activity?.conversation?.conversationType) ?? "personal") === "personal";
|
|
3073
3087
|
if (isPersonal && msteamsCfg?.welcomeCard !== false) {
|
|
3074
3088
|
const card = buildWelcomeCard({
|
|
3075
3089
|
botName: ctx.activity?.recipient?.name ?? void 0,
|
|
@@ -3085,7 +3099,7 @@ function registerMSTeamsHandlers(handler, deps) {
|
|
|
3085
3099
|
});
|
|
3086
3100
|
deps.log.info("sent welcome card");
|
|
3087
3101
|
} catch (err) {
|
|
3088
|
-
deps.log.debug?.("failed to send welcome card", { error: formatUnknownError(err) });
|
|
3102
|
+
deps.log.debug?.("failed to send welcome card", { error: require_setup_surface.formatUnknownError(err) });
|
|
3089
3103
|
}
|
|
3090
3104
|
} else if (!isPersonal && msteamsCfg?.groupWelcomeCard === true) {
|
|
3091
3105
|
const botName = ctx.activity?.recipient?.name ?? void 0;
|
|
@@ -3093,7 +3107,7 @@ function registerMSTeamsHandlers(handler, deps) {
|
|
|
3093
3107
|
await ctx.sendActivity(buildGroupWelcomeText(botName));
|
|
3094
3108
|
deps.log.info("sent group welcome message");
|
|
3095
3109
|
} catch (err) {
|
|
3096
|
-
deps.log.debug?.("failed to send group welcome", { error: formatUnknownError(err) });
|
|
3110
|
+
deps.log.debug?.("failed to send group welcome", { error: require_setup_surface.formatUnknownError(err) });
|
|
3097
3111
|
}
|
|
3098
3112
|
} else deps.log.debug?.("skipping welcome (disabled by config or conversation type)");
|
|
3099
3113
|
} else deps.log.debug?.("member added", { member: member.id });
|
|
@@ -3155,14 +3169,14 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3155
3169
|
userComment = JSON.parse(value.actionValue.feedback).feedbackText || void 0;
|
|
3156
3170
|
} catch {}
|
|
3157
3171
|
const rawConversationId = activity.conversation?.id ?? "unknown";
|
|
3158
|
-
const conversationId = normalizeMSTeamsConversationId(rawConversationId);
|
|
3172
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(rawConversationId);
|
|
3159
3173
|
const senderId = activity.from?.aadObjectId ?? activity.from?.id ?? "unknown";
|
|
3160
3174
|
const messageId = value.replyToId ?? activity.replyToId ?? "unknown";
|
|
3161
3175
|
const isNegative = reaction === "dislike";
|
|
3162
|
-
const convType = normalizeOptionalLowercaseString(activity.conversation?.conversationType);
|
|
3176
|
+
const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(activity.conversation?.conversationType);
|
|
3163
3177
|
const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
|
|
3164
3178
|
const isChannel = convType === "channel";
|
|
3165
|
-
const core = getMSTeamsRuntime();
|
|
3179
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
3166
3180
|
const route = core.channel.routing.resolveAgentRoute({
|
|
3167
3181
|
cfg: deps.cfg,
|
|
3168
3182
|
channel: "msteams",
|
|
@@ -3171,8 +3185,8 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3171
3185
|
id: isDirectMessage ? senderId : conversationId
|
|
3172
3186
|
}
|
|
3173
3187
|
});
|
|
3174
|
-
const feedbackThreadId = isChannel ? extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId ?? void 0 : void 0;
|
|
3175
|
-
if (feedbackThreadId) route.sessionKey = resolveThreadSessionKeys({
|
|
3188
|
+
const feedbackThreadId = isChannel ? require_channel.extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId ?? void 0 : void 0;
|
|
3189
|
+
if (feedbackThreadId) route.sessionKey = (0, openclaw_plugin_sdk_routing.resolveThreadSessionKeys)({
|
|
3176
3190
|
baseSessionKey: route.sessionKey,
|
|
3177
3191
|
threadId: feedbackThreadId,
|
|
3178
3192
|
parentSessionKey: route.sessionKey
|
|
@@ -3194,8 +3208,8 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3194
3208
|
try {
|
|
3195
3209
|
const storePath = core.channel.session.resolveStorePath(deps.cfg.session?.store, { agentId: route.agentId });
|
|
3196
3210
|
const safeKey = route.sessionKey.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3197
|
-
await appendRegularFile({
|
|
3198
|
-
filePath:
|
|
3211
|
+
await (0, openclaw_plugin_sdk_security_runtime.appendRegularFile)({
|
|
3212
|
+
filePath: node_path.default.join(storePath, `${safeKey}.jsonl`),
|
|
3199
3213
|
content: `${JSON.stringify(feedbackEvent)}\n`,
|
|
3200
3214
|
rejectSymlinkParents: true
|
|
3201
3215
|
}).catch(() => {});
|
|
@@ -3211,10 +3225,6 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3211
3225
|
id: activity.recipient.id,
|
|
3212
3226
|
name: activity.recipient.name
|
|
3213
3227
|
} : void 0,
|
|
3214
|
-
bot: activity.recipient ? {
|
|
3215
|
-
id: activity.recipient.id,
|
|
3216
|
-
name: activity.recipient.name
|
|
3217
|
-
} : void 0,
|
|
3218
3228
|
conversation: {
|
|
3219
3229
|
id: conversationId,
|
|
3220
3230
|
conversationType: activity.conversation?.conversationType,
|
|
@@ -3236,7 +3246,7 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3236
3246
|
userComment,
|
|
3237
3247
|
log: deps.log
|
|
3238
3248
|
}).catch((err) => {
|
|
3239
|
-
deps.log.error("feedback reflection failed", { error: formatUnknownError(err) });
|
|
3249
|
+
deps.log.error("feedback reflection failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3240
3250
|
});
|
|
3241
3251
|
return true;
|
|
3242
3252
|
}
|
|
@@ -3249,18 +3259,18 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3249
3259
|
const expiredUploadMessage = "The file upload request has expired. Please try sending the file again.";
|
|
3250
3260
|
const activity = context.activity;
|
|
3251
3261
|
if (activity.type !== "invoke" || activity.name !== "fileConsent/invoke") return false;
|
|
3252
|
-
const consentResponse = parseFileConsentInvoke(activity);
|
|
3262
|
+
const consentResponse = require_messenger.parseFileConsentInvoke(activity);
|
|
3253
3263
|
if (!consentResponse) {
|
|
3254
3264
|
log.debug?.("invalid file consent invoke", { value: activity.value });
|
|
3255
3265
|
return false;
|
|
3256
3266
|
}
|
|
3257
3267
|
const uploadId = typeof consentResponse.context?.uploadId === "string" ? consentResponse.context.uploadId : void 0;
|
|
3258
|
-
const inMemoryFile = getPendingUpload(uploadId);
|
|
3259
|
-
const fsFile = inMemoryFile ? void 0 : await getPendingUploadFs(uploadId);
|
|
3268
|
+
const inMemoryFile = require_messenger.getPendingUpload(uploadId);
|
|
3269
|
+
const fsFile = inMemoryFile ? void 0 : await require_messenger.getPendingUploadFs(uploadId);
|
|
3260
3270
|
const pendingFile = inMemoryFile ?? fsFile;
|
|
3261
3271
|
if (pendingFile) {
|
|
3262
|
-
const pendingConversationId = normalizeMSTeamsConversationId(pendingFile.conversationId);
|
|
3263
|
-
const invokeConversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
3272
|
+
const pendingConversationId = require_channel.normalizeMSTeamsConversationId(pendingFile.conversationId);
|
|
3273
|
+
const invokeConversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
3264
3274
|
if (!invokeConversationId || pendingConversationId !== invokeConversationId) {
|
|
3265
3275
|
log.info("file consent conversation mismatch", {
|
|
3266
3276
|
uploadId,
|
|
@@ -3278,12 +3288,12 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3278
3288
|
size: pendingFile.buffer.length
|
|
3279
3289
|
});
|
|
3280
3290
|
try {
|
|
3281
|
-
await uploadToConsentUrl({
|
|
3291
|
+
await require_messenger.uploadToConsentUrl({
|
|
3282
3292
|
url: consentResponse.uploadInfo.uploadUrl,
|
|
3283
3293
|
buffer: pendingFile.buffer,
|
|
3284
3294
|
contentType: pendingFile.contentType
|
|
3285
3295
|
});
|
|
3286
|
-
const fileInfoCard = buildFileInfoCard({
|
|
3296
|
+
const fileInfoCard = require_messenger.buildFileInfoCard({
|
|
3287
3297
|
filename: consentResponse.uploadInfo.name,
|
|
3288
3298
|
contentUrl: consentResponse.uploadInfo.contentUrl,
|
|
3289
3299
|
uniqueId: consentResponse.uploadInfo.uniqueId,
|
|
@@ -3313,12 +3323,12 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3313
3323
|
} catch (err) {
|
|
3314
3324
|
log.error("file upload failed", {
|
|
3315
3325
|
uploadId,
|
|
3316
|
-
error: formatUnknownError(err)
|
|
3326
|
+
error: require_setup_surface.formatUnknownError(err)
|
|
3317
3327
|
});
|
|
3318
3328
|
await context.sendActivity("File upload failed. Please try again.");
|
|
3319
3329
|
} finally {
|
|
3320
|
-
removePendingUpload(uploadId);
|
|
3321
|
-
await removePendingUploadFs(uploadId);
|
|
3330
|
+
require_messenger.removePendingUpload(uploadId);
|
|
3331
|
+
await require_messenger.removePendingUploadFs(uploadId);
|
|
3322
3332
|
}
|
|
3323
3333
|
} else {
|
|
3324
3334
|
log.debug?.("pending file not found for consent", { uploadId });
|
|
@@ -3326,8 +3336,8 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3326
3336
|
}
|
|
3327
3337
|
else {
|
|
3328
3338
|
log.debug?.("user declined file consent", { uploadId });
|
|
3329
|
-
removePendingUpload(uploadId);
|
|
3330
|
-
await removePendingUploadFs(uploadId);
|
|
3339
|
+
require_messenger.removePendingUpload(uploadId);
|
|
3340
|
+
await require_messenger.removePendingUploadFs(uploadId);
|
|
3331
3341
|
}
|
|
3332
3342
|
return true;
|
|
3333
3343
|
}
|
|
@@ -3338,7 +3348,7 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3338
3348
|
*/
|
|
3339
3349
|
async function runMSTeamsFileConsentInvokeHandler(context, log) {
|
|
3340
3350
|
try {
|
|
3341
|
-
await withRevokedProxyFallback({
|
|
3351
|
+
await require_messenger.withRevokedProxyFallback({
|
|
3342
3352
|
run: async () => await handleMSTeamsFileConsentInvoke(context, log),
|
|
3343
3353
|
onRevoked: async () => true,
|
|
3344
3354
|
onRevokedLog: () => {
|
|
@@ -3346,7 +3356,7 @@ async function runMSTeamsFileConsentInvokeHandler(context, log) {
|
|
|
3346
3356
|
}
|
|
3347
3357
|
});
|
|
3348
3358
|
} catch (err) {
|
|
3349
|
-
log.debug?.("file consent handler error", { error: formatUnknownError(err) });
|
|
3359
|
+
log.debug?.("file consent handler error", { error: require_setup_surface.formatUnknownError(err) });
|
|
3350
3360
|
}
|
|
3351
3361
|
}
|
|
3352
3362
|
//#endregion
|
|
@@ -3365,7 +3375,7 @@ function applyMSTeamsWebhookTimeouts(httpServer, opts) {
|
|
|
3365
3375
|
//#endregion
|
|
3366
3376
|
//#region extensions/msteams/src/monitor.ts
|
|
3367
3377
|
async function monitorMSTeamsProvider(opts) {
|
|
3368
|
-
const core = getMSTeamsRuntime();
|
|
3378
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
3369
3379
|
const log = core.logging.getChildLogger({ name: "msteams" });
|
|
3370
3380
|
let cfg = opts.cfg;
|
|
3371
3381
|
let msteamsCfg = cfg.channels?.msteams;
|
|
@@ -3376,7 +3386,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3376
3386
|
shutdown: async () => {}
|
|
3377
3387
|
};
|
|
3378
3388
|
}
|
|
3379
|
-
const creds = resolveMSTeamsCredentials(msteamsCfg);
|
|
3389
|
+
const creds = require_resolve_allowlist.resolveMSTeamsCredentials(msteamsCfg);
|
|
3380
3390
|
if (!creds) {
|
|
3381
3391
|
log.error("msteams credentials not configured");
|
|
3382
3392
|
return {
|
|
@@ -3392,26 +3402,22 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3392
3402
|
throw new Error(`exit ${code}`);
|
|
3393
3403
|
}
|
|
3394
3404
|
};
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
let
|
|
3398
|
-
|
|
3405
|
+
const configuredAllowFrom = msteamsCfg.allowFrom;
|
|
3406
|
+
const configuredGroupAllowFrom = msteamsCfg.groupAllowFrom;
|
|
3407
|
+
let allowFrom = require_resolve_allowlist.projectStableMSTeamsUserAllowlist(configuredAllowFrom);
|
|
3408
|
+
let groupAllowFrom = require_resolve_allowlist.projectStableMSTeamsUserAllowlist(configuredGroupAllowFrom);
|
|
3409
|
+
let teamsConfig = require_resolve_allowlist.projectStableMSTeamsTeamsConfig(msteamsCfg.teams);
|
|
3410
|
+
const allowNameMatching = (0, openclaw_plugin_sdk_dangerous_name_runtime.isDangerousNameMatchingEnabled)(msteamsCfg);
|
|
3399
3411
|
const cleanAllowEntry = (entry) => entry.replace(/^(msteams|teams):/i, "").replace(/^user:/i, "").trim();
|
|
3400
3412
|
const isStableUserId = (entry) => /^[0-9a-fA-F-]{16,}$/.test(entry);
|
|
3401
3413
|
const cleanAllowEntries = (entries) => entries?.map((entry) => cleanAllowEntry(entry)).filter((entry) => entry && entry !== "*") ?? [];
|
|
3402
|
-
const
|
|
3403
|
-
const additions = cleanAllowEntries(entries).filter((entry) => isStableUserId(entry));
|
|
3404
|
-
return additions.length > 0 ? mergeAllowlist({
|
|
3405
|
-
existing: entries,
|
|
3406
|
-
additions
|
|
3407
|
-
}) : entries;
|
|
3408
|
-
};
|
|
3414
|
+
const isMutableUserEntry = (entry) => !isStableUserId(entry) && !/^accessGroup:/i.test(entry);
|
|
3409
3415
|
const resolveAllowlistUsers = async (label, entries) => {
|
|
3410
3416
|
if (entries.length === 0) return {
|
|
3411
3417
|
additions: [],
|
|
3412
3418
|
unresolved: []
|
|
3413
3419
|
};
|
|
3414
|
-
const resolved = await resolveMSTeamsUserAllowlist({
|
|
3420
|
+
const resolved = await require_resolve_allowlist.resolveMSTeamsUserAllowlist({
|
|
3415
3421
|
cfg,
|
|
3416
3422
|
entries
|
|
3417
3423
|
});
|
|
@@ -3419,102 +3425,44 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3419
3425
|
const unresolved = [];
|
|
3420
3426
|
for (const entry of resolved) if (entry.resolved && entry.id) additions.push(entry.id);
|
|
3421
3427
|
else unresolved.push(entry.input);
|
|
3422
|
-
summarizeMapping(label, resolved.filter((entry) => entry.resolved && entry.id).map((entry) => `${entry.input}→${entry.id}`), unresolved, runtime);
|
|
3428
|
+
(0, openclaw_plugin_sdk_allow_from.summarizeMapping)(label, resolved.filter((entry) => entry.resolved && entry.id).map((entry) => `${entry.input}→${entry.id}`), unresolved, runtime);
|
|
3423
3429
|
return {
|
|
3424
3430
|
additions,
|
|
3425
3431
|
unresolved
|
|
3426
3432
|
};
|
|
3427
3433
|
};
|
|
3428
3434
|
try {
|
|
3429
|
-
allowFrom = mergeStableUserIds(allowFrom);
|
|
3430
|
-
if (Array.isArray(groupAllowFrom) && groupAllowFrom.length > 0) groupAllowFrom = mergeStableUserIds(groupAllowFrom);
|
|
3431
3435
|
if (allowNameMatching) {
|
|
3432
|
-
const allowEntries = cleanAllowEntries(
|
|
3436
|
+
const allowEntries = cleanAllowEntries(configuredAllowFrom).filter(isMutableUserEntry);
|
|
3433
3437
|
if (allowEntries.length > 0) {
|
|
3434
3438
|
const { additions } = await resolveAllowlistUsers("msteams users", allowEntries);
|
|
3435
|
-
allowFrom = mergeAllowlist({
|
|
3439
|
+
allowFrom = (0, openclaw_plugin_sdk_allow_from.mergeAllowlist)({
|
|
3436
3440
|
existing: allowFrom,
|
|
3437
3441
|
additions
|
|
3438
3442
|
});
|
|
3439
3443
|
}
|
|
3440
|
-
if (Array.isArray(
|
|
3441
|
-
const groupEntries = cleanAllowEntries(
|
|
3444
|
+
if (Array.isArray(configuredGroupAllowFrom) && configuredGroupAllowFrom.length > 0) {
|
|
3445
|
+
const groupEntries = cleanAllowEntries(configuredGroupAllowFrom).filter(isMutableUserEntry);
|
|
3442
3446
|
if (groupEntries.length > 0) {
|
|
3443
3447
|
const { additions } = await resolveAllowlistUsers("msteams group users", groupEntries);
|
|
3444
|
-
groupAllowFrom = mergeAllowlist({
|
|
3448
|
+
groupAllowFrom = (0, openclaw_plugin_sdk_allow_from.mergeAllowlist)({
|
|
3445
3449
|
existing: groupAllowFrom,
|
|
3446
3450
|
additions
|
|
3447
3451
|
});
|
|
3448
3452
|
}
|
|
3449
3453
|
}
|
|
3450
3454
|
}
|
|
3451
|
-
if (
|
|
3452
|
-
const
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
input: teamKey,
|
|
3460
|
-
teamKey
|
|
3461
|
-
});
|
|
3462
|
-
continue;
|
|
3463
|
-
}
|
|
3464
|
-
for (const channelKey of channelKeys) entries.push({
|
|
3465
|
-
input: `${teamKey}/${channelKey}`,
|
|
3466
|
-
teamKey,
|
|
3467
|
-
channelKey
|
|
3468
|
-
});
|
|
3469
|
-
}
|
|
3470
|
-
if (entries.length > 0) {
|
|
3471
|
-
const resolved = await resolveMSTeamsChannelAllowlist({
|
|
3472
|
-
cfg,
|
|
3473
|
-
entries: entries.map((entry) => entry.input)
|
|
3474
|
-
});
|
|
3475
|
-
const mapping = [];
|
|
3476
|
-
const unresolved = [];
|
|
3477
|
-
const nextTeams = { ...teamsConfig };
|
|
3478
|
-
resolved.forEach((entry, idx) => {
|
|
3479
|
-
const source = entries[idx];
|
|
3480
|
-
if (!source) return;
|
|
3481
|
-
const sourceTeam = teamsConfig?.[source.teamKey] ?? {};
|
|
3482
|
-
if (!entry.resolved || !entry.teamId) {
|
|
3483
|
-
unresolved.push(entry.input);
|
|
3484
|
-
return;
|
|
3485
|
-
}
|
|
3486
|
-
mapping.push(entry.channelId ? `${entry.input}→${entry.teamId}/${entry.channelId}` : `${entry.input}→${entry.teamId}`);
|
|
3487
|
-
const existing = nextTeams[entry.teamId] ?? {};
|
|
3488
|
-
const mergedChannels = {
|
|
3489
|
-
...sourceTeam.channels,
|
|
3490
|
-
...existing.channels
|
|
3491
|
-
};
|
|
3492
|
-
const mergedTeam = {
|
|
3493
|
-
...sourceTeam,
|
|
3494
|
-
...existing,
|
|
3495
|
-
channels: mergedChannels
|
|
3496
|
-
};
|
|
3497
|
-
nextTeams[entry.teamId] = mergedTeam;
|
|
3498
|
-
if (source.channelKey && entry.channelId) {
|
|
3499
|
-
const sourceChannel = sourceTeam.channels?.[source.channelKey];
|
|
3500
|
-
if (sourceChannel) nextTeams[entry.teamId] = {
|
|
3501
|
-
...mergedTeam,
|
|
3502
|
-
channels: {
|
|
3503
|
-
...mergedChannels,
|
|
3504
|
-
[entry.channelId]: {
|
|
3505
|
-
...sourceChannel,
|
|
3506
|
-
...mergedChannels?.[entry.channelId]
|
|
3507
|
-
}
|
|
3508
|
-
}
|
|
3509
|
-
};
|
|
3510
|
-
}
|
|
3511
|
-
});
|
|
3512
|
-
teamsConfig = nextTeams;
|
|
3513
|
-
summarizeMapping("msteams channels", mapping, unresolved, runtime);
|
|
3514
|
-
}
|
|
3455
|
+
if (msteamsCfg.teams && Object.keys(msteamsCfg.teams).length > 0) {
|
|
3456
|
+
const resolved = await require_resolve_allowlist.resolveMSTeamsTeamsConfig({
|
|
3457
|
+
cfg,
|
|
3458
|
+
teamIdMode: "bot-framework",
|
|
3459
|
+
teams: msteamsCfg.teams
|
|
3460
|
+
});
|
|
3461
|
+
teamsConfig = resolved.teams;
|
|
3462
|
+
(0, openclaw_plugin_sdk_allow_from.summarizeMapping)("msteams channels", resolved.mapping, resolved.unresolved, runtime);
|
|
3515
3463
|
}
|
|
3516
3464
|
} catch (err) {
|
|
3517
|
-
runtime.error?.(`msteams resolve failed;
|
|
3465
|
+
runtime.error?.(`msteams resolve failed; mutable allowlist entries are disabled. ${require_setup_surface.formatUnknownError(err)}`);
|
|
3518
3466
|
}
|
|
3519
3467
|
msteamsCfg = {
|
|
3520
3468
|
...msteamsCfg,
|
|
@@ -3534,8 +3482,8 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3534
3482
|
const MB = 1024 * 1024;
|
|
3535
3483
|
const agentDefaults = cfg.agents?.defaults;
|
|
3536
3484
|
const mediaMaxBytes = typeof agentDefaults?.mediaMaxMb === "number" && agentDefaults.mediaMaxMb > 0 ? Math.floor(agentDefaults.mediaMaxMb * MB) : 8 * MB;
|
|
3537
|
-
const conversationStore = opts.conversationStore ?? createMSTeamsConversationStoreState();
|
|
3538
|
-
const pollStore = opts.pollStore ?? createMSTeamsPollStoreState();
|
|
3485
|
+
const conversationStore = opts.conversationStore ?? require_polls.createMSTeamsConversationStoreState();
|
|
3486
|
+
const pollStore = opts.pollStore ?? require_polls.createMSTeamsPollStoreState();
|
|
3539
3487
|
log.info(`starting provider (port ${port})`);
|
|
3540
3488
|
const express = await import("express");
|
|
3541
3489
|
const expressApp = express.default();
|
|
@@ -3547,7 +3495,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3547
3495
|
}
|
|
3548
3496
|
next();
|
|
3549
3497
|
});
|
|
3550
|
-
expressApp.use(express.json({ limit: DEFAULT_WEBHOOK_MAX_BODY_BYTES }));
|
|
3498
|
+
expressApp.use(express.json({ limit: openclaw_plugin_sdk_webhook_ingress.DEFAULT_WEBHOOK_MAX_BODY_BYTES }));
|
|
3551
3499
|
expressApp.use((err, _req, res, next) => {
|
|
3552
3500
|
if (err && typeof err === "object" && "status" in err && err.status === 413) {
|
|
3553
3501
|
res.status(413).json({ error: "Payload too large" });
|
|
@@ -3556,11 +3504,12 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3556
3504
|
next(err);
|
|
3557
3505
|
});
|
|
3558
3506
|
const configuredPath = msteamsCfg.webhook?.path ?? "/api/messages";
|
|
3559
|
-
const
|
|
3560
|
-
|
|
3561
|
-
|
|
3507
|
+
const ssoConnectionName = msteamsCfg.sso?.enabled && msteamsCfg.sso.connectionName ? msteamsCfg.sso.connectionName : void 0;
|
|
3508
|
+
const { app } = await require_resolve_allowlist.loadMSTeamsSdkWithAuth(creds, {
|
|
3509
|
+
...require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg),
|
|
3510
|
+
httpServerAdapter: await require_resolve_allowlist.createMSTeamsExpressAdapter(expressApp),
|
|
3562
3511
|
messagingEndpoint: configuredPath,
|
|
3563
|
-
...
|
|
3512
|
+
...ssoConnectionName ? { oauthDefaultConnectionName: ssoConnectionName } : {}
|
|
3564
3513
|
});
|
|
3565
3514
|
if (configuredPath !== "/api/messages") {
|
|
3566
3515
|
let warnedLegacyMessagesRoute = false;
|
|
@@ -3573,16 +3522,12 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3573
3522
|
expressApp(req, res, next);
|
|
3574
3523
|
});
|
|
3575
3524
|
}
|
|
3576
|
-
const tokenProvider = createMSTeamsTokenProvider(app);
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
connectionName: msteamsCfg.sso.connectionName
|
|
3583
|
-
};
|
|
3584
|
-
log.debug?.("msteams sso enabled", { connectionName: msteamsCfg.sso.connectionName });
|
|
3585
|
-
}
|
|
3525
|
+
const tokenProvider = require_resolve_allowlist.createMSTeamsTokenProvider(app);
|
|
3526
|
+
const ssoDeps = ssoConnectionName ? {
|
|
3527
|
+
tokenStore: require_sso_token_store.createMSTeamsSsoTokenStoreFs(),
|
|
3528
|
+
connectionName: ssoConnectionName
|
|
3529
|
+
} : void 0;
|
|
3530
|
+
if (ssoDeps) log.debug?.("msteams sso enabled", { connectionName: ssoDeps.connectionName });
|
|
3586
3531
|
const handler = buildActivityHandler();
|
|
3587
3532
|
const handlerDeps = {
|
|
3588
3533
|
cfg,
|
|
@@ -3594,15 +3539,14 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3594
3539
|
mediaMaxBytes,
|
|
3595
3540
|
conversationStore,
|
|
3596
3541
|
pollStore,
|
|
3597
|
-
log
|
|
3598
|
-
sso: ssoDeps
|
|
3542
|
+
log
|
|
3599
3543
|
};
|
|
3600
3544
|
registerMSTeamsHandlers(handler, handlerDeps);
|
|
3601
3545
|
app.on("card.action", async (ctx) => {
|
|
3602
3546
|
const adaptedCtx = adaptSdkContext(ctx, app);
|
|
3603
3547
|
try {
|
|
3604
3548
|
const activity = adaptedCtx.activity;
|
|
3605
|
-
const vote = extractMSTeamsPollVote(activity);
|
|
3549
|
+
const vote = require_polls.extractMSTeamsPollVote(activity);
|
|
3606
3550
|
if (vote) {
|
|
3607
3551
|
const voterId = activity?.from?.aadObjectId ?? activity?.from?.id ?? "unknown";
|
|
3608
3552
|
try {
|
|
@@ -3620,8 +3564,8 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3620
3564
|
value: "Poll not found."
|
|
3621
3565
|
};
|
|
3622
3566
|
}
|
|
3623
|
-
const pollConversationId = existingPoll.conversationId ? normalizeMSTeamsConversationId(existingPoll.conversationId) : void 0;
|
|
3624
|
-
const activityConversationId = normalizeMSTeamsConversationId(activity?.conversation?.id ?? "");
|
|
3567
|
+
const pollConversationId = existingPoll.conversationId ? require_channel.normalizeMSTeamsConversationId(existingPoll.conversationId) : void 0;
|
|
3568
|
+
const activityConversationId = require_channel.normalizeMSTeamsConversationId(activity?.conversation?.id ?? "");
|
|
3625
3569
|
if (pollConversationId && pollConversationId !== activityConversationId) {
|
|
3626
3570
|
log.info("poll vote ignored (conversation mismatch)", {
|
|
3627
3571
|
pollId: vote.pollId,
|
|
@@ -3658,7 +3602,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3658
3602
|
} catch (err) {
|
|
3659
3603
|
log.error("failed to record poll vote", {
|
|
3660
3604
|
pollId: vote.pollId,
|
|
3661
|
-
error: formatUnknownError(err)
|
|
3605
|
+
error: require_setup_surface.formatUnknownError(err)
|
|
3662
3606
|
});
|
|
3663
3607
|
return {
|
|
3664
3608
|
statusCode: 500,
|
|
@@ -3675,7 +3619,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3675
3619
|
}
|
|
3676
3620
|
}
|
|
3677
3621
|
handler.run(adaptedCtx).catch((err) => {
|
|
3678
|
-
log.error("msteams card.action dispatch failed", { error: formatUnknownError(err) });
|
|
3622
|
+
log.error("msteams card.action dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3679
3623
|
});
|
|
3680
3624
|
return {
|
|
3681
3625
|
statusCode: 200,
|
|
@@ -3683,7 +3627,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3683
3627
|
value: "OK"
|
|
3684
3628
|
};
|
|
3685
3629
|
} catch (err) {
|
|
3686
|
-
log.error("msteams card.action failed", { error: formatUnknownError(err) });
|
|
3630
|
+
log.error("msteams card.action failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3687
3631
|
return {
|
|
3688
3632
|
statusCode: 500,
|
|
3689
3633
|
type: "application/vnd.microsoft.error",
|
|
@@ -3751,7 +3695,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3751
3695
|
hasExpiry: Boolean(ctx.token.expiration)
|
|
3752
3696
|
});
|
|
3753
3697
|
})().catch((err) => {
|
|
3754
|
-
log.error("msteams sso token persistence failed", { error: formatUnknownError(err) });
|
|
3698
|
+
log.error("msteams sso token persistence failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3755
3699
|
});
|
|
3756
3700
|
});
|
|
3757
3701
|
app.on("message.submit", async (ctx) => {
|
|
@@ -3768,39 +3712,31 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3768
3712
|
}
|
|
3769
3713
|
await handler.run(adaptedCtx);
|
|
3770
3714
|
} catch (err) {
|
|
3771
|
-
log.error("msteams webhook failed", { error: formatUnknownError(err) });
|
|
3715
|
+
log.error("msteams webhook failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3772
3716
|
}
|
|
3773
3717
|
});
|
|
3774
3718
|
await app.initialize();
|
|
3775
|
-
const httpServer =
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
resolve();
|
|
3781
|
-
};
|
|
3782
|
-
const onError = (err) => {
|
|
3783
|
-
httpServer.off("listening", onListening);
|
|
3784
|
-
log.error("msteams server error", { error: formatUnknownError(err) });
|
|
3785
|
-
reject(toLintErrorObject(err, "MSTeams server failed"));
|
|
3786
|
-
};
|
|
3787
|
-
httpServer.once("listening", onListening);
|
|
3788
|
-
httpServer.once("error", onError);
|
|
3719
|
+
const httpServer = await new Promise((resolve, reject) => {
|
|
3720
|
+
const server = expressApp.listen(port, (err) => err ? reject(err) : resolve(server));
|
|
3721
|
+
}).catch((err) => {
|
|
3722
|
+
log.error("msteams server error", { error: require_setup_surface.formatUnknownError(err) });
|
|
3723
|
+
throw err;
|
|
3789
3724
|
});
|
|
3725
|
+
log.info(`msteams provider started on port ${port}`);
|
|
3790
3726
|
applyMSTeamsWebhookTimeouts(httpServer);
|
|
3791
3727
|
httpServer.on("error", (err) => {
|
|
3792
|
-
log.error("msteams server error", { error: formatUnknownError(err) });
|
|
3728
|
+
log.error("msteams server error", { error: require_setup_surface.formatUnknownError(err) });
|
|
3793
3729
|
});
|
|
3794
3730
|
const shutdown = async () => {
|
|
3795
3731
|
log.info("shutting down msteams provider");
|
|
3796
3732
|
return new Promise((resolve) => {
|
|
3797
3733
|
httpServer.close((err) => {
|
|
3798
|
-
if (err) log.debug?.("msteams server close error", { error: formatUnknownError(err) });
|
|
3734
|
+
if (err) log.debug?.("msteams server close error", { error: require_setup_surface.formatUnknownError(err) });
|
|
3799
3735
|
resolve();
|
|
3800
3736
|
});
|
|
3801
3737
|
});
|
|
3802
3738
|
};
|
|
3803
|
-
await keepHttpServerTaskAlive({
|
|
3739
|
+
await (0, openclaw_plugin_sdk_channel_outbound.keepHttpServerTaskAlive)({
|
|
3804
3740
|
server: httpServer,
|
|
3805
3741
|
abortSignal: opts.abortSignal,
|
|
3806
3742
|
onAbort: shutdown
|
|
@@ -3810,13 +3746,6 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3810
3746
|
shutdown
|
|
3811
3747
|
};
|
|
3812
3748
|
}
|
|
3813
|
-
function toLintErrorObject(value, fallbackMessage) {
|
|
3814
|
-
if (value instanceof Error) return value;
|
|
3815
|
-
if (typeof value === "string") return new Error(value);
|
|
3816
|
-
const error = new Error(fallbackMessage, { cause: value });
|
|
3817
|
-
if (typeof value === "object" && value !== null || typeof value === "function") Object.assign(error, value);
|
|
3818
|
-
return error;
|
|
3819
|
-
}
|
|
3820
3749
|
/**
|
|
3821
3750
|
* Build a minimal ActivityHandler-compatible object that supports
|
|
3822
3751
|
* onMessage / onMembersAdded registration and a run() method.
|
|
@@ -3866,7 +3795,9 @@ function adaptSdkContext(ctx, app) {
|
|
|
3866
3795
|
const sdkCtx = ctx ?? {};
|
|
3867
3796
|
if (typeof sdkCtx.reply !== "function" && typeof sdkCtx.send !== "function") return ctx;
|
|
3868
3797
|
const conversationId = sdkCtx.activity?.conversation?.id ?? "";
|
|
3869
|
-
const
|
|
3798
|
+
const inboundApi = sdkCtx.api;
|
|
3799
|
+
const activityApi = inboundApi ?? app.api;
|
|
3800
|
+
const getTeamDetails = inboundApi ? (teamId) => inboundApi.teams.getById(teamId) : void 0;
|
|
3870
3801
|
const conversationType = (sdkCtx.activity?.conversation?.conversationType ?? "").toLowerCase();
|
|
3871
3802
|
const isThreadable = conversationType === "channel" || conversationType === "groupchat";
|
|
3872
3803
|
const sendActivity = (activity) => isThreadable ? sdkCtx.reply(activity) : sdkCtx.send(activity);
|
|
@@ -3884,8 +3815,9 @@ function adaptSdkContext(ctx, app) {
|
|
|
3884
3815
|
deleteActivity: async (activityId) => {
|
|
3885
3816
|
return activityApi.conversations.activities(conversationId).delete(activityId);
|
|
3886
3817
|
},
|
|
3818
|
+
getTeamDetails,
|
|
3887
3819
|
stream: sdkCtx.stream
|
|
3888
3820
|
});
|
|
3889
3821
|
}
|
|
3890
3822
|
//#endregion
|
|
3891
|
-
|
|
3823
|
+
exports.monitorMSTeamsProvider = monitorMSTeamsProvider;
|