@openclaw/msteams 2026.7.1 → 2026.7.2-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.cjs +8 -0
- package/dist/{channel-Cf5LJ4aO.js → channel-BKc8zroK.cjs} +648 -240
- package/dist/channel-config-api.cjs +3 -0
- package/dist/channel-plugin-api.cjs +3 -0
- package/dist/channel.runtime-hd1Zsoo2.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-BVcKl6dv.cjs} +436 -891
- package/dist/{oauth-BifWxWzq.js → oauth-yNoKtU_3.cjs} +21 -21
- package/dist/{polls-B_hs4aKm.js → polls-BhoI4VaN.cjs} +188 -38
- package/dist/{resolve-allowlist-Dnw4aNOu.js → resolve-allowlist-DXd097EP.cjs} +849 -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-D4f6x5cx.cjs} +127 -61
- package/dist/{src-DZFs5XtZ.js → src-20YasZEp.cjs} +686 -748
- 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-DXd097EP.cjs");
|
|
5
|
+
const require_channel = require("./channel-BKc8zroK.cjs");
|
|
6
|
+
const require_setup_surface = require("./setup-surface-D4f6x5cx.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-BVcKl6dv.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;
|
|
@@ -1512,8 +1461,10 @@ function createTeamsReplyStreamController(params) {
|
|
|
1512
1461
|
let streamFailed = false;
|
|
1513
1462
|
let lastInformativeText = "";
|
|
1514
1463
|
let progressLines = [];
|
|
1464
|
+
let latestPlan;
|
|
1465
|
+
let latestPlanExplanation;
|
|
1515
1466
|
let pendingFinalPayload;
|
|
1516
|
-
let
|
|
1467
|
+
let emittedText = "";
|
|
1517
1468
|
const wasCanceled = () => canceledLocally || Boolean(stream?.canceled);
|
|
1518
1469
|
const fallbackPayloadForSuppressedFinal = (payload) => {
|
|
1519
1470
|
return Boolean(payload.mediaUrl || payload.mediaUrls?.length) ? {
|
|
@@ -1528,12 +1479,14 @@ function createTeamsReplyStreamController(params) {
|
|
|
1528
1479
|
* default) and prepends collected tool-progress lines when configured.
|
|
1529
1480
|
*/
|
|
1530
1481
|
const renderInformativeUpdate = () => {
|
|
1531
|
-
if (!stream || wasCanceled()) return;
|
|
1532
|
-
const informativeText = formatChannelProgressDraftText({
|
|
1482
|
+
if (!stream || wasCanceled() || streamFinalizationPending) return;
|
|
1483
|
+
const informativeText = (0, openclaw_plugin_sdk_channel_outbound.formatChannelProgressDraftText)({
|
|
1533
1484
|
entry: params.msteamsConfig,
|
|
1534
1485
|
lines: shouldStreamPreviewToolProgress ? progressLines : [],
|
|
1535
1486
|
seed: params.progressSeed,
|
|
1536
|
-
bullet: "-"
|
|
1487
|
+
bullet: "-",
|
|
1488
|
+
narration: latestPlanExplanation,
|
|
1489
|
+
plan: latestPlan
|
|
1537
1490
|
});
|
|
1538
1491
|
if (!informativeText || informativeText === lastInformativeText) return;
|
|
1539
1492
|
lastInformativeText = informativeText;
|
|
@@ -1547,17 +1500,26 @@ function createTeamsReplyStreamController(params) {
|
|
|
1547
1500
|
params.log?.debug?.(`stream informative update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1548
1501
|
}
|
|
1549
1502
|
};
|
|
1550
|
-
const progressDraftGate = createChannelProgressDraftGate({ onStart: renderInformativeUpdate });
|
|
1503
|
+
const progressDraftGate = (0, openclaw_plugin_sdk_channel_outbound.createChannelProgressDraftGate)({ onStart: renderInformativeUpdate });
|
|
1551
1504
|
return {
|
|
1552
1505
|
async onReplyStart() {},
|
|
1553
1506
|
onPartialReply(payload) {
|
|
1554
1507
|
if (!stream || !payload.text || wasCanceled() || streamMode !== "partial" || streamFinalizationPending) return;
|
|
1555
1508
|
const fullText = payload.text;
|
|
1556
|
-
|
|
1557
|
-
|
|
1509
|
+
let prefixLength = 0;
|
|
1510
|
+
while (prefixLength < emittedText.length && prefixLength < fullText.length && emittedText[prefixLength] === fullText[prefixLength]) prefixLength += 1;
|
|
1511
|
+
const previousRemainder = emittedText.slice(prefixLength);
|
|
1512
|
+
const delta = fullText.slice(prefixLength);
|
|
1513
|
+
if (!delta) return;
|
|
1514
|
+
if (previousRemainder.trim()) {
|
|
1515
|
+
stream.clearText();
|
|
1516
|
+
streamFailed = true;
|
|
1517
|
+
streamFinalizationPending = true;
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1558
1520
|
try {
|
|
1559
1521
|
stream.emit(delta);
|
|
1560
|
-
|
|
1522
|
+
emittedText = fullText;
|
|
1561
1523
|
tokensEmitted = true;
|
|
1562
1524
|
} catch (err) {
|
|
1563
1525
|
if (isStreamCancelledError(err)) {
|
|
@@ -1575,7 +1537,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
1575
1537
|
*/
|
|
1576
1538
|
async noteProgressWork(options) {
|
|
1577
1539
|
if (!stream || streamMode !== "progress") return;
|
|
1578
|
-
if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
|
|
1540
|
+
if (options?.toolName !== void 0 && !(0, openclaw_plugin_sdk_channel_outbound.isChannelProgressDraftWorkToolName)(options.toolName)) return;
|
|
1579
1541
|
const hadStarted = progressDraftGate.hasStarted;
|
|
1580
1542
|
const progressActive = await progressDraftGate.noteWork();
|
|
1581
1543
|
if ((hadStarted || progressActive) && progressDraftGate.hasStarted) renderInformativeUpdate();
|
|
@@ -1587,15 +1549,23 @@ function createTeamsReplyStreamController(params) {
|
|
|
1587
1549
|
*/
|
|
1588
1550
|
async pushProgressLine(line, options) {
|
|
1589
1551
|
if (!stream || streamMode !== "progress") return;
|
|
1590
|
-
if (options?.toolName !== void 0 && !isChannelProgressDraftWorkToolName(options.toolName)) return;
|
|
1552
|
+
if (options?.toolName !== void 0 && !(0, openclaw_plugin_sdk_channel_outbound.isChannelProgressDraftWorkToolName)(options.toolName)) return;
|
|
1591
1553
|
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) });
|
|
1554
|
+
const normalized = (0, openclaw_plugin_sdk_channel_outbound.normalizeChannelProgressDraftLineIdentity)(line);
|
|
1555
|
+
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
1556
|
}
|
|
1595
1557
|
const hadStarted = progressDraftGate.hasStarted;
|
|
1596
1558
|
const progressActive = await progressDraftGate.noteWork();
|
|
1597
1559
|
if ((hadStarted || progressActive) && progressDraftGate.hasStarted) renderInformativeUpdate();
|
|
1598
1560
|
},
|
|
1561
|
+
async pushPlanProgress(steps, options) {
|
|
1562
|
+
if (!stream || streamMode !== "progress" || streamFinalizationPending) return;
|
|
1563
|
+
latestPlan = steps?.length ? steps.map((entry) => ({ ...entry })) : void 0;
|
|
1564
|
+
latestPlanExplanation = options?.explanation?.replace(/\s+/g, " ").trim() || void 0;
|
|
1565
|
+
const hadStarted = progressDraftGate.hasStarted;
|
|
1566
|
+
await progressDraftGate.startNow();
|
|
1567
|
+
if (hadStarted && progressDraftGate.hasStarted) renderInformativeUpdate();
|
|
1568
|
+
},
|
|
1599
1569
|
preparePayload(payload) {
|
|
1600
1570
|
if (!stream) return payload;
|
|
1601
1571
|
if (wasCanceled()) return;
|
|
@@ -1627,6 +1597,7 @@ function createTeamsReplyStreamController(params) {
|
|
|
1627
1597
|
return payload;
|
|
1628
1598
|
},
|
|
1629
1599
|
async finalize() {
|
|
1600
|
+
progressDraftGate.cancel();
|
|
1630
1601
|
if (!stream || !streamFinalizationPending || wasCanceled()) return;
|
|
1631
1602
|
const finalEntities = [{
|
|
1632
1603
|
type: "https://schema.org/Message",
|
|
@@ -1678,9 +1649,9 @@ function createTeamsReplyStreamController(params) {
|
|
|
1678
1649
|
//#endregion
|
|
1679
1650
|
//#region extensions/msteams/src/reply-dispatcher.ts
|
|
1680
1651
|
function createMSTeamsReplyDispatcher(params) {
|
|
1681
|
-
const core = getMSTeamsRuntime();
|
|
1652
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
1682
1653
|
const msteamsCfg = params.cfg.channels?.msteams;
|
|
1683
|
-
const conversationType = normalizeOptionalLowercaseString(params.conversationRef.conversation?.conversationType);
|
|
1654
|
+
const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationRef.conversation?.conversationType);
|
|
1684
1655
|
const isTypingSupported = conversationType === "personal" || conversationType === "groupchat";
|
|
1685
1656
|
/**
|
|
1686
1657
|
* Keepalive cadence for the typing indicator while the bot is running
|
|
@@ -1701,13 +1672,13 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1701
1672
|
const streamActiveRef = { current: () => false };
|
|
1702
1673
|
const streamCanceledRef = { current: () => false };
|
|
1703
1674
|
const rawSendTypingIndicator = async () => {
|
|
1704
|
-
await withRevokedProxyFallback({
|
|
1675
|
+
await require_messenger.withRevokedProxyFallback({
|
|
1705
1676
|
run: async () => {
|
|
1706
1677
|
await params.context.sendActivity({ type: "typing" });
|
|
1707
1678
|
},
|
|
1708
1679
|
onRevoked: async () => {
|
|
1709
|
-
const baseRef = buildConversationReference(params.conversationRef);
|
|
1710
|
-
await sendMSTeamsActivityWithReference(params.app, baseRef, { type: "typing" }, { serviceUrlBoundary: resolveMSTeamsSdkCloudOptions(msteamsCfg) });
|
|
1680
|
+
const baseRef = require_messenger.buildConversationReference(params.conversationRef);
|
|
1681
|
+
await require_messenger.sendMSTeamsActivityWithReference(params.app, baseRef, { type: "typing" }, { serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg) });
|
|
1711
1682
|
},
|
|
1712
1683
|
onRevokedLog: () => {
|
|
1713
1684
|
params.log.debug?.("turn context revoked, sending typing via proactive messaging");
|
|
@@ -1719,7 +1690,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1719
1690
|
if (streamCanceledRef.current()) return;
|
|
1720
1691
|
await rawSendTypingIndicator();
|
|
1721
1692
|
} : async () => {};
|
|
1722
|
-
const { onModelSelected, typingCallbacks, ...replyPipeline } = createChannelMessageReplyPipeline({
|
|
1693
|
+
const { onModelSelected, typingCallbacks, ...replyPipeline } = (0, openclaw_plugin_sdk_channel_outbound.createChannelMessageReplyPipeline)({
|
|
1723
1694
|
cfg: params.cfg,
|
|
1724
1695
|
agentId: params.agentId,
|
|
1725
1696
|
channel: "msteams",
|
|
@@ -1729,7 +1700,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1729
1700
|
keepaliveIntervalMs: TYPING_KEEPALIVE_INTERVAL_MS,
|
|
1730
1701
|
maxDurationMs: TYPING_KEEPALIVE_MAX_DURATION_MS,
|
|
1731
1702
|
onStartError: (err) => {
|
|
1732
|
-
logTypingFailure({
|
|
1703
|
+
(0, openclaw_plugin_sdk_channel_outbound.logTypingFailure)({
|
|
1733
1704
|
log: (message) => params.log.debug?.(message),
|
|
1734
1705
|
channel: "msteams",
|
|
1735
1706
|
action: "start",
|
|
@@ -1743,7 +1714,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1743
1714
|
cfg: params.cfg,
|
|
1744
1715
|
channel: "msteams"
|
|
1745
1716
|
});
|
|
1746
|
-
const mediaMaxBytes = resolveChannelMediaMaxBytes({
|
|
1717
|
+
const mediaMaxBytes = (0, openclaw_plugin_sdk_media_runtime.resolveChannelMediaMaxBytes)({
|
|
1747
1718
|
cfg: params.cfg,
|
|
1748
1719
|
resolveChannelLimitMb: ({ cfg }) => cfg.channels?.msteams?.mediaMaxMb
|
|
1749
1720
|
});
|
|
@@ -1758,13 +1729,13 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1758
1729
|
});
|
|
1759
1730
|
streamActiveRef.current = () => streamController.isStreamActive();
|
|
1760
1731
|
streamCanceledRef.current = () => streamController.wasCanceled();
|
|
1761
|
-
const teamsStreamMode = resolveChannelPreviewStreamMode(msteamsCfg, "partial");
|
|
1762
|
-
const blockStreamingResolved = teamsStreamMode === "block" ? true : resolveChannelStreamingBlockEnabled(msteamsCfg);
|
|
1732
|
+
const teamsStreamMode = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelPreviewStreamMode)(msteamsCfg, "partial");
|
|
1733
|
+
const blockStreamingResolved = teamsStreamMode === "block" ? true : (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingBlockEnabled)(msteamsCfg);
|
|
1763
1734
|
const blockStreamingEnabled = blockStreamingResolved ?? false;
|
|
1764
1735
|
const typingIndicatorEnabled = typeof msteamsCfg?.typingIndicator === "boolean" ? msteamsCfg.typingIndicator : true;
|
|
1765
1736
|
const pendingMessages = [];
|
|
1766
1737
|
const sendMessages = async (messages) => {
|
|
1767
|
-
return sendMSTeamsMessages({
|
|
1738
|
+
return require_messenger.sendMSTeamsMessages({
|
|
1768
1739
|
replyStyle: params.replyStyle,
|
|
1769
1740
|
app: params.app,
|
|
1770
1741
|
appId: params.appId,
|
|
@@ -1782,12 +1753,12 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1782
1753
|
sharePointSiteId: params.sharePointSiteId,
|
|
1783
1754
|
mediaMaxBytes,
|
|
1784
1755
|
feedbackLoopEnabled,
|
|
1785
|
-
serviceUrlBoundary: resolveMSTeamsSdkCloudOptions(msteamsCfg)
|
|
1756
|
+
serviceUrlBoundary: require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg)
|
|
1786
1757
|
});
|
|
1787
1758
|
};
|
|
1788
1759
|
const queueDeliveryFailureSystemEvent = (failure) => {
|
|
1789
|
-
const classification = classifyMSTeamsSendError(failure.error);
|
|
1790
|
-
const errorText = formatUnknownError(failure.error);
|
|
1760
|
+
const classification = require_setup_surface.classifyMSTeamsSendError(failure.error);
|
|
1761
|
+
const errorText = require_setup_surface.formatUnknownError(failure.error);
|
|
1791
1762
|
const failedAll = failure.failed >= failure.total;
|
|
1792
1763
|
const sentences = [
|
|
1793
1764
|
`Microsoft Teams delivery failed: ${failedAll ? "the previous reply was not delivered" : `${failure.failed} of ${failure.total} message blocks were not delivered`}.`,
|
|
@@ -1802,7 +1773,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1802
1773
|
});
|
|
1803
1774
|
};
|
|
1804
1775
|
const queueReplyPayload = (payload) => {
|
|
1805
|
-
const messages = renderReplyPayloadsToMessages([payload], {
|
|
1776
|
+
const messages = require_messenger.renderReplyPayloadsToMessages([payload], {
|
|
1806
1777
|
textChunkLimit: params.textLimit,
|
|
1807
1778
|
chunkText: true,
|
|
1808
1779
|
mediaMode: "split",
|
|
@@ -1859,9 +1830,9 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1859
1830
|
if (blockStreamingEnabled) await flushPendingMessages();
|
|
1860
1831
|
},
|
|
1861
1832
|
onError: (err, info) => {
|
|
1862
|
-
const errMsg = formatUnknownError(err);
|
|
1863
|
-
const classification = classifyMSTeamsSendError(err);
|
|
1864
|
-
const hint = formatMSTeamsSendErrorHint(classification);
|
|
1833
|
+
const errMsg = require_setup_surface.formatUnknownError(err);
|
|
1834
|
+
const classification = require_setup_surface.classifyMSTeamsSendError(err);
|
|
1835
|
+
const hint = require_setup_surface.formatMSTeamsSendErrorHint(classification);
|
|
1865
1836
|
params.runtime.error?.(`msteams ${info.kind} reply failed: ${errMsg}${hint ? ` (${hint})` : ""}`);
|
|
1866
1837
|
params.log.error("reply failed", {
|
|
1867
1838
|
kind: info.kind,
|
|
@@ -1873,9 +1844,9 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1873
1844
|
});
|
|
1874
1845
|
const markDispatchIdle = () => {
|
|
1875
1846
|
return flushPendingMessages().catch((err) => {
|
|
1876
|
-
const errMsg = formatUnknownError(err);
|
|
1877
|
-
const classification = classifyMSTeamsSendError(err);
|
|
1878
|
-
const hint = formatMSTeamsSendErrorHint(classification);
|
|
1847
|
+
const errMsg = require_setup_surface.formatUnknownError(err);
|
|
1848
|
+
const classification = require_setup_surface.classifyMSTeamsSendError(err);
|
|
1849
|
+
const hint = require_setup_surface.formatMSTeamsSendErrorHint(classification);
|
|
1879
1850
|
params.runtime.error?.(`msteams flush reply failed: ${errMsg}${hint ? ` (${hint})` : ""}`);
|
|
1880
1851
|
params.log.error("flush reply failed", {
|
|
1881
1852
|
error: errMsg,
|
|
@@ -1884,7 +1855,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1884
1855
|
});
|
|
1885
1856
|
}).then(async () => {
|
|
1886
1857
|
const fallbackPayload = await streamController.finalize().catch((err) => {
|
|
1887
|
-
params.log.debug?.("stream finalize failed", { error: formatUnknownError(err) });
|
|
1858
|
+
params.log.debug?.("stream finalize failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
1888
1859
|
});
|
|
1889
1860
|
if (fallbackPayload) {
|
|
1890
1861
|
queueReplyPayload(fallbackPayload);
|
|
@@ -1894,8 +1865,8 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1894
1865
|
baseMarkDispatchIdle();
|
|
1895
1866
|
});
|
|
1896
1867
|
};
|
|
1897
|
-
const previewToolProgressEnabled = resolveChannelStreamingPreviewToolProgress(msteamsCfg);
|
|
1898
|
-
const suppressDefaultToolProgressMessages = resolveChannelStreamingSuppressDefaultToolProgressMessages(msteamsCfg);
|
|
1868
|
+
const previewToolProgressEnabled = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingPreviewToolProgress)(msteamsCfg);
|
|
1869
|
+
const suppressDefaultToolProgressMessages = (0, openclaw_plugin_sdk_channel_outbound.resolveChannelStreamingSuppressDefaultToolProgressMessages)(msteamsCfg);
|
|
1899
1870
|
const shouldSuppressDefaultToolProgressMessages = teamsStreamMode === "progress" && suppressDefaultToolProgressMessages && previewToolProgressEnabled;
|
|
1900
1871
|
const progressCallbacks = streamController.hasStream() ? {
|
|
1901
1872
|
onReasoningStream: async (payload) => {
|
|
@@ -1905,7 +1876,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1905
1876
|
await streamController.pushProgressLine(text);
|
|
1906
1877
|
return;
|
|
1907
1878
|
}
|
|
1908
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLine({
|
|
1879
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
|
|
1909
1880
|
event: "item",
|
|
1910
1881
|
itemId: "reasoning",
|
|
1911
1882
|
itemKind: "analysis",
|
|
@@ -1916,7 +1887,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1916
1887
|
onToolStart: async (payload) => {
|
|
1917
1888
|
const name = typeof payload?.name === "string" ? payload.name : void 0;
|
|
1918
1889
|
const detailMode = typeof payload?.detailMode === "string" ? payload.detailMode : void 0;
|
|
1919
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLineForEntry(msteamsCfg, {
|
|
1890
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
|
|
1920
1891
|
event: "tool",
|
|
1921
1892
|
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
1922
1893
|
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
@@ -1926,7 +1897,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1926
1897
|
}, detailMode === "explain" || detailMode === "raw" ? { detailMode } : void 0), name ? { toolName: name } : void 0);
|
|
1927
1898
|
},
|
|
1928
1899
|
onItemEvent: async (payload) => {
|
|
1929
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLineForEntry(msteamsCfg, {
|
|
1900
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLineForEntry)(msteamsCfg, {
|
|
1930
1901
|
event: "item",
|
|
1931
1902
|
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
1932
1903
|
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
@@ -1942,17 +1913,11 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1942
1913
|
},
|
|
1943
1914
|
onPlanUpdate: async (payload) => {
|
|
1944
1915
|
if (payload?.phase !== "update") return;
|
|
1945
|
-
await streamController.
|
|
1946
|
-
event: "plan",
|
|
1947
|
-
phase: payload.phase,
|
|
1948
|
-
...typeof payload?.title === "string" ? { title: payload.title } : {},
|
|
1949
|
-
...typeof payload?.explanation === "string" ? { explanation: payload.explanation } : {},
|
|
1950
|
-
...Array.isArray(payload?.steps) && payload.steps.every((s) => typeof s === "string") ? { steps: payload.steps } : {}
|
|
1951
|
-
}));
|
|
1916
|
+
await streamController.pushPlanProgress((0, openclaw_plugin_sdk_channel_outbound.normalizeAgentPlanSteps)(payload.planSteps), { explanation: typeof payload.explanation === "string" ? payload.explanation : void 0 });
|
|
1952
1917
|
},
|
|
1953
1918
|
onApprovalEvent: async (payload) => {
|
|
1954
1919
|
if (payload?.phase !== "requested") return;
|
|
1955
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLine({
|
|
1920
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
|
|
1956
1921
|
event: "approval",
|
|
1957
1922
|
phase: payload.phase,
|
|
1958
1923
|
...typeof payload?.title === "string" ? { title: payload.title } : {},
|
|
@@ -1963,7 +1928,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1963
1928
|
},
|
|
1964
1929
|
onCommandOutput: async (payload) => {
|
|
1965
1930
|
if (payload?.phase !== "end") return;
|
|
1966
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLine({
|
|
1931
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
|
|
1967
1932
|
event: "command-output",
|
|
1968
1933
|
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
1969
1934
|
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
@@ -1976,7 +1941,7 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
1976
1941
|
},
|
|
1977
1942
|
onPatchSummary: async (payload) => {
|
|
1978
1943
|
if (payload?.phase !== "end") return;
|
|
1979
|
-
await streamController.pushProgressLine(buildChannelProgressDraftLine({
|
|
1944
|
+
await streamController.pushProgressLine((0, openclaw_plugin_sdk_channel_outbound.buildChannelProgressDraftLine)({
|
|
1980
1945
|
event: "patch",
|
|
1981
1946
|
...typeof payload?.itemId === "string" ? { itemId: payload.itemId } : {},
|
|
1982
1947
|
...typeof payload?.toolCallId === "string" ? { toolCallId: payload.toolCallId } : {},
|
|
@@ -2005,17 +1970,17 @@ function createMSTeamsReplyDispatcher(params) {
|
|
|
2005
1970
|
}
|
|
2006
1971
|
//#endregion
|
|
2007
1972
|
//#region extensions/msteams/src/sent-message-cache.ts
|
|
2008
|
-
const sentMessages = createPersistentDedupeCache({
|
|
1973
|
+
const sentMessages = (0, openclaw_plugin_sdk_dedupe_runtime.createPersistentDedupeCache)({
|
|
2009
1974
|
globalKey: Symbol.for("openclaw.msteamsSentMessages"),
|
|
2010
1975
|
ttlMs: 1440 * 60 * 1e3,
|
|
2011
1976
|
maxSize: 2e4,
|
|
2012
1977
|
persistent: {
|
|
2013
1978
|
namespace: "msteams.sent-messages",
|
|
2014
1979
|
maxEntries: 1e3,
|
|
2015
|
-
openStore: (options) => getOptionalMSTeamsRuntime()?.state.openKeyedStore(options),
|
|
1980
|
+
openStore: (options) => require_runtime.getOptionalMSTeamsRuntime()?.state.openKeyedStore(options),
|
|
2016
1981
|
logError: (error) => {
|
|
2017
1982
|
try {
|
|
2018
|
-
getOptionalMSTeamsRuntime()?.logging.getChildLogger({
|
|
1983
|
+
require_runtime.getOptionalMSTeamsRuntime()?.logging.getChildLogger({
|
|
2019
1984
|
plugin: "msteams",
|
|
2020
1985
|
feature: "sent-message-state"
|
|
2021
1986
|
}).warn("Microsoft Teams persistent sent-message state failed", { error: String(error) });
|
|
@@ -2037,18 +2002,50 @@ async function wasMSTeamsMessageSentWithPersistence(params) {
|
|
|
2037
2002
|
return await sentMessages.lookup(makeKey(params.conversationId, params.messageId));
|
|
2038
2003
|
}
|
|
2039
2004
|
//#endregion
|
|
2005
|
+
//#region extensions/msteams/src/team-identity.ts
|
|
2006
|
+
const teamGroupIdCache = /* @__PURE__ */ new Map();
|
|
2007
|
+
const TEAM_GROUP_ID_CACHE_MAX_ENTRIES = 500;
|
|
2008
|
+
function cacheTeamGroupId(conversationTeamId, groupId) {
|
|
2009
|
+
teamGroupIdCache.set(conversationTeamId, groupId);
|
|
2010
|
+
(0, openclaw_plugin_sdk_collection_runtime.pruneMapToMaxSize)(teamGroupIdCache, TEAM_GROUP_ID_CACHE_MAX_ENTRIES);
|
|
2011
|
+
}
|
|
2012
|
+
/** Resolve the Graph team GUID without ever treating a Bot Framework team ID as equivalent. */
|
|
2013
|
+
async function resolveTeamGroupId(params) {
|
|
2014
|
+
const activityGroupId = params.aadGroupId?.trim();
|
|
2015
|
+
if (activityGroupId) {
|
|
2016
|
+
cacheTeamGroupId(params.conversationTeamId, activityGroupId);
|
|
2017
|
+
return activityGroupId;
|
|
2018
|
+
}
|
|
2019
|
+
const cached = teamGroupIdCache.get(params.conversationTeamId);
|
|
2020
|
+
if (cached) return cached;
|
|
2021
|
+
const getTeamDetails = params.getTeamDetails;
|
|
2022
|
+
if (!getTeamDetails) return;
|
|
2023
|
+
const groupId = (await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2024
|
+
deadline: params.deadline,
|
|
2025
|
+
label: "MS Teams team details",
|
|
2026
|
+
work: () => getTeamDetails(params.conversationTeamId)
|
|
2027
|
+
})).aadGroupId?.trim();
|
|
2028
|
+
if (!groupId) return;
|
|
2029
|
+
cacheTeamGroupId(params.conversationTeamId, groupId);
|
|
2030
|
+
return groupId;
|
|
2031
|
+
}
|
|
2032
|
+
//#endregion
|
|
2040
2033
|
//#region extensions/msteams/src/monitor-handler/inbound-media.ts
|
|
2034
|
+
function shouldAttemptMSTeamsGraphMediaFallback(params) {
|
|
2035
|
+
const conversationType = params.conversationType.trim().toLowerCase();
|
|
2036
|
+
return params.graphMediaFallback === true && (conversationType === "channel" || conversationType === "groupchat") && (params.htmlSummary?.htmlAttachments ?? 0) > 0;
|
|
2037
|
+
}
|
|
2041
2038
|
function resolveMSTeamsInboundMediaBody(params) {
|
|
2042
2039
|
const unavailableCount = Math.max(0, params.expectedMediaCount - params.mediaCount);
|
|
2043
2040
|
if (unavailableCount === 0) return params.body;
|
|
2044
|
-
return formatInboundMediaUnavailableText({
|
|
2041
|
+
return (0, openclaw_plugin_sdk_channel_inbound.formatInboundMediaUnavailableText)({
|
|
2045
2042
|
body: params.mediaCount > 0 && params.body === params.mediaPlaceholder ? params.materializedMediaPlaceholder : params.body,
|
|
2046
2043
|
mediaPlaceholder: params.mediaCount === 0 ? params.mediaPlaceholder : void 0,
|
|
2047
2044
|
notice: `[msteams ${unavailableCount > 1 ? `${unavailableCount} attachments` : "attachment"} unavailable]`
|
|
2048
2045
|
});
|
|
2049
2046
|
}
|
|
2050
2047
|
async function resolveMSTeamsInboundMedia(params) {
|
|
2051
|
-
const { attachments, htmlSummary, maxBytes, tokenProvider, allowHosts, conversationType, conversationId, conversationMessageId, serviceUrl, activity, log, preserveFilenames } = params;
|
|
2048
|
+
const { attachments, htmlSummary, maxBytes, tokenProvider, allowHosts, conversationType, conversationId, conversationMessageId, teamAadGroupId, serviceUrl, activity, log, preserveFilenames } = params;
|
|
2052
2049
|
let mediaList = await downloadMSTeamsAttachments({
|
|
2053
2050
|
attachments,
|
|
2054
2051
|
maxBytes,
|
|
@@ -2056,12 +2053,20 @@ async function resolveMSTeamsInboundMedia(params) {
|
|
|
2056
2053
|
allowHosts,
|
|
2057
2054
|
authAllowHosts: params.authAllowHosts,
|
|
2058
2055
|
preserveFilenames,
|
|
2056
|
+
deadline: params.deadline,
|
|
2059
2057
|
logger: log
|
|
2060
2058
|
});
|
|
2061
2059
|
if (mediaList.length === 0) {
|
|
2062
2060
|
const attachmentIds = extractMSTeamsHtmlAttachmentIds(attachments);
|
|
2063
2061
|
const hasHtmlFileAttachment = attachmentIds.length > 0;
|
|
2064
|
-
|
|
2062
|
+
const hasChannelOrGroupHtml = shouldAttemptMSTeamsGraphMediaFallback({
|
|
2063
|
+
conversationType,
|
|
2064
|
+
htmlSummary,
|
|
2065
|
+
graphMediaFallback: params.graphMediaFallback
|
|
2066
|
+
});
|
|
2067
|
+
const shouldFetchGraphMessage = hasHtmlFileAttachment || hasChannelOrGroupHtml;
|
|
2068
|
+
const isBotFrameworkPersonalChat = isBotFrameworkPersonalChatId(conversationId);
|
|
2069
|
+
if (hasHtmlFileAttachment && isBotFrameworkPersonalChat) if (!serviceUrl) log.debug?.("bot framework attachment skipped (missing serviceUrl)", {
|
|
2065
2070
|
conversationType,
|
|
2066
2071
|
conversationId
|
|
2067
2072
|
});
|
|
@@ -2073,7 +2078,8 @@ async function resolveMSTeamsInboundMedia(params) {
|
|
|
2073
2078
|
maxBytes,
|
|
2074
2079
|
allowHosts,
|
|
2075
2080
|
authAllowHosts: params.authAllowHosts,
|
|
2076
|
-
preserveFilenames
|
|
2081
|
+
preserveFilenames,
|
|
2082
|
+
deadline: params.deadline
|
|
2077
2083
|
});
|
|
2078
2084
|
if (bfMedia.media.length > 0) mediaList = bfMedia.media;
|
|
2079
2085
|
else log.debug?.("bot framework attachments fetch empty", {
|
|
@@ -2081,50 +2087,41 @@ async function resolveMSTeamsInboundMedia(params) {
|
|
|
2081
2087
|
attachmentCount: bfMedia.attachmentCount ?? attachmentIds.length
|
|
2082
2088
|
});
|
|
2083
2089
|
}
|
|
2084
|
-
if (
|
|
2085
|
-
const
|
|
2090
|
+
if (shouldFetchGraphMessage && mediaList.length === 0 && !isBotFrameworkPersonalChat) {
|
|
2091
|
+
const graphTeamAadGroupId = conversationType.trim().toLowerCase() === "channel" && !teamAadGroupId ? await params.resolveTeamAadGroupId?.() : teamAadGroupId;
|
|
2092
|
+
const messageUrl = buildMSTeamsGraphMessageUrl({
|
|
2086
2093
|
conversationType,
|
|
2087
2094
|
conversationId,
|
|
2088
2095
|
messageId: activity.id ?? void 0,
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2096
|
+
threadRootMessageId: conversationMessageId ?? activity.replyToId,
|
|
2097
|
+
teamAadGroupId: graphTeamAadGroupId,
|
|
2098
|
+
channelId: activity.channelData?.channel?.id
|
|
2092
2099
|
});
|
|
2093
|
-
if (
|
|
2100
|
+
if (!messageUrl) log.debug?.("graph message url unavailable", {
|
|
2094
2101
|
conversationType,
|
|
2095
2102
|
hasChannelData: Boolean(activity.channelData),
|
|
2096
2103
|
messageId: activity.id ?? void 0,
|
|
2097
2104
|
replyToId: activity.replyToId ?? void 0
|
|
2098
2105
|
});
|
|
2099
2106
|
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
|
-
}
|
|
2107
|
+
const graphMedia = await downloadMSTeamsGraphMedia({
|
|
2108
|
+
messageUrl,
|
|
2109
|
+
tokenProvider,
|
|
2110
|
+
maxBytes,
|
|
2111
|
+
allowHosts,
|
|
2112
|
+
authAllowHosts: params.authAllowHosts,
|
|
2113
|
+
preserveFilenames,
|
|
2114
|
+
deadline: params.deadline,
|
|
2115
|
+
logger: log
|
|
2116
|
+
});
|
|
2117
|
+
if (graphMedia.media.length > 0) mediaList = graphMedia.media;
|
|
2126
2118
|
if (mediaList.length === 0) log.debug?.("graph media fetch empty", {
|
|
2127
|
-
|
|
2119
|
+
messageUrl,
|
|
2120
|
+
hostedStatus: graphMedia.hostedStatus,
|
|
2121
|
+
attachmentStatus: graphMedia.attachmentStatus,
|
|
2122
|
+
hostedCount: graphMedia.hostedCount,
|
|
2123
|
+
attachmentCount: graphMedia.attachmentCount,
|
|
2124
|
+
tokenError: graphMedia.tokenError,
|
|
2128
2125
|
attachmentIdCount: attachmentIds.length
|
|
2129
2126
|
});
|
|
2130
2127
|
}
|
|
@@ -2143,9 +2140,7 @@ async function resolveMSTeamsInboundMedia(params) {
|
|
|
2143
2140
|
//#region extensions/msteams/src/monitor-handler/message-handler.ts
|
|
2144
2141
|
function extractTextFromHtmlAttachments(attachments) {
|
|
2145
2142
|
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 : "";
|
|
2143
|
+
const raw = require_resolve_allowlist.extractHtmlFromAttachment(attachment);
|
|
2149
2144
|
if (!raw) continue;
|
|
2150
2145
|
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
2146
|
if (text) return text;
|
|
@@ -2176,7 +2171,7 @@ function buildStoredConversationReference(params) {
|
|
|
2176
2171
|
const clientInfo = activity.entities?.find((e) => e.type === "clientInfo");
|
|
2177
2172
|
const tenantId = activity.channelData?.tenant?.id ?? conversation?.tenantId;
|
|
2178
2173
|
const aadObjectId = from?.aadObjectId;
|
|
2179
|
-
const serviceUrl = tryNormalizeBotFrameworkServiceUrl(activity.serviceUrl);
|
|
2174
|
+
const serviceUrl = require_resolve_allowlist.tryNormalizeBotFrameworkServiceUrl(activity.serviceUrl);
|
|
2180
2175
|
return {
|
|
2181
2176
|
activityId: activity.id,
|
|
2182
2177
|
user: from ? {
|
|
@@ -2185,10 +2180,6 @@ function buildStoredConversationReference(params) {
|
|
|
2185
2180
|
aadObjectId: from.aadObjectId
|
|
2186
2181
|
} : void 0,
|
|
2187
2182
|
agent,
|
|
2188
|
-
bot: agent ? {
|
|
2189
|
-
id: agent.id,
|
|
2190
|
-
name: agent.name
|
|
2191
|
-
} : void 0,
|
|
2192
2183
|
conversation: {
|
|
2193
2184
|
id: conversationId,
|
|
2194
2185
|
conversationType,
|
|
@@ -2206,16 +2197,16 @@ function buildStoredConversationReference(params) {
|
|
|
2206
2197
|
}
|
|
2207
2198
|
function createMSTeamsMessageHandler(deps) {
|
|
2208
2199
|
const { cfg, runtime, appId, app, tokenProvider, textLimit, mediaMaxBytes, conversationStore, pollStore, log } = deps;
|
|
2209
|
-
const core = getMSTeamsRuntime();
|
|
2200
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
2210
2201
|
const logVerboseMessage = (message) => {
|
|
2211
2202
|
if (core.logging.shouldLogVerbose()) log.debug?.(message);
|
|
2212
2203
|
};
|
|
2213
2204
|
const msteamsCfg = cfg.channels?.msteams;
|
|
2214
|
-
const contextVisibilityMode = resolveChannelContextVisibilityMode({
|
|
2205
|
+
const contextVisibilityMode = (0, openclaw_plugin_sdk_context_visibility_runtime.resolveChannelContextVisibilityMode)({
|
|
2215
2206
|
cfg,
|
|
2216
2207
|
channel: "msteams"
|
|
2217
2208
|
});
|
|
2218
|
-
const historyLimit = Math.max(0, msteamsCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? DEFAULT_GROUP_HISTORY_LIMIT);
|
|
2209
|
+
const historyLimit = Math.max(0, msteamsCfg?.historyLimit ?? cfg.messages?.groupChat?.historyLimit ?? openclaw_plugin_sdk_reply_history.DEFAULT_GROUP_HISTORY_LIMIT);
|
|
2219
2210
|
const conversationHistories = /* @__PURE__ */ new Map();
|
|
2220
2211
|
const inboundDebounceMs = core.channel.debounce.resolveInboundDebounceMs({
|
|
2221
2212
|
cfg,
|
|
@@ -2233,7 +2224,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2233
2224
|
});
|
|
2234
2225
|
const attachmentPlaceholder = attachmentPresentation.placeholder;
|
|
2235
2226
|
const rawBody = text || attachmentPlaceholder;
|
|
2236
|
-
const quoteInfo = extractMSTeamsQuoteInfo(attachments);
|
|
2227
|
+
const quoteInfo = require_channel.extractMSTeamsQuoteInfo(attachments);
|
|
2237
2228
|
let quoteSenderId;
|
|
2238
2229
|
let quoteSenderName;
|
|
2239
2230
|
const from = activity.from;
|
|
@@ -2241,8 +2232,8 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2241
2232
|
const attachmentTypes = attachments.map((att) => typeof att.contentType === "string" ? att.contentType : void 0).filter(Boolean).slice(0, 3);
|
|
2242
2233
|
const htmlSummary = summarizeMSTeamsHtmlAttachments(attachments);
|
|
2243
2234
|
log.info("received message", {
|
|
2244
|
-
rawText: truncateUtf16Safe(rawText, 50),
|
|
2245
|
-
text: truncateUtf16Safe(text, 50),
|
|
2235
|
+
rawText: (0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(rawText, 50),
|
|
2236
|
+
text: (0, openclaw_plugin_sdk_text_utility_runtime.truncateUtf16Safe)(text, 50),
|
|
2246
2237
|
attachments: attachments.length,
|
|
2247
2238
|
attachmentTypes,
|
|
2248
2239
|
from: from?.id,
|
|
@@ -2254,10 +2245,11 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2254
2245
|
return;
|
|
2255
2246
|
}
|
|
2256
2247
|
const rawConversationId = conversation?.id ?? "";
|
|
2257
|
-
const conversationId = normalizeMSTeamsConversationId(rawConversationId);
|
|
2258
|
-
const conversationMessageId = extractMSTeamsConversationMessageId(rawConversationId);
|
|
2248
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(rawConversationId);
|
|
2249
|
+
const conversationMessageId = require_channel.extractMSTeamsConversationMessageId(rawConversationId);
|
|
2259
2250
|
const conversationType = conversation?.conversationType ?? "personal";
|
|
2260
2251
|
const teamId = activity.channelData?.team?.id;
|
|
2252
|
+
const graphChannelId = activity.channelData?.channel?.id?.trim() || conversationId;
|
|
2261
2253
|
const conversationRef = buildStoredConversationReference({
|
|
2262
2254
|
activity,
|
|
2263
2255
|
conversationId,
|
|
@@ -2288,7 +2280,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2288
2280
|
log.debug?.("dropping dm (dms disabled)");
|
|
2289
2281
|
return;
|
|
2290
2282
|
}
|
|
2291
|
-
const allowMatch = resolveMSTeamsAllowlistMatch({
|
|
2283
|
+
const allowMatch = require_channel.resolveMSTeamsAllowlistMatch({
|
|
2292
2284
|
allowFrom: effectiveDmAllowFrom,
|
|
2293
2285
|
senderId,
|
|
2294
2286
|
senderName,
|
|
@@ -2296,7 +2288,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2296
2288
|
});
|
|
2297
2289
|
if (senderAccess.decision === "pairing") {
|
|
2298
2290
|
conversationStore.upsert(conversationId, conversationRef).catch((err) => {
|
|
2299
|
-
log.debug?.("failed to save conversation reference", { error: formatUnknownError(err) });
|
|
2291
|
+
log.debug?.("failed to save conversation reference", { error: require_setup_surface.formatUnknownError(err) });
|
|
2300
2292
|
});
|
|
2301
2293
|
if (await pairing.upsertPairingRequest({
|
|
2302
2294
|
id: senderId,
|
|
@@ -2309,7 +2301,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2309
2301
|
log.debug?.("dropping dm (not allowlisted)", {
|
|
2310
2302
|
sender: senderId,
|
|
2311
2303
|
label: senderName,
|
|
2312
|
-
allowlistMatch: formatAllowlistMatchMeta(allowMatch)
|
|
2304
|
+
allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
|
|
2313
2305
|
});
|
|
2314
2306
|
log.info("dropping dm (not allowlisted)", {
|
|
2315
2307
|
sender: senderId,
|
|
@@ -2320,7 +2312,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2320
2312
|
dmPolicy,
|
|
2321
2313
|
groupPolicy
|
|
2322
2314
|
}),
|
|
2323
|
-
allowlistMatch: formatAllowlistMatchMeta(allowMatch)
|
|
2315
|
+
allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
|
|
2324
2316
|
});
|
|
2325
2317
|
return;
|
|
2326
2318
|
}
|
|
@@ -2353,7 +2345,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2353
2345
|
return;
|
|
2354
2346
|
}
|
|
2355
2347
|
if (!senderAccess.allowed && senderAccess.reasonCode === "group_policy_not_allowlisted") {
|
|
2356
|
-
const allowMatch = resolveMSTeamsAllowlistMatch({
|
|
2348
|
+
const allowMatch = require_channel.resolveMSTeamsAllowlistMatch({
|
|
2357
2349
|
allowFrom: effectiveGroupAllowFrom,
|
|
2358
2350
|
senderId,
|
|
2359
2351
|
senderName,
|
|
@@ -2362,18 +2354,18 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2362
2354
|
log.debug?.("dropping group message (not in groupAllowFrom)", {
|
|
2363
2355
|
sender: senderId,
|
|
2364
2356
|
label: senderName,
|
|
2365
|
-
allowlistMatch: formatAllowlistMatchMeta(allowMatch)
|
|
2357
|
+
allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
|
|
2366
2358
|
});
|
|
2367
2359
|
log.info("dropping group message (not in groupAllowFrom)", {
|
|
2368
2360
|
sender: senderId,
|
|
2369
2361
|
label: senderName,
|
|
2370
|
-
allowlistMatch: formatAllowlistMatchMeta(allowMatch)
|
|
2362
|
+
allowlistMatch: (0, openclaw_plugin_sdk_allow_from.formatAllowlistMatchMeta)(allowMatch)
|
|
2371
2363
|
});
|
|
2372
2364
|
return;
|
|
2373
2365
|
}
|
|
2374
2366
|
}
|
|
2375
2367
|
if (commandAccess.shouldBlockControlCommand) {
|
|
2376
|
-
logInboundDrop({
|
|
2368
|
+
(0, openclaw_plugin_sdk_channel_inbound.logInboundDrop)({
|
|
2377
2369
|
log: logVerboseMessage,
|
|
2378
2370
|
channel: "msteams",
|
|
2379
2371
|
reason: "control command (unauthorized)",
|
|
@@ -2382,9 +2374,9 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2382
2374
|
return;
|
|
2383
2375
|
}
|
|
2384
2376
|
conversationStore.upsert(conversationId, conversationRef).catch((err) => {
|
|
2385
|
-
log.debug?.("failed to save conversation reference", { error: formatUnknownError(err) });
|
|
2377
|
+
log.debug?.("failed to save conversation reference", { error: require_setup_surface.formatUnknownError(err) });
|
|
2386
2378
|
});
|
|
2387
|
-
const pollVote = extractMSTeamsPollVote(activity);
|
|
2379
|
+
const pollVote = require_polls.extractMSTeamsPollVote(activity);
|
|
2388
2380
|
if (pollVote) {
|
|
2389
2381
|
try {
|
|
2390
2382
|
if (!await pollStore.recordVote({
|
|
@@ -2400,12 +2392,17 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2400
2392
|
} catch (err) {
|
|
2401
2393
|
log.error("failed to record poll vote", {
|
|
2402
2394
|
pollId: pollVote.pollId,
|
|
2403
|
-
error: formatUnknownError(err)
|
|
2395
|
+
error: require_setup_surface.formatUnknownError(err)
|
|
2404
2396
|
});
|
|
2405
2397
|
}
|
|
2406
2398
|
return;
|
|
2407
2399
|
}
|
|
2408
|
-
|
|
2400
|
+
const mayRecoverGraphMedia = Boolean(htmlSummary?.attachmentIds.length) || shouldAttemptMSTeamsGraphMediaFallback({
|
|
2401
|
+
conversationType,
|
|
2402
|
+
htmlSummary: htmlSummary ?? void 0,
|
|
2403
|
+
graphMediaFallback: msteamsCfg?.graphMediaFallback
|
|
2404
|
+
});
|
|
2405
|
+
if (!rawBody && !mayRecoverGraphMedia) {
|
|
2409
2406
|
log.debug?.("skipping empty message after stripping mentions");
|
|
2410
2407
|
return;
|
|
2411
2408
|
}
|
|
@@ -2420,13 +2417,13 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2420
2417
|
id: isDirectMessage ? senderId : conversationId
|
|
2421
2418
|
}
|
|
2422
2419
|
});
|
|
2423
|
-
route.sessionKey = resolveMSTeamsRouteSessionKey({
|
|
2420
|
+
route.sessionKey = require_channel.resolveMSTeamsRouteSessionKey({
|
|
2424
2421
|
baseSessionKey: route.sessionKey,
|
|
2425
2422
|
isChannel,
|
|
2426
2423
|
conversationMessageId,
|
|
2427
2424
|
replyToId: activity.replyToId
|
|
2428
2425
|
});
|
|
2429
|
-
const preview = sliceUtf16Safe(rawBody.replace(/\s+/g, " "), 0, 160);
|
|
2426
|
+
const preview = (0, openclaw_plugin_sdk_text_utility_runtime.sliceUtf16Safe)(rawBody.replace(/\s+/g, " "), 0, 160);
|
|
2430
2427
|
const inboundLabel = isDirectMessage ? `Teams DM from ${senderName}` : `Teams message in ${conversationType} from ${senderName}`;
|
|
2431
2428
|
const enqueuePrimaryMessageSystemEvent = () => core.system.enqueueSystemEvent(inboundLabel, {
|
|
2432
2429
|
sessionKey: route.sessionKey,
|
|
@@ -2434,14 +2431,14 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2434
2431
|
});
|
|
2435
2432
|
const channelId = conversationId;
|
|
2436
2433
|
const { teamConfig, channelConfig } = channelGate;
|
|
2437
|
-
const { requireMention, replyStyle } = resolveMSTeamsReplyPolicy({
|
|
2434
|
+
const { requireMention, replyStyle } = require_channel.resolveMSTeamsReplyPolicy({
|
|
2438
2435
|
isDirectMessage,
|
|
2439
2436
|
globalConfig: msteamsCfg,
|
|
2440
2437
|
teamConfig,
|
|
2441
2438
|
channelConfig
|
|
2442
2439
|
});
|
|
2443
|
-
const timestamp = parseMSTeamsActivityTimestamp(activity.timestamp);
|
|
2444
|
-
const mentionDecision = resolveInboundMentionDecision({
|
|
2440
|
+
const timestamp = require_channel.parseMSTeamsActivityTimestamp(activity.timestamp);
|
|
2441
|
+
const mentionDecision = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundMentionDecision)({
|
|
2445
2442
|
facts: {
|
|
2446
2443
|
canDetectMention: true,
|
|
2447
2444
|
wasMentioned: params.wasMentioned,
|
|
@@ -2464,124 +2461,163 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2464
2461
|
requireMention,
|
|
2465
2462
|
mentioned
|
|
2466
2463
|
});
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2464
|
+
if (rawBody) {
|
|
2465
|
+
enqueuePrimaryMessageSystemEvent();
|
|
2466
|
+
(0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).record({
|
|
2467
|
+
historyKey: conversationId,
|
|
2468
|
+
limit: historyLimit,
|
|
2469
|
+
entry: {
|
|
2470
|
+
sender: senderName,
|
|
2471
|
+
body: rawBody,
|
|
2472
|
+
timestamp: timestamp?.getTime(),
|
|
2473
|
+
messageId: activity.id ?? void 0
|
|
2474
|
+
}
|
|
2475
|
+
});
|
|
2476
|
+
}
|
|
2478
2477
|
return;
|
|
2479
2478
|
}
|
|
2480
2479
|
}
|
|
2481
|
-
|
|
2482
|
-
let
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2480
|
+
const preprocessingDeadline = require_resolve_allowlist.createMSTeamsInboundDeadline();
|
|
2481
|
+
let teamAadGroupId = activity.channelData?.team?.aadGroupId?.trim() || void 0;
|
|
2482
|
+
const conversationTeamId = isChannel ? teamId : void 0;
|
|
2483
|
+
let teamGroupIdPromise;
|
|
2484
|
+
const resolveChannelTeamGroupId = async () => {
|
|
2485
|
+
if (!conversationTeamId) return;
|
|
2486
|
+
teamGroupIdPromise ??= resolveTeamGroupId({
|
|
2487
|
+
conversationTeamId,
|
|
2488
|
+
aadGroupId: teamAadGroupId,
|
|
2489
|
+
getTeamDetails: context.getTeamDetails,
|
|
2490
|
+
deadline: preprocessingDeadline
|
|
2491
|
+
}).catch((err) => {
|
|
2492
|
+
log.debug?.("failed to resolve Teams AAD group ID", {
|
|
2493
|
+
teamId: conversationTeamId,
|
|
2494
|
+
error: require_setup_surface.formatUnknownError(err)
|
|
2496
2495
|
});
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2496
|
+
});
|
|
2497
|
+
teamAadGroupId = await teamGroupIdPromise;
|
|
2498
|
+
return teamAadGroupId;
|
|
2499
|
+
};
|
|
2500
|
+
let mediaList = [];
|
|
2501
|
+
try {
|
|
2502
|
+
mediaList = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2503
|
+
deadline: preprocessingDeadline,
|
|
2504
|
+
label: "MS Teams inbound media",
|
|
2505
|
+
work: () => resolveMSTeamsInboundMedia({
|
|
2506
|
+
attachments,
|
|
2507
|
+
htmlSummary: htmlSummary ?? void 0,
|
|
2508
|
+
maxBytes: mediaMaxBytes,
|
|
2509
|
+
tokenProvider,
|
|
2510
|
+
allowHosts: msteamsCfg?.mediaAllowHosts,
|
|
2511
|
+
authAllowHosts: msteamsCfg?.mediaAuthAllowHosts,
|
|
2512
|
+
graphMediaFallback: msteamsCfg?.graphMediaFallback,
|
|
2513
|
+
conversationType,
|
|
2514
|
+
conversationId,
|
|
2515
|
+
conversationMessageId: conversationMessageId ?? void 0,
|
|
2516
|
+
teamAadGroupId,
|
|
2517
|
+
resolveTeamAadGroupId: resolveChannelTeamGroupId,
|
|
2518
|
+
serviceUrl: activity.serviceUrl,
|
|
2519
|
+
activity: {
|
|
2520
|
+
id: activity.id,
|
|
2521
|
+
replyToId: activity.replyToId,
|
|
2522
|
+
channelData: activity.channelData
|
|
2523
|
+
},
|
|
2524
|
+
log,
|
|
2525
|
+
deadline: preprocessingDeadline,
|
|
2526
|
+
preserveFilenames: cfg.media?.preserveFilenames
|
|
2527
|
+
})
|
|
2528
|
+
});
|
|
2529
|
+
} catch (err) {
|
|
2530
|
+
log.debug?.("failed to resolve inbound Teams media", { error: require_setup_surface.formatUnknownError(err) });
|
|
2507
2531
|
}
|
|
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
2532
|
const mediaPayload = buildMSTeamsMediaPayload(mediaList);
|
|
2528
2533
|
const materializedMediaPlaceholder = resolveMSTeamsInboundAttachmentPresentation(mediaList.map((media) => ({
|
|
2529
2534
|
contentType: media.contentType,
|
|
2530
2535
|
name: media.path
|
|
2531
2536
|
}))).placeholder;
|
|
2532
2537
|
const agentBody = resolveMSTeamsInboundMediaBody({
|
|
2533
|
-
body: rawBody,
|
|
2538
|
+
body: rawBody || materializedMediaPlaceholder,
|
|
2534
2539
|
mediaPlaceholder: attachmentPlaceholder,
|
|
2535
2540
|
materializedMediaPlaceholder,
|
|
2536
2541
|
expectedMediaCount: attachmentPresentation.expectedMediaCount,
|
|
2537
2542
|
mediaCount: mediaList.length
|
|
2538
2543
|
});
|
|
2544
|
+
if (!agentBody) {
|
|
2545
|
+
log.debug?.("skipping empty message after Graph media recovery");
|
|
2546
|
+
return;
|
|
2547
|
+
}
|
|
2548
|
+
enqueuePrimaryMessageSystemEvent();
|
|
2549
|
+
teamAadGroupId = await resolveChannelTeamGroupId();
|
|
2550
|
+
let quoteBodyFull;
|
|
2551
|
+
const quoteMessageId = quoteInfo?.id;
|
|
2552
|
+
if (quoteMessageId && isDirectMessage && conversationId.startsWith("19:")) try {
|
|
2553
|
+
const graphToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2554
|
+
deadline: preprocessingDeadline,
|
|
2555
|
+
label: "MS Teams quote token",
|
|
2556
|
+
work: () => tokenProvider.getAccessToken("https://graph.microsoft.com")
|
|
2557
|
+
});
|
|
2558
|
+
quoteBodyFull = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2559
|
+
deadline: preprocessingDeadline,
|
|
2560
|
+
label: "MS Teams quote lookup",
|
|
2561
|
+
work: () => require_messenger.fetchChatMessageText(graphToken, conversationId, quoteMessageId, preprocessingDeadline)
|
|
2562
|
+
});
|
|
2563
|
+
} catch (err) {
|
|
2564
|
+
log.debug?.("failed to fetch full quoted message text", { error: require_setup_surface.formatUnknownError(err) });
|
|
2565
|
+
}
|
|
2539
2566
|
let threadContext;
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
const
|
|
2567
|
+
const threadParentId = activity.replyToId;
|
|
2568
|
+
const channelGroupId = teamAadGroupId;
|
|
2569
|
+
if (threadParentId && isChannel && channelGroupId) try {
|
|
2570
|
+
const graphToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2571
|
+
deadline: preprocessingDeadline,
|
|
2572
|
+
label: "MS Teams thread token",
|
|
2573
|
+
work: () => tokenProvider.getAccessToken("https://graph.microsoft.com")
|
|
2574
|
+
});
|
|
2575
|
+
const [parentResult, repliesResult] = await require_resolve_allowlist.withMSTeamsRequestDeadline({
|
|
2576
|
+
deadline: preprocessingDeadline,
|
|
2577
|
+
label: "MS Teams thread history",
|
|
2578
|
+
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)])
|
|
2579
|
+
});
|
|
2544
2580
|
const parentMsg = parentResult.status === "fulfilled" ? parentResult.value : void 0;
|
|
2545
2581
|
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({
|
|
2582
|
+
if (parentResult.status === "rejected") log.debug?.("failed to fetch parent message", { error: require_setup_surface.formatUnknownError(parentResult.reason) });
|
|
2583
|
+
if (repliesResult.status === "rejected") log.debug?.("failed to fetch thread replies", { error: require_setup_surface.formatUnknownError(repliesResult.reason) });
|
|
2584
|
+
const isThreadSenderAllowed = (msg) => groupPolicy === "allowlist" ? require_channel.resolveMSTeamsAllowlistMatch({
|
|
2549
2585
|
allowFrom: effectiveGroupAllowFrom,
|
|
2550
2586
|
senderId: msg.from?.user?.id ?? "",
|
|
2551
2587
|
senderName: msg.from?.user?.displayName,
|
|
2552
2588
|
allowNameMatching
|
|
2553
2589
|
}).allowed : true;
|
|
2554
2590
|
const parentSummary = summarizeParentMessage(parentMsg);
|
|
2555
|
-
const visibleParentMessages = parentMsg ? filterSupplementalContextItems({
|
|
2591
|
+
const visibleParentMessages = parentMsg ? (0, openclaw_plugin_sdk_context_visibility_runtime.filterSupplementalContextItems)({
|
|
2556
2592
|
items: [parentMsg],
|
|
2557
2593
|
mode: contextVisibilityMode,
|
|
2558
2594
|
kind: "thread",
|
|
2559
2595
|
isSenderAllowed: isThreadSenderAllowed
|
|
2560
2596
|
}).items : [];
|
|
2561
|
-
if (parentSummary && visibleParentMessages.length > 0 && shouldInjectParentContext(route.sessionKey,
|
|
2597
|
+
if (parentSummary && visibleParentMessages.length > 0 && shouldInjectParentContext(route.sessionKey, threadParentId)) {
|
|
2562
2598
|
core.system.enqueueSystemEvent(formatParentContextEvent(parentSummary), {
|
|
2563
2599
|
sessionKey: route.sessionKey,
|
|
2564
|
-
contextKey: `msteams:thread-parent:${conversationId}:${
|
|
2600
|
+
contextKey: `msteams:thread-parent:${conversationId}:${threadParentId}`
|
|
2565
2601
|
});
|
|
2566
|
-
markParentContextInjected(route.sessionKey,
|
|
2602
|
+
markParentContextInjected(route.sessionKey, threadParentId);
|
|
2567
2603
|
}
|
|
2568
2604
|
const allMessages = parentMsg ? [parentMsg, ...replies] : replies;
|
|
2569
2605
|
quoteSenderId = parentMsg?.from?.user?.id ?? parentMsg?.from?.application?.id ?? void 0;
|
|
2570
2606
|
quoteSenderName = parentMsg?.from?.user?.displayName ?? parentMsg?.from?.application?.displayName ?? quoteInfo?.sender;
|
|
2571
|
-
const { items: threadMessages } = filterSupplementalContextItems({
|
|
2607
|
+
const { items: threadMessages } = (0, openclaw_plugin_sdk_context_visibility_runtime.filterSupplementalContextItems)({
|
|
2572
2608
|
items: allMessages,
|
|
2573
2609
|
mode: contextVisibilityMode,
|
|
2574
2610
|
kind: "thread",
|
|
2575
2611
|
isSenderAllowed: isThreadSenderAllowed
|
|
2576
2612
|
});
|
|
2577
|
-
const formatted = formatThreadContext(threadMessages, activity.id);
|
|
2613
|
+
const formatted = require_messenger.formatThreadContext(threadMessages, activity.id);
|
|
2578
2614
|
if (formatted) threadContext = formatted;
|
|
2579
2615
|
} catch (err) {
|
|
2580
|
-
log.debug?.("failed to fetch thread history", { error: formatUnknownError(err) });
|
|
2616
|
+
log.debug?.("failed to fetch thread history", { error: require_setup_surface.formatUnknownError(err) });
|
|
2581
2617
|
}
|
|
2582
2618
|
quoteSenderName ??= quoteInfo?.sender;
|
|
2583
2619
|
const envelopeFrom = isDirectMessage ? senderName : conversationType;
|
|
2584
|
-
const { storePath, envelopeOptions, previousTimestamp } = resolveInboundSessionEnvelopeContext({
|
|
2620
|
+
const { storePath, envelopeOptions, previousTimestamp } = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundSessionEnvelopeContext)({
|
|
2585
2621
|
cfg,
|
|
2586
2622
|
agentId: route.agentId,
|
|
2587
2623
|
sessionKey: route.sessionKey
|
|
@@ -2596,7 +2632,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2596
2632
|
});
|
|
2597
2633
|
const isRoomish = !isDirectMessage;
|
|
2598
2634
|
const historyKey = isRoomish ? conversationId : void 0;
|
|
2599
|
-
if (isRoomish && historyKey) combinedBody = createChannelHistoryWindow({ historyMap: conversationHistories }).buildPendingContext({
|
|
2635
|
+
if (isRoomish && historyKey) combinedBody = (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).buildPendingContext({
|
|
2600
2636
|
historyKey,
|
|
2601
2637
|
limit: historyLimit,
|
|
2602
2638
|
currentMessage: combinedBody,
|
|
@@ -2608,26 +2644,26 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2608
2644
|
envelope: envelopeOptions
|
|
2609
2645
|
})
|
|
2610
2646
|
});
|
|
2611
|
-
const inboundHistory = isRoomish && historyKey && historyLimit > 0 ? createChannelHistoryWindow({ historyMap: conversationHistories }).buildInboundHistory({
|
|
2647
|
+
const inboundHistory = isRoomish && historyKey && historyLimit > 0 ? (0, openclaw_plugin_sdk_reply_history.createChannelHistoryWindow)({ historyMap: conversationHistories }).buildInboundHistory({
|
|
2612
2648
|
historyKey,
|
|
2613
2649
|
limit: historyLimit
|
|
2614
2650
|
}) : void 0;
|
|
2615
2651
|
const commandBody = text.trim();
|
|
2616
|
-
const quoteSenderAllowed = quoteInfo && quoteInfo.sender ? !isChannel || groupPolicy !== "allowlist" ? true : resolveMSTeamsAllowlistMatch({
|
|
2652
|
+
const quoteSenderAllowed = quoteInfo && quoteInfo.sender ? !isChannel || groupPolicy !== "allowlist" ? true : require_channel.resolveMSTeamsAllowlistMatch({
|
|
2617
2653
|
allowFrom: effectiveGroupAllowFrom,
|
|
2618
2654
|
senderId: quoteSenderId ?? "",
|
|
2619
2655
|
senderName: quoteSenderName,
|
|
2620
2656
|
allowNameMatching
|
|
2621
2657
|
}).allowed : true;
|
|
2622
2658
|
const bodyForAgent = threadContext ? `[Thread history]\n${threadContext}\n[/Thread history]\n\n${agentBody}` : agentBody;
|
|
2623
|
-
const nativeChannelId = isChannel &&
|
|
2624
|
-
const ctxPayload = buildChannelInboundEventContext({
|
|
2659
|
+
const nativeChannelId = isChannel && teamAadGroupId ? `${teamAadGroupId}/${graphChannelId}` : void 0;
|
|
2660
|
+
const ctxPayload = (0, openclaw_plugin_sdk_channel_inbound.buildChannelInboundEventContext)({
|
|
2625
2661
|
channel: "msteams",
|
|
2626
2662
|
finalize: core.channel.reply.finalizeInboundContext,
|
|
2627
2663
|
contextVisibility: contextVisibilityMode,
|
|
2628
2664
|
supplemental: { quote: quoteInfo ? {
|
|
2629
|
-
id: activity.replyToId ?? void 0,
|
|
2630
|
-
body: quoteInfo.body,
|
|
2665
|
+
id: quoteInfo.id ?? activity.replyToId ?? void 0,
|
|
2666
|
+
body: quoteBodyFull ?? quoteInfo.body,
|
|
2631
2667
|
sender: quoteInfo.sender,
|
|
2632
2668
|
senderAllowed: quoteSenderAllowed,
|
|
2633
2669
|
isQuote: true
|
|
@@ -2668,7 +2704,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2668
2704
|
canDetectMention: !isDirectMessage,
|
|
2669
2705
|
wasMentioned: isDirectMessage || mentionDecision.effectiveWasMentioned
|
|
2670
2706
|
},
|
|
2671
|
-
commands: { authorized: commandAuthorized }
|
|
2707
|
+
commands: { authorized: commandAuthorized === true }
|
|
2672
2708
|
},
|
|
2673
2709
|
extra: {
|
|
2674
2710
|
GroupSubject: !isDirectMessage ? conversationType : void 0,
|
|
@@ -2725,7 +2761,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2725
2761
|
ctxPayload,
|
|
2726
2762
|
recordInboundSession: core.channel.session.recordInboundSession,
|
|
2727
2763
|
record: { onRecordError: (err) => {
|
|
2728
|
-
logVerboseMessage(`msteams: failed updating session meta: ${formatUnknownError(err)}`);
|
|
2764
|
+
logVerboseMessage(`msteams: failed updating session meta: ${require_setup_surface.formatUnknownError(err)}`);
|
|
2729
2765
|
} },
|
|
2730
2766
|
history: {
|
|
2731
2767
|
isGroup: isRoomish,
|
|
@@ -2737,7 +2773,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2737
2773
|
dispatcher,
|
|
2738
2774
|
onSettled: () => markDispatchIdle()
|
|
2739
2775
|
}),
|
|
2740
|
-
runDispatch: () => dispatchReplyFromConfigWithSettledDispatcher({
|
|
2776
|
+
runDispatch: () => (0, openclaw_plugin_sdk_channel_inbound.dispatchReplyFromConfigWithSettledDispatcher)({
|
|
2741
2777
|
cfg,
|
|
2742
2778
|
ctxPayload,
|
|
2743
2779
|
dispatcher,
|
|
@@ -2750,8 +2786,8 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2750
2786
|
});
|
|
2751
2787
|
const dispatchResult = turnResult.dispatched ? turnResult.dispatchResult : void 0;
|
|
2752
2788
|
const queuedFinal = dispatchResult?.queuedFinal ?? false;
|
|
2753
|
-
const counts = resolveInboundReplyDispatchCounts(dispatchResult);
|
|
2754
|
-
const hasFinalResponse = hasFinalInboundReplyDispatch(dispatchResult);
|
|
2789
|
+
const counts = (0, openclaw_plugin_sdk_channel_inbound.resolveInboundReplyDispatchCounts)(dispatchResult);
|
|
2790
|
+
const hasFinalResponse = (0, openclaw_plugin_sdk_channel_inbound.hasFinalInboundReplyDispatch)(dispatchResult);
|
|
2755
2791
|
log.info("dispatch complete", {
|
|
2756
2792
|
queuedFinal,
|
|
2757
2793
|
counts
|
|
@@ -2760,8 +2796,8 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2760
2796
|
const finalCount = counts.final;
|
|
2761
2797
|
logVerboseMessage(`msteams: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${teamsTo}`);
|
|
2762
2798
|
} catch (err) {
|
|
2763
|
-
log.error("dispatch failed", { error: formatUnknownError(err) });
|
|
2764
|
-
runtime.error(`msteams dispatch failed: ${formatUnknownError(err)}`);
|
|
2799
|
+
log.error("dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
2800
|
+
runtime.error(`msteams dispatch failed: ${require_setup_surface.formatUnknownError(err)}`);
|
|
2765
2801
|
try {
|
|
2766
2802
|
await context.sendActivity("⚠️ Something went wrong. Please try again.");
|
|
2767
2803
|
} catch {}
|
|
@@ -2770,7 +2806,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2770
2806
|
const inboundDebouncer = core.channel.debounce.createInboundDebouncer({
|
|
2771
2807
|
debounceMs: inboundDebounceMs,
|
|
2772
2808
|
buildKey: (entry) => {
|
|
2773
|
-
const conversationId = normalizeMSTeamsConversationId(entry.context.activity.conversation?.id ?? "");
|
|
2809
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(entry.context.activity.conversation?.id ?? "");
|
|
2774
2810
|
const senderId = entry.context.activity.from?.aadObjectId ?? entry.context.activity.from?.id ?? "";
|
|
2775
2811
|
if (!senderId || !conversationId) return null;
|
|
2776
2812
|
return `msteams:${appId}:${conversationId}:${senderId}`;
|
|
@@ -2802,7 +2838,7 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2802
2838
|
});
|
|
2803
2839
|
},
|
|
2804
2840
|
onError: (err) => {
|
|
2805
|
-
runtime.error(`msteams debounce flush failed: ${formatUnknownError(err)}`);
|
|
2841
|
+
runtime.error(`msteams debounce flush failed: ${require_setup_surface.formatUnknownError(err)}`);
|
|
2806
2842
|
}
|
|
2807
2843
|
});
|
|
2808
2844
|
return async function handleTeamsMessage(context) {
|
|
@@ -2811,9 +2847,9 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2811
2847
|
const rawText = activity.text?.trim() ?? "";
|
|
2812
2848
|
const htmlText = extractTextFromHtmlAttachments(attachments);
|
|
2813
2849
|
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 ?? "");
|
|
2850
|
+
const text = require_channel.stripMSTeamsMentionTags(rawText || htmlText || valueText || "");
|
|
2851
|
+
const wasMentioned = require_channel.wasMSTeamsBotMentioned(activity);
|
|
2852
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
2817
2853
|
const replyToId = activity.replyToId ?? void 0;
|
|
2818
2854
|
const implicitMentionKinds = conversationId && replyToId && await wasMSTeamsMessageSentWithPersistence({
|
|
2819
2855
|
conversationId,
|
|
@@ -2831,29 +2867,13 @@ function createMSTeamsMessageHandler(deps) {
|
|
|
2831
2867
|
}
|
|
2832
2868
|
//#endregion
|
|
2833
2869
|
//#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
2870
|
/**
|
|
2851
2871
|
* Create a handler for MS Teams reaction activities (reactionsAdded / reactionsRemoved).
|
|
2852
2872
|
* The returned function accepts a turn context and a direction string.
|
|
2853
2873
|
*/
|
|
2854
2874
|
function createMSTeamsReactionHandler(deps) {
|
|
2855
2875
|
const { cfg, log } = deps;
|
|
2856
|
-
const core = getMSTeamsRuntime();
|
|
2876
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
2857
2877
|
const msteamsCfg = cfg.channels?.msteams;
|
|
2858
2878
|
return async function handleReaction(context, direction) {
|
|
2859
2879
|
const activity = context.activity;
|
|
@@ -2867,7 +2887,7 @@ function createMSTeamsReactionHandler(deps) {
|
|
|
2867
2887
|
log.debug?.("reaction activity missing from.id; skipping");
|
|
2868
2888
|
return;
|
|
2869
2889
|
}
|
|
2870
|
-
const conversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
2890
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
2871
2891
|
const conversationType = activity.conversation?.conversationType ?? "personal";
|
|
2872
2892
|
const isGroupChat = conversationType === "groupChat" || activity.conversation?.isGroup === true;
|
|
2873
2893
|
const isChannel = conversationType === "channel";
|
|
@@ -2900,7 +2920,7 @@ function createMSTeamsReactionHandler(deps) {
|
|
|
2900
2920
|
const targetMessageId = activity.replyToId ?? "unknown";
|
|
2901
2921
|
for (const reaction of reactions) {
|
|
2902
2922
|
const reactionType = reaction.type ?? "unknown";
|
|
2903
|
-
const emoji =
|
|
2923
|
+
const emoji = require_messenger.resolveMSTeamsReactionEmoji(reactionType);
|
|
2904
2924
|
const label = direction === "added" ? `Teams reaction ${emoji} added by ${senderName} on message ${targetMessageId}` : `Teams reaction ${emoji} removed by ${senderName} from message ${targetMessageId}`;
|
|
2905
2925
|
log.info(`reaction ${direction}`, {
|
|
2906
2926
|
sender: senderId,
|
|
@@ -3059,7 +3079,7 @@ function registerMSTeamsHandlers(handler, deps) {
|
|
|
3059
3079
|
try {
|
|
3060
3080
|
await handleTeamsMessage(context);
|
|
3061
3081
|
} catch (err) {
|
|
3062
|
-
deps.runtime.error(`msteams handler failed: ${formatUnknownError(err)}`);
|
|
3082
|
+
deps.runtime.error(`msteams handler failed: ${require_setup_surface.formatUnknownError(err)}`);
|
|
3063
3083
|
}
|
|
3064
3084
|
await next();
|
|
3065
3085
|
});
|
|
@@ -3069,7 +3089,7 @@ function registerMSTeamsHandlers(handler, deps) {
|
|
|
3069
3089
|
const botId = ctx.activity?.recipient?.id;
|
|
3070
3090
|
const msteamsCfg = deps.cfg.channels?.msteams;
|
|
3071
3091
|
for (const member of membersAdded) if (member.id === botId) {
|
|
3072
|
-
const isPersonal = (normalizeOptionalLowercaseString(ctx.activity?.conversation?.conversationType) ?? "personal") === "personal";
|
|
3092
|
+
const isPersonal = ((0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(ctx.activity?.conversation?.conversationType) ?? "personal") === "personal";
|
|
3073
3093
|
if (isPersonal && msteamsCfg?.welcomeCard !== false) {
|
|
3074
3094
|
const card = buildWelcomeCard({
|
|
3075
3095
|
botName: ctx.activity?.recipient?.name ?? void 0,
|
|
@@ -3085,7 +3105,7 @@ function registerMSTeamsHandlers(handler, deps) {
|
|
|
3085
3105
|
});
|
|
3086
3106
|
deps.log.info("sent welcome card");
|
|
3087
3107
|
} catch (err) {
|
|
3088
|
-
deps.log.debug?.("failed to send welcome card", { error: formatUnknownError(err) });
|
|
3108
|
+
deps.log.debug?.("failed to send welcome card", { error: require_setup_surface.formatUnknownError(err) });
|
|
3089
3109
|
}
|
|
3090
3110
|
} else if (!isPersonal && msteamsCfg?.groupWelcomeCard === true) {
|
|
3091
3111
|
const botName = ctx.activity?.recipient?.name ?? void 0;
|
|
@@ -3093,7 +3113,7 @@ function registerMSTeamsHandlers(handler, deps) {
|
|
|
3093
3113
|
await ctx.sendActivity(buildGroupWelcomeText(botName));
|
|
3094
3114
|
deps.log.info("sent group welcome message");
|
|
3095
3115
|
} catch (err) {
|
|
3096
|
-
deps.log.debug?.("failed to send group welcome", { error: formatUnknownError(err) });
|
|
3116
|
+
deps.log.debug?.("failed to send group welcome", { error: require_setup_surface.formatUnknownError(err) });
|
|
3097
3117
|
}
|
|
3098
3118
|
} else deps.log.debug?.("skipping welcome (disabled by config or conversation type)");
|
|
3099
3119
|
} else deps.log.debug?.("member added", { member: member.id });
|
|
@@ -3155,14 +3175,14 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3155
3175
|
userComment = JSON.parse(value.actionValue.feedback).feedbackText || void 0;
|
|
3156
3176
|
} catch {}
|
|
3157
3177
|
const rawConversationId = activity.conversation?.id ?? "unknown";
|
|
3158
|
-
const conversationId = normalizeMSTeamsConversationId(rawConversationId);
|
|
3178
|
+
const conversationId = require_channel.normalizeMSTeamsConversationId(rawConversationId);
|
|
3159
3179
|
const senderId = activity.from?.aadObjectId ?? activity.from?.id ?? "unknown";
|
|
3160
3180
|
const messageId = value.replyToId ?? activity.replyToId ?? "unknown";
|
|
3161
3181
|
const isNegative = reaction === "dislike";
|
|
3162
|
-
const convType = normalizeOptionalLowercaseString(activity.conversation?.conversationType);
|
|
3182
|
+
const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(activity.conversation?.conversationType);
|
|
3163
3183
|
const isDirectMessage = convType === "personal" || !convType && !activity.conversation?.isGroup;
|
|
3164
3184
|
const isChannel = convType === "channel";
|
|
3165
|
-
const core = getMSTeamsRuntime();
|
|
3185
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
3166
3186
|
const route = core.channel.routing.resolveAgentRoute({
|
|
3167
3187
|
cfg: deps.cfg,
|
|
3168
3188
|
channel: "msteams",
|
|
@@ -3171,8 +3191,8 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3171
3191
|
id: isDirectMessage ? senderId : conversationId
|
|
3172
3192
|
}
|
|
3173
3193
|
});
|
|
3174
|
-
const feedbackThreadId = isChannel ? extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId ?? void 0 : void 0;
|
|
3175
|
-
if (feedbackThreadId) route.sessionKey = resolveThreadSessionKeys({
|
|
3194
|
+
const feedbackThreadId = isChannel ? require_channel.extractMSTeamsConversationMessageId(rawConversationId) ?? activity.replyToId ?? void 0 : void 0;
|
|
3195
|
+
if (feedbackThreadId) route.sessionKey = (0, openclaw_plugin_sdk_routing.resolveThreadSessionKeys)({
|
|
3176
3196
|
baseSessionKey: route.sessionKey,
|
|
3177
3197
|
threadId: feedbackThreadId,
|
|
3178
3198
|
parentSessionKey: route.sessionKey
|
|
@@ -3194,8 +3214,8 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3194
3214
|
try {
|
|
3195
3215
|
const storePath = core.channel.session.resolveStorePath(deps.cfg.session?.store, { agentId: route.agentId });
|
|
3196
3216
|
const safeKey = route.sessionKey.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
3197
|
-
await appendRegularFile({
|
|
3198
|
-
filePath:
|
|
3217
|
+
await (0, openclaw_plugin_sdk_security_runtime.appendRegularFile)({
|
|
3218
|
+
filePath: node_path.default.join(storePath, `${safeKey}.jsonl`),
|
|
3199
3219
|
content: `${JSON.stringify(feedbackEvent)}\n`,
|
|
3200
3220
|
rejectSymlinkParents: true
|
|
3201
3221
|
}).catch(() => {});
|
|
@@ -3211,10 +3231,6 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3211
3231
|
id: activity.recipient.id,
|
|
3212
3232
|
name: activity.recipient.name
|
|
3213
3233
|
} : void 0,
|
|
3214
|
-
bot: activity.recipient ? {
|
|
3215
|
-
id: activity.recipient.id,
|
|
3216
|
-
name: activity.recipient.name
|
|
3217
|
-
} : void 0,
|
|
3218
3234
|
conversation: {
|
|
3219
3235
|
id: conversationId,
|
|
3220
3236
|
conversationType: activity.conversation?.conversationType,
|
|
@@ -3236,7 +3252,7 @@ async function runMSTeamsFeedbackInvokeHandler(context, deps) {
|
|
|
3236
3252
|
userComment,
|
|
3237
3253
|
log: deps.log
|
|
3238
3254
|
}).catch((err) => {
|
|
3239
|
-
deps.log.error("feedback reflection failed", { error: formatUnknownError(err) });
|
|
3255
|
+
deps.log.error("feedback reflection failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3240
3256
|
});
|
|
3241
3257
|
return true;
|
|
3242
3258
|
}
|
|
@@ -3249,18 +3265,18 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3249
3265
|
const expiredUploadMessage = "The file upload request has expired. Please try sending the file again.";
|
|
3250
3266
|
const activity = context.activity;
|
|
3251
3267
|
if (activity.type !== "invoke" || activity.name !== "fileConsent/invoke") return false;
|
|
3252
|
-
const consentResponse = parseFileConsentInvoke(activity);
|
|
3268
|
+
const consentResponse = require_messenger.parseFileConsentInvoke(activity);
|
|
3253
3269
|
if (!consentResponse) {
|
|
3254
3270
|
log.debug?.("invalid file consent invoke", { value: activity.value });
|
|
3255
3271
|
return false;
|
|
3256
3272
|
}
|
|
3257
3273
|
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);
|
|
3274
|
+
const inMemoryFile = require_messenger.getPendingUpload(uploadId);
|
|
3275
|
+
const fsFile = inMemoryFile ? void 0 : await require_messenger.getPendingUploadFs(uploadId);
|
|
3260
3276
|
const pendingFile = inMemoryFile ?? fsFile;
|
|
3261
3277
|
if (pendingFile) {
|
|
3262
|
-
const pendingConversationId = normalizeMSTeamsConversationId(pendingFile.conversationId);
|
|
3263
|
-
const invokeConversationId = normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
3278
|
+
const pendingConversationId = require_channel.normalizeMSTeamsConversationId(pendingFile.conversationId);
|
|
3279
|
+
const invokeConversationId = require_channel.normalizeMSTeamsConversationId(activity.conversation?.id ?? "");
|
|
3264
3280
|
if (!invokeConversationId || pendingConversationId !== invokeConversationId) {
|
|
3265
3281
|
log.info("file consent conversation mismatch", {
|
|
3266
3282
|
uploadId,
|
|
@@ -3278,12 +3294,12 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3278
3294
|
size: pendingFile.buffer.length
|
|
3279
3295
|
});
|
|
3280
3296
|
try {
|
|
3281
|
-
await uploadToConsentUrl({
|
|
3297
|
+
await require_messenger.uploadToConsentUrl({
|
|
3282
3298
|
url: consentResponse.uploadInfo.uploadUrl,
|
|
3283
3299
|
buffer: pendingFile.buffer,
|
|
3284
3300
|
contentType: pendingFile.contentType
|
|
3285
3301
|
});
|
|
3286
|
-
const fileInfoCard = buildFileInfoCard({
|
|
3302
|
+
const fileInfoCard = require_messenger.buildFileInfoCard({
|
|
3287
3303
|
filename: consentResponse.uploadInfo.name,
|
|
3288
3304
|
contentUrl: consentResponse.uploadInfo.contentUrl,
|
|
3289
3305
|
uniqueId: consentResponse.uploadInfo.uniqueId,
|
|
@@ -3313,12 +3329,12 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3313
3329
|
} catch (err) {
|
|
3314
3330
|
log.error("file upload failed", {
|
|
3315
3331
|
uploadId,
|
|
3316
|
-
error: formatUnknownError(err)
|
|
3332
|
+
error: require_setup_surface.formatUnknownError(err)
|
|
3317
3333
|
});
|
|
3318
3334
|
await context.sendActivity("File upload failed. Please try again.");
|
|
3319
3335
|
} finally {
|
|
3320
|
-
removePendingUpload(uploadId);
|
|
3321
|
-
await removePendingUploadFs(uploadId);
|
|
3336
|
+
require_messenger.removePendingUpload(uploadId);
|
|
3337
|
+
await require_messenger.removePendingUploadFs(uploadId);
|
|
3322
3338
|
}
|
|
3323
3339
|
} else {
|
|
3324
3340
|
log.debug?.("pending file not found for consent", { uploadId });
|
|
@@ -3326,8 +3342,8 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3326
3342
|
}
|
|
3327
3343
|
else {
|
|
3328
3344
|
log.debug?.("user declined file consent", { uploadId });
|
|
3329
|
-
removePendingUpload(uploadId);
|
|
3330
|
-
await removePendingUploadFs(uploadId);
|
|
3345
|
+
require_messenger.removePendingUpload(uploadId);
|
|
3346
|
+
await require_messenger.removePendingUploadFs(uploadId);
|
|
3331
3347
|
}
|
|
3332
3348
|
return true;
|
|
3333
3349
|
}
|
|
@@ -3338,7 +3354,7 @@ async function handleMSTeamsFileConsentInvoke(context, log) {
|
|
|
3338
3354
|
*/
|
|
3339
3355
|
async function runMSTeamsFileConsentInvokeHandler(context, log) {
|
|
3340
3356
|
try {
|
|
3341
|
-
await withRevokedProxyFallback({
|
|
3357
|
+
await require_messenger.withRevokedProxyFallback({
|
|
3342
3358
|
run: async () => await handleMSTeamsFileConsentInvoke(context, log),
|
|
3343
3359
|
onRevoked: async () => true,
|
|
3344
3360
|
onRevokedLog: () => {
|
|
@@ -3346,7 +3362,7 @@ async function runMSTeamsFileConsentInvokeHandler(context, log) {
|
|
|
3346
3362
|
}
|
|
3347
3363
|
});
|
|
3348
3364
|
} catch (err) {
|
|
3349
|
-
log.debug?.("file consent handler error", { error: formatUnknownError(err) });
|
|
3365
|
+
log.debug?.("file consent handler error", { error: require_setup_surface.formatUnknownError(err) });
|
|
3350
3366
|
}
|
|
3351
3367
|
}
|
|
3352
3368
|
//#endregion
|
|
@@ -3365,7 +3381,7 @@ function applyMSTeamsWebhookTimeouts(httpServer, opts) {
|
|
|
3365
3381
|
//#endregion
|
|
3366
3382
|
//#region extensions/msteams/src/monitor.ts
|
|
3367
3383
|
async function monitorMSTeamsProvider(opts) {
|
|
3368
|
-
const core = getMSTeamsRuntime();
|
|
3384
|
+
const core = require_runtime.getMSTeamsRuntime();
|
|
3369
3385
|
const log = core.logging.getChildLogger({ name: "msteams" });
|
|
3370
3386
|
let cfg = opts.cfg;
|
|
3371
3387
|
let msteamsCfg = cfg.channels?.msteams;
|
|
@@ -3376,7 +3392,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3376
3392
|
shutdown: async () => {}
|
|
3377
3393
|
};
|
|
3378
3394
|
}
|
|
3379
|
-
const creds = resolveMSTeamsCredentials(msteamsCfg);
|
|
3395
|
+
const creds = require_resolve_allowlist.resolveMSTeamsCredentials(msteamsCfg);
|
|
3380
3396
|
if (!creds) {
|
|
3381
3397
|
log.error("msteams credentials not configured");
|
|
3382
3398
|
return {
|
|
@@ -3392,26 +3408,22 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3392
3408
|
throw new Error(`exit ${code}`);
|
|
3393
3409
|
}
|
|
3394
3410
|
};
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
let
|
|
3398
|
-
|
|
3411
|
+
const configuredAllowFrom = msteamsCfg.allowFrom;
|
|
3412
|
+
const configuredGroupAllowFrom = msteamsCfg.groupAllowFrom;
|
|
3413
|
+
let allowFrom = require_resolve_allowlist.projectStableMSTeamsUserAllowlist(configuredAllowFrom);
|
|
3414
|
+
let groupAllowFrom = require_resolve_allowlist.projectStableMSTeamsUserAllowlist(configuredGroupAllowFrom);
|
|
3415
|
+
let teamsConfig = require_resolve_allowlist.projectStableMSTeamsTeamsConfig(msteamsCfg.teams);
|
|
3416
|
+
const allowNameMatching = (0, openclaw_plugin_sdk_dangerous_name_runtime.isDangerousNameMatchingEnabled)(msteamsCfg);
|
|
3399
3417
|
const cleanAllowEntry = (entry) => entry.replace(/^(msteams|teams):/i, "").replace(/^user:/i, "").trim();
|
|
3400
3418
|
const isStableUserId = (entry) => /^[0-9a-fA-F-]{16,}$/.test(entry);
|
|
3401
3419
|
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
|
-
};
|
|
3420
|
+
const isMutableUserEntry = (entry) => !isStableUserId(entry) && !/^accessGroup:/i.test(entry);
|
|
3409
3421
|
const resolveAllowlistUsers = async (label, entries) => {
|
|
3410
3422
|
if (entries.length === 0) return {
|
|
3411
3423
|
additions: [],
|
|
3412
3424
|
unresolved: []
|
|
3413
3425
|
};
|
|
3414
|
-
const resolved = await resolveMSTeamsUserAllowlist({
|
|
3426
|
+
const resolved = await require_resolve_allowlist.resolveMSTeamsUserAllowlist({
|
|
3415
3427
|
cfg,
|
|
3416
3428
|
entries
|
|
3417
3429
|
});
|
|
@@ -3419,102 +3431,44 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3419
3431
|
const unresolved = [];
|
|
3420
3432
|
for (const entry of resolved) if (entry.resolved && entry.id) additions.push(entry.id);
|
|
3421
3433
|
else unresolved.push(entry.input);
|
|
3422
|
-
summarizeMapping(label, resolved.filter((entry) => entry.resolved && entry.id).map((entry) => `${entry.input}→${entry.id}`), unresolved, runtime);
|
|
3434
|
+
(0, openclaw_plugin_sdk_allow_from.summarizeMapping)(label, resolved.filter((entry) => entry.resolved && entry.id).map((entry) => `${entry.input}→${entry.id}`), unresolved, runtime);
|
|
3423
3435
|
return {
|
|
3424
3436
|
additions,
|
|
3425
3437
|
unresolved
|
|
3426
3438
|
};
|
|
3427
3439
|
};
|
|
3428
3440
|
try {
|
|
3429
|
-
allowFrom = mergeStableUserIds(allowFrom);
|
|
3430
|
-
if (Array.isArray(groupAllowFrom) && groupAllowFrom.length > 0) groupAllowFrom = mergeStableUserIds(groupAllowFrom);
|
|
3431
3441
|
if (allowNameMatching) {
|
|
3432
|
-
const allowEntries = cleanAllowEntries(
|
|
3442
|
+
const allowEntries = cleanAllowEntries(configuredAllowFrom).filter(isMutableUserEntry);
|
|
3433
3443
|
if (allowEntries.length > 0) {
|
|
3434
3444
|
const { additions } = await resolveAllowlistUsers("msteams users", allowEntries);
|
|
3435
|
-
allowFrom = mergeAllowlist({
|
|
3445
|
+
allowFrom = (0, openclaw_plugin_sdk_allow_from.mergeAllowlist)({
|
|
3436
3446
|
existing: allowFrom,
|
|
3437
3447
|
additions
|
|
3438
3448
|
});
|
|
3439
3449
|
}
|
|
3440
|
-
if (Array.isArray(
|
|
3441
|
-
const groupEntries = cleanAllowEntries(
|
|
3450
|
+
if (Array.isArray(configuredGroupAllowFrom) && configuredGroupAllowFrom.length > 0) {
|
|
3451
|
+
const groupEntries = cleanAllowEntries(configuredGroupAllowFrom).filter(isMutableUserEntry);
|
|
3442
3452
|
if (groupEntries.length > 0) {
|
|
3443
3453
|
const { additions } = await resolveAllowlistUsers("msteams group users", groupEntries);
|
|
3444
|
-
groupAllowFrom = mergeAllowlist({
|
|
3454
|
+
groupAllowFrom = (0, openclaw_plugin_sdk_allow_from.mergeAllowlist)({
|
|
3445
3455
|
existing: groupAllowFrom,
|
|
3446
3456
|
additions
|
|
3447
3457
|
});
|
|
3448
3458
|
}
|
|
3449
3459
|
}
|
|
3450
3460
|
}
|
|
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
|
-
}
|
|
3461
|
+
if (msteamsCfg.teams && Object.keys(msteamsCfg.teams).length > 0) {
|
|
3462
|
+
const resolved = await require_resolve_allowlist.resolveMSTeamsTeamsConfig({
|
|
3463
|
+
cfg,
|
|
3464
|
+
teamIdMode: "bot-framework",
|
|
3465
|
+
teams: msteamsCfg.teams
|
|
3466
|
+
});
|
|
3467
|
+
teamsConfig = resolved.teams;
|
|
3468
|
+
(0, openclaw_plugin_sdk_allow_from.summarizeMapping)("msteams channels", resolved.mapping, resolved.unresolved, runtime);
|
|
3515
3469
|
}
|
|
3516
3470
|
} catch (err) {
|
|
3517
|
-
runtime.error?.(`msteams resolve failed;
|
|
3471
|
+
runtime.error?.(`msteams resolve failed; mutable allowlist entries are disabled. ${require_setup_surface.formatUnknownError(err)}`);
|
|
3518
3472
|
}
|
|
3519
3473
|
msteamsCfg = {
|
|
3520
3474
|
...msteamsCfg,
|
|
@@ -3534,8 +3488,8 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3534
3488
|
const MB = 1024 * 1024;
|
|
3535
3489
|
const agentDefaults = cfg.agents?.defaults;
|
|
3536
3490
|
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();
|
|
3491
|
+
const conversationStore = opts.conversationStore ?? require_polls.createMSTeamsConversationStoreState();
|
|
3492
|
+
const pollStore = opts.pollStore ?? require_polls.createMSTeamsPollStoreState();
|
|
3539
3493
|
log.info(`starting provider (port ${port})`);
|
|
3540
3494
|
const express = await import("express");
|
|
3541
3495
|
const expressApp = express.default();
|
|
@@ -3547,7 +3501,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3547
3501
|
}
|
|
3548
3502
|
next();
|
|
3549
3503
|
});
|
|
3550
|
-
expressApp.use(express.json({ limit: DEFAULT_WEBHOOK_MAX_BODY_BYTES }));
|
|
3504
|
+
expressApp.use(express.json({ limit: openclaw_plugin_sdk_webhook_ingress.DEFAULT_WEBHOOK_MAX_BODY_BYTES }));
|
|
3551
3505
|
expressApp.use((err, _req, res, next) => {
|
|
3552
3506
|
if (err && typeof err === "object" && "status" in err && err.status === 413) {
|
|
3553
3507
|
res.status(413).json({ error: "Payload too large" });
|
|
@@ -3556,11 +3510,12 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3556
3510
|
next(err);
|
|
3557
3511
|
});
|
|
3558
3512
|
const configuredPath = msteamsCfg.webhook?.path ?? "/api/messages";
|
|
3559
|
-
const
|
|
3560
|
-
|
|
3561
|
-
|
|
3513
|
+
const ssoConnectionName = msteamsCfg.sso?.enabled && msteamsCfg.sso.connectionName ? msteamsCfg.sso.connectionName : void 0;
|
|
3514
|
+
const { app } = await require_resolve_allowlist.loadMSTeamsSdkWithAuth(creds, {
|
|
3515
|
+
...require_resolve_allowlist.resolveMSTeamsSdkCloudOptions(msteamsCfg),
|
|
3516
|
+
httpServerAdapter: await require_resolve_allowlist.createMSTeamsExpressAdapter(expressApp),
|
|
3562
3517
|
messagingEndpoint: configuredPath,
|
|
3563
|
-
...
|
|
3518
|
+
...ssoConnectionName ? { oauthDefaultConnectionName: ssoConnectionName } : {}
|
|
3564
3519
|
});
|
|
3565
3520
|
if (configuredPath !== "/api/messages") {
|
|
3566
3521
|
let warnedLegacyMessagesRoute = false;
|
|
@@ -3573,16 +3528,12 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3573
3528
|
expressApp(req, res, next);
|
|
3574
3529
|
});
|
|
3575
3530
|
}
|
|
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
|
-
}
|
|
3531
|
+
const tokenProvider = require_resolve_allowlist.createMSTeamsTokenProvider(app);
|
|
3532
|
+
const ssoDeps = ssoConnectionName ? {
|
|
3533
|
+
tokenStore: require_sso_token_store.createMSTeamsSsoTokenStoreFs(),
|
|
3534
|
+
connectionName: ssoConnectionName
|
|
3535
|
+
} : void 0;
|
|
3536
|
+
if (ssoDeps) log.debug?.("msteams sso enabled", { connectionName: ssoDeps.connectionName });
|
|
3586
3537
|
const handler = buildActivityHandler();
|
|
3587
3538
|
const handlerDeps = {
|
|
3588
3539
|
cfg,
|
|
@@ -3594,15 +3545,14 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3594
3545
|
mediaMaxBytes,
|
|
3595
3546
|
conversationStore,
|
|
3596
3547
|
pollStore,
|
|
3597
|
-
log
|
|
3598
|
-
sso: ssoDeps
|
|
3548
|
+
log
|
|
3599
3549
|
};
|
|
3600
3550
|
registerMSTeamsHandlers(handler, handlerDeps);
|
|
3601
3551
|
app.on("card.action", async (ctx) => {
|
|
3602
3552
|
const adaptedCtx = adaptSdkContext(ctx, app);
|
|
3603
3553
|
try {
|
|
3604
3554
|
const activity = adaptedCtx.activity;
|
|
3605
|
-
const vote = extractMSTeamsPollVote(activity);
|
|
3555
|
+
const vote = require_polls.extractMSTeamsPollVote(activity);
|
|
3606
3556
|
if (vote) {
|
|
3607
3557
|
const voterId = activity?.from?.aadObjectId ?? activity?.from?.id ?? "unknown";
|
|
3608
3558
|
try {
|
|
@@ -3620,8 +3570,8 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3620
3570
|
value: "Poll not found."
|
|
3621
3571
|
};
|
|
3622
3572
|
}
|
|
3623
|
-
const pollConversationId = existingPoll.conversationId ? normalizeMSTeamsConversationId(existingPoll.conversationId) : void 0;
|
|
3624
|
-
const activityConversationId = normalizeMSTeamsConversationId(activity?.conversation?.id ?? "");
|
|
3573
|
+
const pollConversationId = existingPoll.conversationId ? require_channel.normalizeMSTeamsConversationId(existingPoll.conversationId) : void 0;
|
|
3574
|
+
const activityConversationId = require_channel.normalizeMSTeamsConversationId(activity?.conversation?.id ?? "");
|
|
3625
3575
|
if (pollConversationId && pollConversationId !== activityConversationId) {
|
|
3626
3576
|
log.info("poll vote ignored (conversation mismatch)", {
|
|
3627
3577
|
pollId: vote.pollId,
|
|
@@ -3658,7 +3608,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3658
3608
|
} catch (err) {
|
|
3659
3609
|
log.error("failed to record poll vote", {
|
|
3660
3610
|
pollId: vote.pollId,
|
|
3661
|
-
error: formatUnknownError(err)
|
|
3611
|
+
error: require_setup_surface.formatUnknownError(err)
|
|
3662
3612
|
});
|
|
3663
3613
|
return {
|
|
3664
3614
|
statusCode: 500,
|
|
@@ -3675,7 +3625,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3675
3625
|
}
|
|
3676
3626
|
}
|
|
3677
3627
|
handler.run(adaptedCtx).catch((err) => {
|
|
3678
|
-
log.error("msteams card.action dispatch failed", { error: formatUnknownError(err) });
|
|
3628
|
+
log.error("msteams card.action dispatch failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3679
3629
|
});
|
|
3680
3630
|
return {
|
|
3681
3631
|
statusCode: 200,
|
|
@@ -3683,7 +3633,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3683
3633
|
value: "OK"
|
|
3684
3634
|
};
|
|
3685
3635
|
} catch (err) {
|
|
3686
|
-
log.error("msteams card.action failed", { error: formatUnknownError(err) });
|
|
3636
|
+
log.error("msteams card.action failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3687
3637
|
return {
|
|
3688
3638
|
statusCode: 500,
|
|
3689
3639
|
type: "application/vnd.microsoft.error",
|
|
@@ -3751,7 +3701,7 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3751
3701
|
hasExpiry: Boolean(ctx.token.expiration)
|
|
3752
3702
|
});
|
|
3753
3703
|
})().catch((err) => {
|
|
3754
|
-
log.error("msteams sso token persistence failed", { error: formatUnknownError(err) });
|
|
3704
|
+
log.error("msteams sso token persistence failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3755
3705
|
});
|
|
3756
3706
|
});
|
|
3757
3707
|
app.on("message.submit", async (ctx) => {
|
|
@@ -3768,39 +3718,31 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3768
3718
|
}
|
|
3769
3719
|
await handler.run(adaptedCtx);
|
|
3770
3720
|
} catch (err) {
|
|
3771
|
-
log.error("msteams webhook failed", { error: formatUnknownError(err) });
|
|
3721
|
+
log.error("msteams webhook failed", { error: require_setup_surface.formatUnknownError(err) });
|
|
3772
3722
|
}
|
|
3773
3723
|
});
|
|
3774
3724
|
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);
|
|
3725
|
+
const httpServer = await new Promise((resolve, reject) => {
|
|
3726
|
+
const server = expressApp.listen(port, (err) => err ? reject(err) : resolve(server));
|
|
3727
|
+
}).catch((err) => {
|
|
3728
|
+
log.error("msteams server error", { error: require_setup_surface.formatUnknownError(err) });
|
|
3729
|
+
throw err;
|
|
3789
3730
|
});
|
|
3731
|
+
log.info(`msteams provider started on port ${port}`);
|
|
3790
3732
|
applyMSTeamsWebhookTimeouts(httpServer);
|
|
3791
3733
|
httpServer.on("error", (err) => {
|
|
3792
|
-
log.error("msteams server error", { error: formatUnknownError(err) });
|
|
3734
|
+
log.error("msteams server error", { error: require_setup_surface.formatUnknownError(err) });
|
|
3793
3735
|
});
|
|
3794
3736
|
const shutdown = async () => {
|
|
3795
3737
|
log.info("shutting down msteams provider");
|
|
3796
3738
|
return new Promise((resolve) => {
|
|
3797
3739
|
httpServer.close((err) => {
|
|
3798
|
-
if (err) log.debug?.("msteams server close error", { error: formatUnknownError(err) });
|
|
3740
|
+
if (err) log.debug?.("msteams server close error", { error: require_setup_surface.formatUnknownError(err) });
|
|
3799
3741
|
resolve();
|
|
3800
3742
|
});
|
|
3801
3743
|
});
|
|
3802
3744
|
};
|
|
3803
|
-
await keepHttpServerTaskAlive({
|
|
3745
|
+
await (0, openclaw_plugin_sdk_channel_outbound.keepHttpServerTaskAlive)({
|
|
3804
3746
|
server: httpServer,
|
|
3805
3747
|
abortSignal: opts.abortSignal,
|
|
3806
3748
|
onAbort: shutdown
|
|
@@ -3810,13 +3752,6 @@ async function monitorMSTeamsProvider(opts) {
|
|
|
3810
3752
|
shutdown
|
|
3811
3753
|
};
|
|
3812
3754
|
}
|
|
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
3755
|
/**
|
|
3821
3756
|
* Build a minimal ActivityHandler-compatible object that supports
|
|
3822
3757
|
* onMessage / onMembersAdded registration and a run() method.
|
|
@@ -3866,7 +3801,9 @@ function adaptSdkContext(ctx, app) {
|
|
|
3866
3801
|
const sdkCtx = ctx ?? {};
|
|
3867
3802
|
if (typeof sdkCtx.reply !== "function" && typeof sdkCtx.send !== "function") return ctx;
|
|
3868
3803
|
const conversationId = sdkCtx.activity?.conversation?.id ?? "";
|
|
3869
|
-
const
|
|
3804
|
+
const inboundApi = sdkCtx.api;
|
|
3805
|
+
const activityApi = inboundApi ?? app.api;
|
|
3806
|
+
const getTeamDetails = inboundApi ? (teamId) => inboundApi.teams.getById(teamId) : void 0;
|
|
3870
3807
|
const conversationType = (sdkCtx.activity?.conversation?.conversationType ?? "").toLowerCase();
|
|
3871
3808
|
const isThreadable = conversationType === "channel" || conversationType === "groupchat";
|
|
3872
3809
|
const sendActivity = (activity) => isThreadable ? sdkCtx.reply(activity) : sdkCtx.send(activity);
|
|
@@ -3884,8 +3821,9 @@ function adaptSdkContext(ctx, app) {
|
|
|
3884
3821
|
deleteActivity: async (activityId) => {
|
|
3885
3822
|
return activityApi.conversations.activities(conversationId).delete(activityId);
|
|
3886
3823
|
},
|
|
3824
|
+
getTeamDetails,
|
|
3887
3825
|
stream: sdkCtx.stream
|
|
3888
3826
|
});
|
|
3889
3827
|
}
|
|
3890
3828
|
//#endregion
|
|
3891
|
-
|
|
3829
|
+
exports.monitorMSTeamsProvider = monitorMSTeamsProvider;
|