@openclaw/msteams 2026.7.2-beta.7 → 2026.8.1-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.
@@ -1,43 +1,19 @@
1
- //#region \0rolldown/runtime.js
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
- key = keys[i];
11
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
- get: ((k) => from[k]).bind(null, key),
13
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
- });
15
- }
16
- return to;
17
- };
18
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
- value: mod,
20
- enumerable: true
21
- }) : target, mod));
22
- //#endregion
1
+ const require_rolldown_runtime = require("./rolldown-runtime-D6vf50IK.cjs");
23
2
  const require_runtime = require("./runtime-gZTAuFux.cjs");
24
3
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
25
4
  let openclaw_plugin_sdk_file_lock = require("openclaw/plugin-sdk/file-lock");
26
5
  let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-runtime");
27
6
  let node_crypto = require("node:crypto");
28
- node_crypto = __toESM(node_crypto, 1);
7
+ node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
29
8
  let node_path = require("node:path");
30
- node_path = __toESM(node_path, 1);
9
+ node_path = require_rolldown_runtime.__toESM(node_path, 1);
31
10
  let openclaw_plugin_sdk_keyed_async_queue = require("openclaw/plugin-sdk/keyed-async-queue");
32
11
  //#region extensions/msteams/src/conversation-store-helpers.ts
33
12
  function normalizeStoredConversationId(raw) {
34
13
  return raw.split(";")[0] ?? raw;
35
14
  }
36
15
  function parseStoredConversationTimestamp(value) {
37
- if (!value) return null;
38
- const parsed = Date.parse(value);
39
- if (!Number.isFinite(parsed)) return null;
40
- return parsed;
16
+ return (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(value) ?? null;
41
17
  }
42
18
  function toConversationStoreEntries(entries) {
43
19
  return Array.from(entries, ([conversationId, reference]) => ({
@@ -384,9 +360,7 @@ function createPollVoteBucketStateStore(params) {
384
360
  });
385
361
  }
386
362
  function parseTimestamp(value) {
387
- if (!value) return null;
388
- const parsed = Date.parse(value);
389
- return Number.isFinite(parsed) ? parsed : null;
363
+ return (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(value) ?? null;
390
364
  }
391
365
  function pruneExpired(polls) {
392
366
  const cutoff = Date.now() - MSTEAMS_POLL_TTL_MS;
@@ -405,8 +379,7 @@ function selectRetainedMSTeamsPolls(polls) {
405
379
  }
406
380
  function normalizeMSTeamsPollSelections(poll, selections) {
407
381
  const maxSelections = Math.max(1, poll.maxSelections);
408
- const mapped = selections.map((entry) => (0, openclaw_plugin_sdk_number_runtime.parseStrictNonNegativeInteger)(entry)).filter((value) => value !== void 0).filter((value) => value >= 0 && value < poll.options.length).map((value) => String(value));
409
- return (0, openclaw_plugin_sdk_string_coerce_runtime.uniqueStrings)(maxSelections > 1 ? mapped.slice(0, maxSelections) : mapped.slice(0, 1));
382
+ return (0, openclaw_plugin_sdk_string_coerce_runtime.uniqueStrings)(selections.map((entry) => (0, openclaw_plugin_sdk_number_runtime.parseStrictNonNegativeInteger)(entry)).filter((value) => value !== void 0).filter((value) => value >= 0 && value < poll.options.length).map((value) => String(value))).slice(0, maxSelections);
410
383
  }
411
384
  function splitMSTeamsPoll(poll) {
412
385
  const { votes, ...metadata } = poll;
@@ -589,12 +562,6 @@ Object.defineProperty(exports, "MSTEAMS_SQLITE_MAX_POLL_ROWS", {
589
562
  return MSTEAMS_SQLITE_MAX_POLL_ROWS;
590
563
  }
591
564
  });
592
- Object.defineProperty(exports, "__toESM", {
593
- enumerable: true,
594
- get: function() {
595
- return __toESM;
596
- }
597
- });
598
565
  Object.defineProperty(exports, "buildMSTeamsConversationStateKey", {
599
566
  enumerable: true,
600
567
  get: function() {
@@ -0,0 +1,36 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let openclaw_plugin_sdk_qa_runner_runtime = require("openclaw/plugin-sdk/qa-runner-runtime");
3
+ //#region extensions/msteams/src/qa/cli.ts
4
+ const DEFAULT_MSTEAMS_QA_SCENARIOS = ["channel-canary"];
5
+ const loadMSTeamsQaAdapterRuntime = (0, openclaw_plugin_sdk_qa_runner_runtime.createLazyCliRuntimeLoader)(() => Promise.resolve().then(() => require("./adapter.runtime-DEYjslFX.cjs")));
6
+ async function runQaMSTeams(options) {
7
+ await (0, openclaw_plugin_sdk_qa_runner_runtime.runLiveTransportQaSuiteCommand)({
8
+ channelId: "msteams",
9
+ defaultProviderMode: "mock-openai",
10
+ options,
11
+ selectScenarioIds: ({ scenarioIds }) => scenarioIds?.length ? [...scenarioIds] : [...DEFAULT_MSTEAMS_QA_SCENARIOS]
12
+ });
13
+ }
14
+ //#endregion
15
+ //#region extensions/msteams/qa-runner-api.ts
16
+ const qaRunnerCliRegistrations = [(0, openclaw_plugin_sdk_qa_runner_runtime.createLiveTransportQaCliRegistration)({
17
+ commandName: "msteams",
18
+ adapterFactory: {
19
+ id: "msteams",
20
+ isolatesInstances: true,
21
+ matches: ({ channelId, driver }) => channelId === "msteams" && driver === "live",
22
+ async create(context) {
23
+ return await (await loadMSTeamsQaAdapterRuntime()).createMSTeamsQaTransportAdapter(context);
24
+ }
25
+ },
26
+ defaultProviderMode: "mock-openai",
27
+ description: "Run Microsoft Teams Gateway QA against a local Bot Framework mock",
28
+ providerModeHelp: "Provider mode: mock-openai, aimock, or live-frontier",
29
+ outputDirHelp: "Microsoft Teams QA artifact directory",
30
+ allowFailuresHelp: "Write artifacts without setting a failing exit code when scenarios fail",
31
+ scenarioHelp: "Run only the named Microsoft Teams QA scenario (repeatable)",
32
+ sutAccountHelp: "Normalized Microsoft Teams SUT account id in QA artifacts",
33
+ run: runQaMSTeams
34
+ })];
35
+ //#endregion
36
+ exports.qaRunnerCliRegistrations = qaRunnerCliRegistrations;
@@ -5,16 +5,74 @@ let openclaw_plugin_sdk_lazy_runtime = require("openclaw/plugin-sdk/lazy-runtime
5
5
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
6
6
  let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
7
7
  let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
8
+ let openclaw_plugin_sdk_html_entity_runtime = require("openclaw/plugin-sdk/html-entity-runtime");
9
+ let openclaw_plugin_sdk_fetch_runtime = require("openclaw/plugin-sdk/fetch-runtime");
8
10
  let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-http");
9
11
  let node_buffer = require("node:buffer");
10
12
  let node_dns_promises = require("node:dns/promises");
11
13
  let openclaw_plugin_sdk_ssrf_policy = require("openclaw/plugin-sdk/ssrf-policy");
12
14
  let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-runtime");
13
15
  let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text-utility-runtime");
16
+ let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
14
17
  let openclaw_plugin_sdk_secret_file = require("openclaw/plugin-sdk/secret-file");
15
18
  let node_module = require("node:module");
16
19
  let openclaw_plugin_sdk_secret_input = require("openclaw/plugin-sdk/secret-input");
17
- let openclaw_plugin_sdk_html_entity_runtime = require("openclaw/plugin-sdk/html-entity-runtime");
20
+ //#region extensions/msteams/src/inbound.ts
21
+ /**
22
+ * Strip HTML tags, preserving text content.
23
+ */
24
+ function htmlToPlainText(html) {
25
+ return (0, openclaw_plugin_sdk_html_entity_runtime.decodeHtmlEntities)(html.replace(/<[^>]*>/g, " ")).replaceAll("\xA0", " ").replace(/\s+/g, " ").trim();
26
+ }
27
+ /**
28
+ * Extract quote info from MS Teams HTML reply attachments.
29
+ * Teams wraps quoted content in a blockquote with itemtype="http://schema.skype.com/Reply".
30
+ */
31
+ function extractMSTeamsQuoteInfo(attachments) {
32
+ for (const att of attachments) {
33
+ let content = "";
34
+ if (typeof att.content === "string") content = att.content;
35
+ else if (typeof att.content === "object" && att.content !== null) {
36
+ const record = att.content;
37
+ content = typeof record.text === "string" ? record.text : typeof record.body === "string" ? record.body : "";
38
+ }
39
+ if (!content) continue;
40
+ if (!content.includes("http://schema.skype.com/Reply")) continue;
41
+ const senderMatch = /<strong[^>]*itemprop=["']mri["'][^>]*>(.*?)<\/strong>/i.exec(content);
42
+ const sender = senderMatch?.[1] ? htmlToPlainText(senderMatch[1]) : void 0;
43
+ const bodyMatch = /<p[^>]*itemprop=["']copy["'][^>]*>(.*?)<\/p>/is.exec(content) ?? /<p[^>]*itemprop=["']preview["'][^>]*>(.*?)<\/p>/is.exec(content);
44
+ const body = bodyMatch?.[1] ? htmlToPlainText(bodyMatch[1]) : void 0;
45
+ const id = /<blockquote[^>]*\bitemid=["']([^"']+)["'][^>]*>/is.exec(content)?.[1]?.trim() || void 0;
46
+ if (body) return {
47
+ sender: sender ?? "unknown",
48
+ body,
49
+ ...id ? { id } : {}
50
+ };
51
+ }
52
+ }
53
+ function normalizeMSTeamsConversationId(raw) {
54
+ return raw.split(";")[0] ?? raw;
55
+ }
56
+ function extractMSTeamsConversationMessageId(raw) {
57
+ if (!raw) return;
58
+ return (/(?:^|;)messageid=([^;]+)/i.exec(raw)?.[1]?.trim() ?? "") || void 0;
59
+ }
60
+ function parseMSTeamsActivityTimestamp(value) {
61
+ if (!value) return;
62
+ if (value instanceof Date) return value;
63
+ if (typeof value !== "string") return;
64
+ const date = new Date(value);
65
+ return Number.isNaN(date.getTime()) ? void 0 : date;
66
+ }
67
+ function stripMSTeamsMentionTags(text) {
68
+ return text.replace(/<at[^>]*>.*?<\/at>/gi, "").trim();
69
+ }
70
+ function wasMSTeamsBotMentioned(activity) {
71
+ const botId = activity.recipient?.id;
72
+ if (!botId) return false;
73
+ return (activity.entities ?? []).some((e) => e.type === "mention" && e.mentioned?.id === botId);
74
+ }
75
+ //#endregion
18
76
  //#region extensions/msteams/src/request-timeout.ts
19
77
  const MSTEAMS_REQUEST_TIMEOUT_MS = 3e4;
20
78
  const MSTEAMS_SHAREPOINT_UPLOAD_BASE_TIMEOUT_MS = 5 * 6e4;
@@ -58,52 +116,6 @@ async function withMSTeamsAbortableRequestTimeout(params) {
58
116
  }
59
117
  }
60
118
  //#endregion
61
- //#region extensions/msteams/src/response-with-release.ts
62
- const NULL_BODY_STATUSES = /* @__PURE__ */ new Set([
63
- 101,
64
- 204,
65
- 205,
66
- 304
67
- ]);
68
- function responseWithRelease(response, release) {
69
- let released = false;
70
- const releaseOnce = async () => {
71
- if (released) return;
72
- released = true;
73
- await release();
74
- };
75
- if (!response.body || NULL_BODY_STATUSES.has(response.status)) {
76
- releaseOnce();
77
- return response;
78
- }
79
- const reader = response.body.getReader();
80
- const body = new ReadableStream({
81
- async pull(controller) {
82
- try {
83
- const next = await reader.read();
84
- if (next.done) {
85
- controller.close();
86
- await releaseOnce();
87
- return;
88
- }
89
- controller.enqueue(next.value);
90
- } catch (error) {
91
- await releaseOnce();
92
- throw error;
93
- }
94
- },
95
- async cancel(reason) {
96
- reader.cancel(reason).catch(() => void 0);
97
- await releaseOnce();
98
- }
99
- });
100
- return new Response(body, {
101
- status: response.status,
102
- statusText: response.statusText,
103
- headers: response.headers
104
- });
105
- }
106
- //#endregion
107
119
  //#region extensions/msteams/src/attachments/shared.ts
108
120
  const IMAGE_EXT_RE = /\.(avif|bmp|gif|heic|heif|jpe?g|png|tiff?|webp)$/i;
109
121
  const IMG_SRC_RE = /<img[^>]+src=["']([^"']+)["'][^>]*>/gi;
@@ -477,6 +489,9 @@ async function resolveAndValidateIP(hostname, resolveFn) {
477
489
  }
478
490
  /** Maximum number of redirects to follow in safeFetch. */
479
491
  const MAX_SAFE_REDIRECTS = 5;
492
+ function isRedirectStatus(status) {
493
+ return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
494
+ }
480
495
  /**
481
496
  * Fetch a URL with redirect: "manual", validating each redirect target
482
497
  * against the hostname allowlist and optional DNS-resolved IP (anti-SSRF).
@@ -493,6 +508,13 @@ async function safeFetch(params) {
493
508
  if (!isUrlAllowed(currentUrl, params.allowHosts)) throw new Error(`Initial download URL blocked: ${currentUrl}`);
494
509
  if (currentHeaders.has("authorization") && params.authorizationAllowHosts && !isUrlAllowed(currentUrl, params.authorizationAllowHosts)) currentHeaders.delete("authorization");
495
510
  if (!hasDispatcher) {
511
+ const lookupFn = async (hostname) => {
512
+ const resolved = await resolveFn(hostname);
513
+ return [{
514
+ ...resolved,
515
+ family: resolved.address.includes(":") ? 6 : 4
516
+ }];
517
+ };
496
518
  const guarded = await (0, openclaw_plugin_sdk_ssrf_runtime.fetchWithSsrFGuard)({
497
519
  url: currentUrl,
498
520
  fetchImpl: resolveGuardedFetchImpl({
@@ -506,12 +528,12 @@ async function safeFetch(params) {
506
528
  maxRedirects: MAX_SAFE_REDIRECTS,
507
529
  requireHttps: true,
508
530
  policy: resolveMediaSsrfPolicy(params.allowHosts),
509
- lookupFn: resolveFn,
531
+ lookupFn,
510
532
  retainAuthorizationRedirectHostnameAllowlist: resolveRetainedAuthorizationRedirectHostnameAllowlist(params.authorizationAllowHosts),
511
533
  auditContext: "msteams.attachment",
512
534
  timeoutMs: params.timeoutMs ?? 3e4
513
535
  });
514
- return responseWithRelease(guarded.response, guarded.release);
536
+ return (0, openclaw_plugin_sdk_fetch_runtime.responseWithRelease)(guarded.response, guarded.release);
515
537
  }
516
538
  if (resolveFn) try {
517
539
  const initialHost = new URL(currentUrl).hostname;
@@ -525,13 +547,7 @@ async function safeFetch(params) {
525
547
  headers: currentHeaders,
526
548
  redirect: "manual"
527
549
  });
528
- if (![
529
- 301,
530
- 302,
531
- 303,
532
- 307,
533
- 308
534
- ].includes(res.status)) return res;
550
+ if (!isRedirectStatus(res.status)) return res;
535
551
  const location = res.headers.get("location");
536
552
  if (!location) return res;
537
553
  let redirectUrl;
@@ -656,6 +672,117 @@ function normalizeBotFrameworkServiceUrl(serviceUrl) {
656
672
  throw new Error(`Blocked Microsoft Teams serviceUrl host: ${describeBotFrameworkServiceUrlHost(serviceUrl)}`);
657
673
  }
658
674
  //#endregion
675
+ //#region extensions/msteams/src/qa/private-runtime.ts
676
+ const PRIVATE_QA_BUILD_ENV = "OPENCLAW_BUILD_PRIVATE_QA";
677
+ const PRIVATE_QA_NONCE_HEADER = "x-openclaw-msteams-qa-nonce";
678
+ const PRIVATE_QA_RUNTIME_SYMBOL = Symbol.for("openclaw.msteams.privateQaRuntime");
679
+ var PrivateQaHttpClient = class PrivateQaHttpClient {
680
+ constructor(connectorUrl, nonce, options = {}) {
681
+ this.connectorUrl = connectorUrl;
682
+ this.nonce = nonce;
683
+ this.options = options;
684
+ }
685
+ clone(options = {}) {
686
+ return new PrivateQaHttpClient(this.connectorUrl, this.nonce, {
687
+ ...this.options,
688
+ ...options,
689
+ headers: {
690
+ ...this.options.headers,
691
+ ...options.headers
692
+ }
693
+ });
694
+ }
695
+ async request(config) {
696
+ const sourceUrl = new URL(config.url ?? "", "https://smba.trafficmanager.net");
697
+ const targetUrl = new URL(`${sourceUrl.pathname}${sourceUrl.search}`, this.connectorUrl);
698
+ const headers = new Headers(this.options.headers);
699
+ for (const [key, value] of Object.entries(config.headers ?? {})) if (value != null) headers.set(key, String(value));
700
+ headers.set(PRIVATE_QA_NONCE_HEADER, this.nonce);
701
+ const token = typeof this.options.token === "function" ? await this.options.token() : this.options.token;
702
+ if (token) headers.set("authorization", `Bearer ${token}`);
703
+ const method = String(config.method ?? "GET").toUpperCase();
704
+ const { response, release } = await (0, openclaw_plugin_sdk_ssrf_runtime.fetchWithSsrFGuard)({
705
+ url: targetUrl.toString(),
706
+ init: {
707
+ method,
708
+ headers,
709
+ body: method === "GET" || method === "HEAD" || config.data == null ? void 0 : typeof config.data === "string" ? config.data : JSON.stringify(config.data)
710
+ },
711
+ policy: (0, openclaw_plugin_sdk_ssrf_runtime.ssrfPolicyFromHttpBaseUrlAllowedOrigin)(targetUrl.toString()),
712
+ maxRedirects: 0,
713
+ auditContext: "msteams-private-qa-connector"
714
+ });
715
+ try {
716
+ const text = await response.text();
717
+ const data = text ? JSON.parse(text) : void 0;
718
+ if (!response.ok) throw new Error(`Microsoft Teams private QA connector returned HTTP ${response.status}`);
719
+ return {
720
+ data,
721
+ status: response.status,
722
+ statusText: response.statusText,
723
+ headers: Object.fromEntries(response.headers),
724
+ config
725
+ };
726
+ } finally {
727
+ await release();
728
+ }
729
+ }
730
+ get(url, config = {}) {
731
+ return this.request({
732
+ ...config,
733
+ method: "GET",
734
+ url
735
+ });
736
+ }
737
+ post(url, data, config = {}) {
738
+ return this.request({
739
+ ...config,
740
+ data,
741
+ method: "POST",
742
+ url
743
+ });
744
+ }
745
+ put(url, data, config = {}) {
746
+ return this.request({
747
+ ...config,
748
+ data,
749
+ method: "PUT",
750
+ url
751
+ });
752
+ }
753
+ patch(url, data, config = {}) {
754
+ return this.request({
755
+ ...config,
756
+ data,
757
+ method: "PATCH",
758
+ url
759
+ });
760
+ }
761
+ delete(url, config = {}) {
762
+ return this.request({
763
+ ...config,
764
+ method: "DELETE",
765
+ url
766
+ });
767
+ }
768
+ };
769
+ function resolveMSTeamsPrivateQaRuntime(env = process.env, bootstrap = globalThis[PRIVATE_QA_RUNTIME_SYMBOL]) {
770
+ if (!bootstrap) return;
771
+ if (env[PRIVATE_QA_BUILD_ENV] !== "1") throw new Error("Microsoft Teams private QA runtime requires OPENCLAW_BUILD_PRIVATE_QA=1");
772
+ const connectorUrl = bootstrap.connectorUrl?.trim();
773
+ const nonce = bootstrap.nonce?.trim();
774
+ const botToken = bootstrap.botToken?.trim();
775
+ if (!connectorUrl || !nonce || !botToken) throw new Error("Microsoft Teams private QA bootstrap requires connector URL, nonce, and bot token");
776
+ const parsedConnectorUrl = new URL(connectorUrl);
777
+ if (parsedConnectorUrl.protocol !== "http:" || parsedConnectorUrl.hostname !== "127.0.0.1" && parsedConnectorUrl.hostname !== "localhost") throw new Error("Microsoft Teams private QA connector must use loopback HTTP");
778
+ return {
779
+ client: new PrivateQaHttpClient(parsedConnectorUrl.toString(), nonce),
780
+ listenHost: "127.0.0.1",
781
+ skipAuth: true,
782
+ token: async () => botToken
783
+ };
784
+ }
785
+ //#endregion
659
786
  //#region extensions/msteams/src/user-agent.ts
660
787
  let cachedUserAgent;
661
788
  function resolveTeamsSdkVersion() {
@@ -733,13 +860,19 @@ async function createMSTeamsExpressAdapter(serverOrApp) {
733
860
  */
734
861
  async function createMSTeamsApp(creds, options) {
735
862
  const { App, cloudFromName } = await loadSdkModules();
863
+ const privateQaRuntime = resolveMSTeamsPrivateQaRuntime();
736
864
  const cloud = options?.cloud ?? "Public";
737
865
  const serviceUrl = options?.serviceUrl ? normalizeBotFrameworkServiceUrl(options.serviceUrl) : void 0;
738
866
  const appOptions = {
739
- client: options?.httpClient ?? {
867
+ client: privateQaRuntime?.client ?? options?.httpClient ?? {
740
868
  headers: { "User-Agent": buildOpenClawUserAgentFragment() },
741
869
  timeout: 3e4
742
870
  },
871
+ ...privateQaRuntime ? {
872
+ clientSecret: "",
873
+ skipAuth: privateQaRuntime.skipAuth,
874
+ token: privateQaRuntime.token
875
+ } : {},
743
876
  ...options?.httpServerAdapter ? { httpServerAdapter: options.httpServerAdapter } : {},
744
877
  ...options?.messagingEndpoint ? { messagingEndpoint: options.messagingEndpoint } : {},
745
878
  cloud: cloudFromName(cloud),
@@ -766,7 +899,7 @@ async function createFederatedApp(creds, App, appOptions) {
766
899
  try {
767
900
  privateKey = await (0, openclaw_plugin_sdk_secret_file.readSecretFile)(creds.certificatePath, "Microsoft Teams certificate");
768
901
  } catch (err) {
769
- const msg = err instanceof Error ? err.message : String(err);
902
+ const msg = (0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err);
770
903
  throw new Error(`Failed to read certificate file at '${creds.certificatePath}': ${msg}`, { cause: err });
771
904
  }
772
905
  return createCertificateApp(creds, privateKey, App, appOptions);
@@ -854,6 +987,10 @@ function createMSTeamsTokenBody(params) {
854
987
  function resolveMSTeamsTokenExpiresAt(value) {
855
988
  return (0, openclaw_plugin_sdk_number_runtime.resolveExpiresAtMsFromDurationSeconds)(value, { bufferMs: EXPIRY_BUFFER_MS });
856
989
  }
990
+ function assertMSTeamsTokenResponseObject(data, failureLabel) {
991
+ if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(data)) throw new Error(`MSTeams ${failureLabel} failed: invalid token response fields`);
992
+ return data;
993
+ }
857
994
  function parseMSTeamsTokenResponse(data, failureLabel) {
858
995
  const expiresAt = resolveMSTeamsTokenExpiresAt(data.expires_in);
859
996
  if (typeof data.access_token !== "string" || !data.access_token || expiresAt === void 0 || data.refresh_token !== void 0 && typeof data.refresh_token !== "string" || data.scope !== void 0 && typeof data.scope !== "string") throw new Error(`MSTeams ${failureLabel} failed: invalid token response fields`);
@@ -880,7 +1017,7 @@ async function fetchMSTeamsTokens(params) {
880
1017
  });
881
1018
  try {
882
1019
  if (!response.ok) throw await createMSTeamsHttpError(response, `MSTeams ${params.failureLabel} failed`);
883
- return parseMSTeamsTokenResponse(await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(response, `MSTeams ${params.failureLabel} failed`), params.failureLabel);
1020
+ return parseMSTeamsTokenResponse(assertMSTeamsTokenResponseObject(await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(response, `MSTeams ${params.failureLabel} failed`), params.failureLabel), params.failureLabel);
884
1021
  } finally {
885
1022
  await release();
886
1023
  }
@@ -1050,21 +1187,30 @@ async function requestGraph(params) {
1050
1187
  try {
1051
1188
  if (!response.ok) throw await createMSTeamsHttpError(response, `${params.errorPrefix ?? "Graph"} ${params.path} failed`);
1052
1189
  releaseInFinally = false;
1053
- return responseWithRelease(response, release);
1190
+ return (0, openclaw_plugin_sdk_fetch_runtime.responseWithRelease)(response, release);
1054
1191
  } finally {
1055
1192
  if (releaseInFinally) await release();
1056
1193
  }
1057
1194
  }
1058
1195
  async function readOptionalGraphJson(res, label) {
1059
- if (res.status === 204 || res.headers?.get?.("content-length") === "0") return;
1196
+ if (res.status === 204 || res.headers.get("content-length") === "0") return;
1060
1197
  return await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(res, label);
1061
1198
  }
1062
- async function fetchGraphJson(params) {
1063
- return await readOptionalGraphJson(await requestGraph({
1199
+ async function mutateGraphJson(params) {
1200
+ const errorPrefix = `Graph${params.beta ? " beta" : ""} ${params.method}`;
1201
+ return readOptionalGraphJson(await requestGraph({
1064
1202
  token: params.token,
1065
1203
  path: params.path,
1066
1204
  method: params.method,
1067
1205
  body: params.body,
1206
+ root: params.beta ? GRAPH_BETA : void 0,
1207
+ errorPrefix
1208
+ }), `${errorPrefix} ${params.path} failed`);
1209
+ }
1210
+ async function fetchGraphJson(params) {
1211
+ return await readOptionalGraphJson(await requestGraph({
1212
+ token: params.token,
1213
+ path: params.path,
1068
1214
  headers: params.headers,
1069
1215
  deadline: params.deadline
1070
1216
  }), `Graph ${params.path} failed`);
@@ -1159,41 +1305,13 @@ async function listTeamsByNameWithPageInfo(token, query) {
1159
1305
  path: `/groups?$filter=${encodeURIComponent(filter)}&$select=id,displayName`
1160
1306
  });
1161
1307
  }
1162
- async function postGraphJson(params) {
1163
- return readOptionalGraphJson(await requestGraph({
1164
- token: params.token,
1165
- path: params.path,
1166
- method: "POST",
1167
- body: params.body,
1168
- errorPrefix: "Graph POST"
1169
- }), `Graph POST ${params.path} failed`);
1170
- }
1171
- async function postGraphBetaJson(params) {
1172
- return readOptionalGraphJson(await requestGraph({
1173
- token: params.token,
1174
- path: params.path,
1175
- method: "POST",
1176
- root: GRAPH_BETA,
1177
- body: params.body,
1178
- errorPrefix: "Graph beta POST"
1179
- }), `Graph beta POST ${params.path} failed`);
1180
- }
1181
1308
  async function deleteGraphRequest(params) {
1182
- await requestGraph({
1309
+ await (await requestGraph({
1183
1310
  token: params.token,
1184
1311
  path: params.path,
1185
1312
  method: "DELETE",
1186
1313
  errorPrefix: "Graph DELETE"
1187
- });
1188
- }
1189
- async function patchGraphJson(params) {
1190
- return readOptionalGraphJson(await requestGraph({
1191
- token: params.token,
1192
- path: params.path,
1193
- method: "PATCH",
1194
- body: params.body,
1195
- errorPrefix: "Graph PATCH"
1196
- }), `Graph PATCH ${params.path} failed`);
1314
+ })).body?.cancel().catch(() => void 0);
1197
1315
  }
1198
1316
  async function listChannelsForTeam(token, teamId) {
1199
1317
  return (await listChannelsForTeamWithPageInfo(token, teamId)).items;
@@ -1241,62 +1359,6 @@ async function findGraphUsersByExactIdentity(params) {
1241
1359
  });
1242
1360
  }
1243
1361
  //#endregion
1244
- //#region extensions/msteams/src/inbound.ts
1245
- /**
1246
- * Strip HTML tags, preserving text content.
1247
- */
1248
- function htmlToPlainText(html) {
1249
- return (0, openclaw_plugin_sdk_html_entity_runtime.decodeHtmlEntities)(html.replace(/<[^>]*>/g, " ")).replaceAll("\xA0", " ").replace(/\s+/g, " ").trim();
1250
- }
1251
- /**
1252
- * Extract quote info from MS Teams HTML reply attachments.
1253
- * Teams wraps quoted content in a blockquote with itemtype="http://schema.skype.com/Reply".
1254
- */
1255
- function extractMSTeamsQuoteInfo(attachments) {
1256
- for (const att of attachments) {
1257
- let content = "";
1258
- if (typeof att.content === "string") content = att.content;
1259
- else if (typeof att.content === "object" && att.content !== null) {
1260
- const record = att.content;
1261
- content = typeof record.text === "string" ? record.text : typeof record.body === "string" ? record.body : "";
1262
- }
1263
- if (!content) continue;
1264
- if (!content.includes("http://schema.skype.com/Reply")) continue;
1265
- const senderMatch = /<strong[^>]*itemprop=["']mri["'][^>]*>(.*?)<\/strong>/i.exec(content);
1266
- const sender = senderMatch?.[1] ? htmlToPlainText(senderMatch[1]) : void 0;
1267
- const bodyMatch = /<p[^>]*itemprop=["']copy["'][^>]*>(.*?)<\/p>/is.exec(content) ?? /<p[^>]*itemprop=["']preview["'][^>]*>(.*?)<\/p>/is.exec(content);
1268
- const body = bodyMatch?.[1] ? htmlToPlainText(bodyMatch[1]) : void 0;
1269
- const id = /<blockquote[^>]*\bitemid=["']([^"']+)["'][^>]*>/is.exec(content)?.[1]?.trim() || void 0;
1270
- if (body) return {
1271
- sender: sender ?? "unknown",
1272
- body,
1273
- ...id ? { id } : {}
1274
- };
1275
- }
1276
- }
1277
- function normalizeMSTeamsConversationId(raw) {
1278
- return raw.split(";")[0] ?? raw;
1279
- }
1280
- function extractMSTeamsConversationMessageId(raw) {
1281
- if (!raw) return;
1282
- return (/(?:^|;)messageid=([^;]+)/i.exec(raw)?.[1]?.trim() ?? "") || void 0;
1283
- }
1284
- function parseMSTeamsActivityTimestamp(value) {
1285
- if (!value) return;
1286
- if (value instanceof Date) return value;
1287
- if (typeof value !== "string") return;
1288
- const date = new Date(value);
1289
- return Number.isNaN(date.getTime()) ? void 0 : date;
1290
- }
1291
- function stripMSTeamsMentionTags(text) {
1292
- return text.replace(/<at[^>]*>.*?<\/at>/gi, "").trim();
1293
- }
1294
- function wasMSTeamsBotMentioned(activity) {
1295
- const botId = activity.recipient?.id;
1296
- if (!botId) return false;
1297
- return (activity.entities ?? []).some((e) => e.type === "mention" && e.mentioned?.id === botId);
1298
- }
1299
- //#endregion
1300
1362
  //#region extensions/msteams/src/resolve-allowlist.ts
1301
1363
  const MSTEAMS_GROUP_CONVERSATION_ID = /^19:.+@thread\.(?:tacv2|skype|v2)$/i;
1302
1364
  function normalizeExactMatch(value) {
@@ -1930,6 +1992,12 @@ Object.defineProperty(exports, "isLikelyImageAttachment", {
1930
1992
  return isLikelyImageAttachment;
1931
1993
  }
1932
1994
  });
1995
+ Object.defineProperty(exports, "isRedirectStatus", {
1996
+ enumerable: true,
1997
+ get: function() {
1998
+ return isRedirectStatus;
1999
+ }
2000
+ });
1933
2001
  Object.defineProperty(exports, "isUrlAllowed", {
1934
2002
  enumerable: true,
1935
2003
  get: function() {
@@ -1978,6 +2046,12 @@ Object.defineProperty(exports, "looksLikeMSTeamsTargetId", {
1978
2046
  return looksLikeMSTeamsTargetId;
1979
2047
  }
1980
2048
  });
2049
+ Object.defineProperty(exports, "mutateGraphJson", {
2050
+ enumerable: true,
2051
+ get: function() {
2052
+ return mutateGraphJson;
2053
+ }
2054
+ });
1981
2055
  Object.defineProperty(exports, "normalizeBotFrameworkServiceUrl", {
1982
2056
  enumerable: true,
1983
2057
  get: function() {
@@ -2038,24 +2112,6 @@ Object.defineProperty(exports, "parseMSTeamsTeamEntry", {
2038
2112
  return parseMSTeamsTeamEntry;
2039
2113
  }
2040
2114
  });
2041
- Object.defineProperty(exports, "patchGraphJson", {
2042
- enumerable: true,
2043
- get: function() {
2044
- return patchGraphJson;
2045
- }
2046
- });
2047
- Object.defineProperty(exports, "postGraphBetaJson", {
2048
- enumerable: true,
2049
- get: function() {
2050
- return postGraphBetaJson;
2051
- }
2052
- });
2053
- Object.defineProperty(exports, "postGraphJson", {
2054
- enumerable: true,
2055
- get: function() {
2056
- return postGraphJson;
2057
- }
2058
- });
2059
2115
  Object.defineProperty(exports, "projectStableMSTeamsGroupAllowlist", {
2060
2116
  enumerable: true,
2061
2117
  get: function() {
@@ -2110,6 +2166,12 @@ Object.defineProperty(exports, "resolveMSTeamsMediaKind", {
2110
2166
  return resolveMSTeamsMediaKind;
2111
2167
  }
2112
2168
  });
2169
+ Object.defineProperty(exports, "resolveMSTeamsPrivateQaRuntime", {
2170
+ enumerable: true,
2171
+ get: function() {
2172
+ return resolveMSTeamsPrivateQaRuntime;
2173
+ }
2174
+ });
2113
2175
  Object.defineProperty(exports, "resolveMSTeamsRequestTimeoutMs", {
2114
2176
  enumerable: true,
2115
2177
  get: function() {