@openclaw/msteams 2026.9.4 → 2026.9.6

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.
Files changed (38) hide show
  1. package/assets/activity.svg +40 -0
  2. package/assets/icon.png +0 -0
  3. package/dist/{adapter.runtime-BzR3RK2J.cjs → .setup/adapter.runtime-CQWZeqB5.cjs} +5 -3
  4. package/dist/{approval-handler.runtime-DRhFNeJS.cjs → .setup/approval-handler.runtime-CUnanUdR.cjs} +3 -3
  5. package/dist/{channel-B9LIyYeu.cjs → .setup/channel-BDp16XRR.cjs} +128 -89
  6. package/dist/{channel.runtime-BergqMHg.cjs → .setup/channel.runtime-Cv5F8JCa.cjs} +61 -34
  7. package/dist/{channel.setup-BhxokviI.cjs → .setup/channel.setup-BVDfgdCc.cjs} +3 -3
  8. package/dist/{delegated-state-DiEz9JIa.cjs → .setup/delegated-state-u-ywZyZT.cjs} +5 -5
  9. package/dist/{messenger-TLdZf9Ar.cjs → .setup/messenger-8RA1FUJa.cjs} +112 -188
  10. package/dist/{oauth-DUtLTz2u.cjs → .setup/oauth-C6m7dvHj.cjs} +1 -1
  11. package/dist/{polls-BBdZPx_R.cjs → .setup/polls-BJ4-4uZm.cjs} +14 -7
  12. package/dist/{reaction-types-BbBBdnQ5.cjs → .setup/reaction-types-C7RA7Wxt.cjs} +1 -1
  13. package/dist/{resolve-allowlist-DmUTQxEz.cjs → .setup/resolve-allowlist-CHzaaWiC.cjs} +135 -93
  14. package/dist/.setup/secret-contract-api-ChQdBeU1.cjs +4 -0
  15. package/dist/.setup/secret-contract-api-ipiUsANg.cjs +28 -0
  16. package/dist/{send-CU61PSDN.cjs → .setup/send-CYN_N7O1.cjs} +99 -34
  17. package/dist/.setup/setup-plugin-api-DpiaA8z1.cjs +3 -0
  18. package/dist/{src-D7Do7sM5.cjs → .setup/src-BfOCwlHp.cjs} +150 -125
  19. package/dist/{sso-token-store-DTeYxubp.cjs → .setup/sso-token-store-YYLHXgFH.cjs} +1 -1
  20. package/dist/{thread-session-NmA7_njI.cjs → .setup/thread-session-CXgHK-yi.cjs} +1 -1
  21. package/dist/api.cjs +2 -2
  22. package/dist/channel-config-api.cjs +1 -1
  23. package/dist/channel-plugin-api.cjs +1 -1
  24. package/dist/configured-state.cjs +1 -1
  25. package/dist/directory-contract-api.cjs +1 -1
  26. package/dist/doctor-contract-api.cjs +64 -62
  27. package/dist/qa-runner-api.cjs +1 -1
  28. package/dist/runtime-api.cjs +1 -1
  29. package/dist/secret-contract-api.cjs +4 -13
  30. package/dist/setup-entry.cjs +5 -3
  31. package/dist/setup-plugin-api.cjs +1 -1
  32. package/openclaw.plugin.json +1 -1
  33. package/package.json +14 -11
  34. /package/dist/{approval-card-CPNH_q3P.cjs → .setup/approval-card-CPNH_q3P.cjs} +0 -0
  35. /package/dist/{config-schema-D22Lr1Qk.cjs → .setup/config-schema-D22Lr1Qk.cjs} +0 -0
  36. /package/dist/{rolldown-runtime-VH7oDXx4.cjs → .setup/rolldown-runtime-VH7oDXx4.cjs} +0 -0
  37. /package/dist/{runtime-gZTAuFux.cjs → .setup/runtime-gZTAuFux.cjs} +0 -0
  38. /package/dist/{secret-input-C65t6kjM.cjs → .setup/secret-input-C65t6kjM.cjs} +0 -0
@@ -4,7 +4,7 @@ const MSTEAMS_DELEGATED_TOKEN_LEGACY_FILENAME = "msteams-delegated.json";
4
4
  const MSTEAMS_DELEGATED_TOKEN_NAMESPACE = "delegated-token";
5
5
  const MSTEAMS_DELEGATED_TOKEN_KEY = "current";
6
6
  function openDelegatedTokenStore(env) {
7
- return require_runtime.getMSTeamsRuntime().state.openSyncKeyedStore({
7
+ return require_runtime.getMSTeamsRuntime().state.openKeyedStore({
8
8
  namespace: MSTEAMS_DELEGATED_TOKEN_NAMESPACE,
9
9
  maxEntries: 1,
10
10
  overflowPolicy: "reject-new",
@@ -23,13 +23,13 @@ function normalizeMSTeamsDelegatedTokens(value) {
23
23
  ...typeof token.userPrincipalName === "string" ? { userPrincipalName: token.userPrincipalName } : {}
24
24
  };
25
25
  }
26
- function loadMSTeamsDelegatedTokens(env) {
27
- return normalizeMSTeamsDelegatedTokens(openDelegatedTokenStore(env).lookup("current")) ?? void 0;
26
+ async function loadMSTeamsDelegatedTokens(env) {
27
+ return normalizeMSTeamsDelegatedTokens(await openDelegatedTokenStore(env).lookup("current")) ?? void 0;
28
28
  }
29
- function saveMSTeamsDelegatedTokens(tokens, env) {
29
+ async function saveMSTeamsDelegatedTokens(tokens, env) {
30
30
  const normalized = normalizeMSTeamsDelegatedTokens(tokens);
31
31
  if (!normalized) throw new Error("Invalid Microsoft Teams delegated token payload");
32
- openDelegatedTokenStore(env).register(MSTEAMS_DELEGATED_TOKEN_KEY, normalized);
32
+ await openDelegatedTokenStore(env).register(MSTEAMS_DELEGATED_TOKEN_KEY, normalized);
33
33
  }
34
34
  //#endregion
35
35
  Object.defineProperty(exports, "MSTEAMS_DELEGATED_TOKEN_KEY", {
@@ -1,26 +1,30 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
2
2
  const require_runtime = require("./runtime-gZTAuFux.cjs");
3
- require("./runtime-api.cjs");
4
- const require_resolve_allowlist = require("./resolve-allowlist-DmUTQxEz.cjs");
5
- const require_channel_setup = require("./channel.setup-BhxokviI.cjs");
6
- const require_polls = require("./polls-BBdZPx_R.cjs");
3
+ require("../runtime-api.cjs");
4
+ const require_resolve_allowlist = require("./resolve-allowlist-CHzaaWiC.cjs");
5
+ const require_channel_setup = require("./channel.setup-BVDfgdCc.cjs");
6
+ const require_polls = require("./polls-BJ4-4uZm.cjs");
7
7
  let openclaw_plugin_sdk_lazy_runtime = require("openclaw/plugin-sdk/lazy-runtime");
8
8
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
9
+ let openclaw_plugin_sdk_fetch_runtime = require("openclaw/plugin-sdk/fetch-runtime");
9
10
  let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-http");
10
11
  let openclaw_plugin_sdk_reply_payload = require("openclaw/plugin-sdk/reply-payload");
12
+ let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
11
13
  let openclaw_plugin_sdk_text_chunking = require("openclaw/plugin-sdk/text-chunking");
12
14
  let node_dns_promises = require("node:dns/promises");
13
15
  let openclaw_plugin_sdk_ssrf_policy = require("openclaw/plugin-sdk/ssrf-policy");
14
16
  let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text-utility-runtime");
17
+ let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
15
18
  let openclaw_plugin_sdk_retry_runtime = require("openclaw/plugin-sdk/retry-runtime");
16
19
  let node_crypto = require("node:crypto");
17
20
  node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
18
21
  let node_path = require("node:path");
19
22
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
20
23
  let openclaw_plugin_sdk_runtime_env = require("openclaw/plugin-sdk/runtime-env");
24
+ let openclaw_plugin_sdk_markdown_table_runtime = require("openclaw/plugin-sdk/markdown-table-runtime");
21
25
  let openclaw_plugin_sdk_blob_runtime = require("openclaw/plugin-sdk/blob-runtime");
22
26
  let openclaw_plugin_sdk_global_singleton = require("openclaw/plugin-sdk/global-singleton");
23
- let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
27
+ let openclaw_plugin_sdk_expect_runtime = require("openclaw/plugin-sdk/expect-runtime");
24
28
  let openclaw_plugin_sdk_reply_chunking = require("openclaw/plugin-sdk/reply-chunking");
25
29
  let openclaw_plugin_sdk_web_media = require("openclaw/plugin-sdk/web-media");
26
30
  let openclaw_plugin_sdk_media_runtime = require("openclaw/plugin-sdk/media-runtime");
@@ -475,18 +479,16 @@ function prepareFileConsentActivity(params) {
475
479
  */
476
480
  async function prepareFileConsentActivityFs(params) {
477
481
  const { media, conversationId, description } = params;
478
- const uploadId = storePendingUpload({
482
+ const upload = {
479
483
  buffer: media.buffer,
480
484
  filename: media.filename,
481
485
  contentType: media.contentType,
482
486
  conversationId
483
- });
487
+ };
488
+ const uploadId = storePendingUpload(upload);
484
489
  await storePendingUploadFs({
485
490
  id: uploadId,
486
- buffer: media.buffer,
487
- filename: media.filename,
488
- contentType: media.contentType,
489
- conversationId
491
+ ...upload
490
492
  });
491
493
  return {
492
494
  activity: buildConsentActivity({
@@ -497,10 +499,6 @@ async function prepareFileConsentActivityFs(params) {
497
499
  uploadId
498
500
  };
499
501
  }
500
- /**
501
- * Check if a file requires FileConsentCard flow.
502
- * True for: personal chat AND (large file OR non-image)
503
- */
504
502
  function requiresFileConsent(params) {
505
503
  const isPersonal = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(params.conversationType) === "personal";
506
504
  const isImage = params.contentType?.startsWith("image/") ?? false;
@@ -635,6 +633,7 @@ function serializeMarkdownDestination(href) {
635
633
  return `<${href.replace(/([\\<>])/gu, "\\$1")}>`;
636
634
  }
637
635
  function blankBlockEnd(text, index) {
636
+ if (text[index] !== "\n" && text[index] !== "\r") return;
638
637
  const match = /^(?:\r?\n)[ \t]*(?:\r?\n)/u.exec(text.slice(index));
639
638
  return match ? index + match[0].length : void 0;
640
639
  }
@@ -692,136 +691,25 @@ function protectDelimitedMarkdown(text, opener, tokenPrefix, values) {
692
691
  }
693
692
  return protectedText + text.slice(cursor);
694
693
  }
695
- function parseQuotePrefix(line) {
696
- let cursor = 0;
697
- let depth = 0;
698
- while (cursor < line.length) {
699
- const checkpoint = cursor;
700
- let spaces = 0;
701
- while (spaces < 3 && line[cursor] === " ") {
702
- cursor += 1;
703
- spaces += 1;
704
- }
705
- if (line[cursor] !== ">") {
706
- cursor = checkpoint;
707
- break;
708
- }
709
- cursor += 1;
710
- depth += 1;
711
- if (line[cursor] === " " || line[cursor] === " ") cursor += 1;
712
- }
713
- return {
714
- content: line.slice(cursor).replace(/\r$/u, ""),
715
- depth,
716
- prefix: line.slice(0, cursor)
717
- };
718
- }
719
- function isTableDelimiterLine(content) {
720
- const cells = content.trim().replace(/^\|/u, "").replace(/\|$/u, "").split("|").map((cell) => cell.trim());
721
- return cells.length > 0 && cells.every((cell) => /^:?-+:?$/u.test(cell));
722
- }
723
- function protectRawTablesInSegment(text, tokenPrefix, rawTables) {
724
- const lines = text.split("\n");
725
- const output = [];
726
- for (let index = 0; index < lines.length;) {
727
- const line = lines[index] ?? "";
728
- const header = parseQuotePrefix(line);
729
- const delimiter = parseQuotePrefix(lines[index + 1] ?? "");
730
- if (header.content.includes("|") && delimiter.depth === header.depth && isTableDelimiterLine(delimiter.content)) {
731
- let end = index + 2;
732
- while (end < lines.length) {
733
- const row = parseQuotePrefix(lines[end] ?? "");
734
- if (row.depth !== header.depth || !row.content.trim() || isInterruptingBlock(lines[end] ?? "")) break;
735
- end += 1;
736
- }
737
- const table = lines.slice(index, end).join("\n");
738
- if ((0, openclaw_plugin_sdk_text_chunking.convertMarkdownTables)(table, "code") !== table) {
739
- const tableIndex = rawTables.push(table.slice(header.prefix.length)) - 1;
740
- output.push(`${header.prefix}${tokenPrefix}t${tableIndex}${TOKEN_END}`);
741
- index = end;
742
- continue;
743
- }
744
- for (let lineIndex = index; lineIndex < end; lineIndex += 1) output.push(lines[lineIndex] ?? "");
745
- index = end;
746
- continue;
747
- }
748
- output.push(line);
749
- index += 1;
750
- }
751
- return output.join("\n");
752
- }
753
- function isInterruptingBlock(line) {
754
- const content = parseQuotePrefix(line).content;
755
- return /^[ \t]{0,3}(?:#{1,6}(?:[ \t]|$)|`{3,}|~{3,}|(?:[-+*]|\d+[.)])[ \t]+)/u.test(content);
756
- }
757
- function leadingQuoteDepth(line) {
758
- return parseQuotePrefix(line).depth;
759
- }
760
- function parseFenceLine(line) {
761
- const match = /^((?: {0,3}>[ \t]?)*)(?:((?:[-+*]|\d+[.)])[ \t]+))?( {0,3})(`{3,}|~{3,})(.*)$/u.exec(line);
762
- const marker = match?.[4];
763
- const trailing = match?.[5] ?? "";
764
- if (!marker || marker.startsWith("`") && trailing.includes("`")) return;
765
- return {
766
- marker,
767
- quoteDepth: match?.[1]?.match(/>/gu)?.length ?? 0,
768
- trailing,
769
- listIndent: match?.[2]?.length ?? 0,
770
- indent: match?.[3]?.length ?? 0
771
- };
772
- }
773
- function protectRawTablesOutsideFences(text, tokenPrefix, rawTables) {
694
+ function protectRawTables(text, tokenPrefix, rawTables) {
695
+ if (!text.includes("|")) return text;
696
+ const { tables } = (0, openclaw_plugin_sdk_text_chunking.markdownToIRWithMeta)(text, {
697
+ autolink: false,
698
+ linkify: false,
699
+ tableMode: "block"
700
+ });
774
701
  let result = "";
775
- let outsideStart = 0;
776
- let fenceStart;
777
- let active;
778
- let listContextIndent = 0;
779
- let offset = 0;
780
- while (offset <= text.length) {
781
- const nextNewline = text.indexOf("\n", offset);
782
- const lineEnd = nextNewline < 0 ? text.length : nextNewline;
783
- const line = text.slice(offset, lineEnd).replace(/\r$/u, "");
784
- let fence = parseFenceLine(line);
785
- const lineQuoteDepth = leadingQuoteDepth(line);
786
- const lineWithoutQuotes = line.replace(/^(?:[ \t]*>[ \t]?)+/u, "");
787
- const lineIndent = /^[ \t]*/u.exec(lineWithoutQuotes)?.[0].length ?? 0;
788
- const listMarkerIndent = /^((?:[-+*]|\d+[.)])[ \t]+)/u.exec(lineWithoutQuotes)?.[1]?.length;
789
- if (listMarkerIndent) listContextIndent = listMarkerIndent;
790
- else if (line.trim() && lineIndent < listContextIndent && !fence) listContextIndent = 0;
791
- if (fence && fence.listIndent === 0 && listContextIndent > 0 && fence.indent >= listContextIndent) fence = {
792
- ...fence,
793
- listIndent: listContextIndent
794
- };
795
- const listOutdented = Boolean(active?.listIndent && line.trim() && lineIndent < active.listIndent && !fence);
796
- if (active && (active.quoteDepth > lineQuoteDepth || listOutdented)) {
797
- result += text.slice(fenceStart, offset);
798
- outsideStart = offset;
799
- active = void 0;
800
- fenceStart = void 0;
801
- }
802
- if (!active && fence) {
803
- result += protectRawTablesInSegment(text.slice(outsideStart, offset), tokenPrefix, rawTables);
804
- fenceStart = offset;
805
- active = {
806
- marker: fence.marker,
807
- quoteDepth: fence.quoteDepth,
808
- listIndent: fence.listIndent
809
- };
810
- } else if (active && fence && fence.marker[0] === active.marker[0] && fence.marker.length >= active.marker.length && fence.quoteDepth === active.quoteDepth && /^[ \t]*$/u.test(fence.trailing)) {
811
- const fenceEnd = nextNewline < 0 ? lineEnd : nextNewline + 1;
812
- result += text.slice(fenceStart, fenceEnd);
813
- outsideStart = fenceEnd;
814
- active = void 0;
815
- fenceStart = void 0;
816
- }
817
- if (nextNewline < 0) break;
818
- offset = nextNewline + 1;
819
- }
820
- if (active && fenceStart !== void 0) {
821
- result += text.slice(fenceStart);
822
- return result;
702
+ let cursor = 0;
703
+ for (const table of tables) {
704
+ const source = (0, openclaw_plugin_sdk_expect_runtime.expectDefined)((0, openclaw_plugin_sdk_markdown_table_runtime.getMarkdownTableSource)(table), "Markdown table source");
705
+ const lineStart = Math.max(text.lastIndexOf("\n", source.start - 1), text.lastIndexOf("\r", source.start - 1)) + 1;
706
+ const start = lineStart + (/^(?: {0,3}>[ \t]?)*/u.exec(text.slice(lineStart, source.start))?.[0] ?? "").length;
707
+ const end = source.end + (text.slice(source.end, source.end + 2) === "\r\n" ? 1 : 0);
708
+ const index = rawTables.push(text.slice(start, end)) - 1;
709
+ result += text.slice(cursor, start) + `${tokenPrefix}t${index}${TOKEN_END}`;
710
+ cursor = end;
823
711
  }
824
- return result + protectRawTablesInSegment(text.slice(outsideStart), tokenPrefix, rawTables);
712
+ return result + text.slice(cursor);
825
713
  }
826
714
  function protectMSTeamsCode(ir, tokenPrefix, code, protectedValues) {
827
715
  const codeSpans = ir.styles.filter((span) => span.style === "code" || span.style === "code_block");
@@ -877,7 +765,7 @@ function formatMSTeamsMarkdown(markdown, tableMode) {
877
765
  return `${tokenPrefix}h${index}${TOKEN_END}`;
878
766
  }), "![", `${tokenPrefix}i`, images), "@[", `${tokenPrefix}m`, mentions);
879
767
  const tableInput = (0, openclaw_plugin_sdk_text_chunking.convertMarkdownTables)(mentionsProtected, tableMode);
880
- const protectedMarkdown = (tableMode === "off" ? protectRawTablesOutsideFences(tableInput, tokenPrefix, rawTables) : tableInput).replace(ESCAPED_MARKDOWN_RE, (escaped) => {
768
+ const protectedMarkdown = (tableMode === "off" ? protectRawTables(tableInput, tokenPrefix, rawTables) : tableInput).replace(ESCAPED_MARKDOWN_RE, (escaped) => {
881
769
  const index = escapedMarkdown.push(escaped) - 1;
882
770
  return `${tokenPrefix}e${index}${TOKEN_END}`;
883
771
  });
@@ -957,6 +845,19 @@ function buildTeamsFileInfoCard(file) {
957
845
  const GRAPH_ROOT = "https://graph.microsoft.com/v1.0";
958
846
  const GRAPH_BETA = "https://graph.microsoft.com/beta";
959
847
  const GRAPH_SCOPE = "https://graph.microsoft.com";
848
+ async function requestSharePoint(url, init, params) {
849
+ const { response, release } = await (0, openclaw_plugin_sdk_ssrf_runtime.fetchWithSsrFGuard)({
850
+ url,
851
+ init,
852
+ fetchImpl: params.fetchFn,
853
+ mode: "trusted_env_proxy",
854
+ beforeRequest: () => require_resolve_allowlist.assertMSTeamsSendHandoff(params),
855
+ maxRedirects: 20,
856
+ allowCrossOriginUnsafeRedirectReplay: true,
857
+ auditContext: "msteams.graph-upload"
858
+ });
859
+ return (0, openclaw_plugin_sdk_fetch_runtime.responseWithRelease)(response, release);
860
+ }
960
861
  function requireMSTeamsSharePointSiteId(siteId) {
961
862
  const normalized = siteId?.trim();
962
863
  if (!normalized) throw new Error("channels.msteams.sharePointSiteId is required to send files to group chats or channels");
@@ -978,7 +879,6 @@ async function getGraphAccessToken(tokenProvider) {
978
879
  * @param params.siteId - SharePoint site ID (e.g., "contoso.sharepoint.com,guid1,guid2")
979
880
  */
980
881
  async function uploadToSharePoint(params) {
981
- const fetchFn = params.fetchFn ?? fetch;
982
882
  const uploadPath = `/OpenClawShared/${encodeURIComponent(params.filename)}`;
983
883
  const uploadUrl = `${GRAPH_ROOT}/sites/${params.siteId}/drive/root:${uploadPath}:/content?@microsoft.graph.conflictBehavior=rename`;
984
884
  const timeoutMs = require_resolve_allowlist.resolveMSTeamsSharePointUploadTimeoutMs(params.buffer.length);
@@ -986,8 +886,10 @@ async function uploadToSharePoint(params) {
986
886
  label: SHAREPOINT_UPLOAD_TIMEOUT_LABEL,
987
887
  timeoutMs,
988
888
  work: async (signal) => {
889
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
989
890
  const token = await getGraphAccessToken(params.tokenProvider);
990
- const res = await fetchFn(uploadUrl, {
891
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
892
+ const res = await requestSharePoint(uploadUrl, {
991
893
  method: "PUT",
992
894
  headers: {
993
895
  "User-Agent": require_resolve_allowlist.buildUserAgent(),
@@ -996,7 +898,7 @@ async function uploadToSharePoint(params) {
996
898
  },
997
899
  body: new Blob([(0, openclaw_plugin_sdk_blob_runtime.bufferToBlobPart)(params.buffer)]),
998
900
  signal
999
- });
901
+ }, params);
1000
902
  if (!res.ok) throw await require_resolve_allowlist.createMSTeamsHttpError(res, "SharePoint upload failed");
1001
903
  return await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(res, "msteams.graph-upload.uploadSharePointFile", { chunkTimeoutMs: timeoutMs });
1002
904
  }
@@ -1016,18 +918,19 @@ async function uploadToSharePoint(params) {
1016
918
  * @param params.itemId - The driveItem ID (returned from upload)
1017
919
  */
1018
920
  async function getDriveItemProperties(params) {
1019
- const fetchFn = params.fetchFn ?? fetch;
1020
921
  const data = await require_resolve_allowlist.withMSTeamsAbortableRequestTimeout({
1021
922
  label: SHAREPOINT_REQUEST_TIMEOUT_LABEL,
1022
923
  work: async (signal) => {
924
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
1023
925
  const token = await getGraphAccessToken(params.tokenProvider);
1024
- const res = await fetchFn(`${GRAPH_ROOT}/sites/${params.siteId}/drive/items/${params.itemId}?$select=eTag,webDavUrl,name`, {
926
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
927
+ const res = await requestSharePoint(`${GRAPH_ROOT}/sites/${params.siteId}/drive/items/${params.itemId}?$select=eTag,webDavUrl,name`, {
1025
928
  headers: {
1026
929
  "User-Agent": require_resolve_allowlist.buildUserAgent(),
1027
930
  Authorization: `Bearer ${token}`
1028
931
  },
1029
932
  signal
1030
- });
933
+ }, params);
1031
934
  if (!res.ok) throw await require_resolve_allowlist.createMSTeamsHttpError(res, "Get driveItem properties failed");
1032
935
  return await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(res, "msteams.graph-upload.getDriveItemProperties");
1033
936
  }
@@ -1044,18 +947,19 @@ async function getDriveItemProperties(params) {
1044
947
  * Used to create sharing links scoped to only the chat participants.
1045
948
  */
1046
949
  async function getChatMembers(params) {
1047
- const fetchFn = params.fetchFn ?? fetch;
1048
950
  return await require_resolve_allowlist.withMSTeamsAbortableRequestTimeout({
1049
951
  label: SHAREPOINT_REQUEST_TIMEOUT_LABEL,
1050
952
  work: async (signal) => {
953
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
1051
954
  const token = await getGraphAccessToken(params.tokenProvider);
1052
- const res = await fetchFn(`${GRAPH_ROOT}/chats/${params.chatId}/members`, {
955
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
956
+ const res = await requestSharePoint(`${GRAPH_ROOT}/chats/${params.chatId}/members`, {
1053
957
  headers: {
1054
958
  "User-Agent": require_resolve_allowlist.buildUserAgent(),
1055
959
  Authorization: `Bearer ${token}`
1056
960
  },
1057
961
  signal
1058
- });
962
+ }, params);
1059
963
  if (!res.ok) {
1060
964
  const message = res.status === 403 ? "Get chat members failed; verify Graph chat-member permissions and tenant access policies" : "Get chat members failed";
1061
965
  throw await require_resolve_allowlist.createMSTeamsHttpError(res, message);
@@ -1070,7 +974,6 @@ async function getChatMembers(params) {
1070
974
  * For per-user scope, uses beta API with recipients.
1071
975
  */
1072
976
  async function createSharePointSharingLink(params) {
1073
- const fetchFn = params.fetchFn ?? fetch;
1074
977
  const scope = params.scope ?? "organization";
1075
978
  const apiRoot = scope === "users" ? GRAPH_BETA : GRAPH_ROOT;
1076
979
  const body = {
@@ -1081,8 +984,10 @@ async function createSharePointSharingLink(params) {
1081
984
  const data = await require_resolve_allowlist.withMSTeamsAbortableRequestTimeout({
1082
985
  label: SHAREPOINT_REQUEST_TIMEOUT_LABEL,
1083
986
  work: async (signal) => {
987
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
1084
988
  const token = await getGraphAccessToken(params.tokenProvider);
1085
- const res = await fetchFn(`${apiRoot}/sites/${params.siteId}/drive/items/${params.itemId}/createLink`, {
989
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
990
+ const res = await requestSharePoint(`${apiRoot}/sites/${params.siteId}/drive/items/${params.itemId}/createLink`, {
1086
991
  method: "POST",
1087
992
  headers: {
1088
993
  "User-Agent": require_resolve_allowlist.buildUserAgent(),
@@ -1091,7 +996,7 @@ async function createSharePointSharingLink(params) {
1091
996
  },
1092
997
  body: JSON.stringify(body),
1093
998
  signal
1094
- });
999
+ }, params);
1095
1000
  if (!res.ok) throw await require_resolve_allowlist.createMSTeamsHttpError(res, "Create SharePoint sharing link failed");
1096
1001
  return await (0, openclaw_plugin_sdk_provider_http.readProviderJsonResponse)(res, "msteams.graph-upload.createSharePointSharingLink");
1097
1002
  }
@@ -1116,7 +1021,8 @@ async function uploadAndShareSharePoint(params) {
1116
1021
  contentType: params.contentType,
1117
1022
  tokenProvider: params.tokenProvider,
1118
1023
  siteId: params.siteId,
1119
- fetchFn: params.fetchFn
1024
+ fetchFn: params.fetchFn,
1025
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
1120
1026
  });
1121
1027
  let scope = "organization";
1122
1028
  let recipientObjectIds;
@@ -1124,7 +1030,8 @@ async function uploadAndShareSharePoint(params) {
1124
1030
  const members = await getChatMembers({
1125
1031
  chatId: params.chatId,
1126
1032
  tokenProvider: params.tokenProvider,
1127
- fetchFn: params.fetchFn
1033
+ fetchFn: params.fetchFn,
1034
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
1128
1035
  });
1129
1036
  if (members.length === 0) throw new Error("MS Teams chat member lookup returned no recipients");
1130
1037
  scope = "users";
@@ -1136,7 +1043,8 @@ async function uploadAndShareSharePoint(params) {
1136
1043
  tokenProvider: params.tokenProvider,
1137
1044
  scope,
1138
1045
  recipientObjectIds,
1139
- fetchFn: params.fetchFn
1046
+ fetchFn: params.fetchFn,
1047
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
1140
1048
  });
1141
1049
  return {
1142
1050
  itemId: uploaded.id,
@@ -1236,13 +1144,13 @@ function isValidTeamsId(id) {
1236
1144
  * Returns both the formatted text with <at> tags and the entities array.
1237
1145
  */
1238
1146
  function parseMentions(text) {
1239
- const mentionPattern = /@\[([^\]]+)\]\(([^)]+)\)/g;
1147
+ const mentionPattern = /@\[((?:\\[\s\S]|[^\]\\])+)\]\(([^)]+)\)/g;
1240
1148
  const entities = [];
1241
1149
  return {
1242
1150
  text: text.replace(mentionPattern, (match, name, id) => {
1243
1151
  const trimmedId = id.trim();
1244
1152
  if (!isValidTeamsId(trimmedId)) return match;
1245
- const trimmedName = name.trim();
1153
+ const trimmedName = name.replace(/\\([\\[\]])/g, "$1").trim();
1246
1154
  const mentionTag = `<at>${trimmedName}</at>`;
1247
1155
  entities.push({
1248
1156
  type: "mention",
@@ -1390,24 +1298,28 @@ function mergeReferenceIntoActivity(activity, ref) {
1390
1298
  };
1391
1299
  }
1392
1300
  async function sendMSTeamsActivityWithReference(app, source, activity, options) {
1393
- const ref = buildSdkConversationReference(source, options);
1394
- const activities = (await getApiClientForReference(app, ref)).conversations.activities(ref.conversation.id);
1395
- const activityWithRef = mergeReferenceIntoActivity(options?.quoteActivityId ? await quoteMSTeamsActivity(activity, options.quoteActivityId) : activity, ref);
1396
- const isTargeted = activityWithRef.recipient?.isTargeted === true;
1397
- if (isTargeted && ref.conversation.conversationType === "personal") throw new Error("Targeted messages are not supported in 1:1 (personal) chats.");
1398
- const activityId = typeof activityWithRef.id === "string" ? activityWithRef.id : void 0;
1399
- if (activityId) {
1400
- const res = isTargeted && activities.updateTargeted ? await activities.updateTargeted(activityId, activityWithRef) : await activities.update(activityId, activityWithRef);
1301
+ return require_resolve_allowlist.withMSTeamsConnectorHandoff(options ?? {}, async (handoff) => {
1302
+ require_resolve_allowlist.assertMSTeamsSendHandoff(handoff);
1303
+ const ref = buildSdkConversationReference(source, options);
1304
+ const activities = (await getApiClientForReference(app, ref)).conversations.activities(ref.conversation.id);
1305
+ const activityWithRef = mergeReferenceIntoActivity(options?.quoteActivityId ? await quoteMSTeamsActivity(activity, options.quoteActivityId) : activity, ref);
1306
+ const isTargeted = activityWithRef.recipient?.isTargeted === true;
1307
+ if (isTargeted && ref.conversation.conversationType === "personal") throw new Error("Targeted messages are not supported in 1:1 (personal) chats.");
1308
+ const activityId = typeof activityWithRef.id === "string" ? activityWithRef.id : void 0;
1309
+ require_resolve_allowlist.assertMSTeamsSendHandoff(handoff);
1310
+ if (activityId) {
1311
+ const res = isTargeted && activities.updateTargeted ? await activities.updateTargeted(activityId, activityWithRef) : await activities.update(activityId, activityWithRef);
1312
+ return {
1313
+ ...activityWithRef,
1314
+ ...res && typeof res === "object" ? res : {}
1315
+ };
1316
+ }
1317
+ const res = isTargeted && activities.createTargeted ? await activities.createTargeted(activityWithRef) : await activities.create(activityWithRef);
1401
1318
  return {
1402
1319
  ...activityWithRef,
1403
- ...res && typeof res === "object" ? res : {}
1320
+ ...res
1404
1321
  };
1405
- }
1406
- const res = isTargeted && activities.createTargeted ? await activities.createTargeted(activityWithRef) : await activities.create(activityWithRef);
1407
- return {
1408
- ...activityWithRef,
1409
- ...res
1410
- };
1322
+ });
1411
1323
  }
1412
1324
  async function updateMSTeamsActivityWithReference(app, source, activityId, activity, options) {
1413
1325
  const ref = buildSdkConversationReference(source, options);
@@ -1566,6 +1478,7 @@ async function buildActivity(msg, conversationRef, tokenProvider, sharePointSite
1566
1478
  thresholdBytes: FILE_CONSENT_THRESHOLD_BYTES
1567
1479
  })) {
1568
1480
  const conversationId = conversationRef.conversation?.id ?? "unknown";
1481
+ require_resolve_allowlist.assertMSTeamsSendHandoff(options);
1569
1482
  const { activity: consentActivity, uploadId } = prepareFileConsentActivity({
1570
1483
  media: {
1571
1484
  buffer: media.buffer,
@@ -1582,17 +1495,20 @@ async function buildActivity(msg, conversationRef, tokenProvider, sharePointSite
1582
1495
  const siteId = requireMSTeamsSharePointSiteId(sharePointSiteId);
1583
1496
  if (!tokenProvider) throw new Error("MS Teams Graph token provider unavailable for SharePoint file send");
1584
1497
  const chatId = conversationRef.conversation?.id;
1498
+ const uploaded = await uploadAndShareSharePoint({
1499
+ assertDirectAdapterHandoff: options?.assertDirectAdapterHandoff,
1500
+ buffer: media.buffer,
1501
+ filename: fileName,
1502
+ contentType,
1503
+ tokenProvider,
1504
+ siteId,
1505
+ chatId: chatId ?? void 0,
1506
+ usePerUserSharing: conversationType === "groupchat"
1507
+ });
1585
1508
  activity.attachments = [buildTeamsFileInfoCard(await getDriveItemProperties({
1509
+ assertDirectAdapterHandoff: options?.assertDirectAdapterHandoff,
1586
1510
  siteId,
1587
- itemId: (await uploadAndShareSharePoint({
1588
- buffer: media.buffer,
1589
- filename: fileName,
1590
- contentType,
1591
- tokenProvider,
1592
- siteId,
1593
- chatId: chatId ?? void 0,
1594
- usePerUserSharing: conversationType === "groupchat"
1595
- })).itemId,
1511
+ itemId: uploaded.itemId,
1596
1512
  tokenProvider
1597
1513
  }))];
1598
1514
  return activity;
@@ -1640,9 +1556,14 @@ async function sendMSTeamsMessages(params) {
1640
1556
  let response;
1641
1557
  try {
1642
1558
  response = await sendWithRetry(async () => {
1643
- activity ??= await buildActivity(message, params.conversationRef, params.tokenProvider, params.sharePointSiteId, params.mediaMaxBytes, { feedbackLoopEnabled: params.feedbackLoopEnabled });
1559
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
1560
+ activity ??= await buildActivity(message, params.conversationRef, params.tokenProvider, params.sharePointSiteId, params.mediaMaxBytes, {
1561
+ feedbackLoopEnabled: params.feedbackLoopEnabled,
1562
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
1563
+ });
1644
1564
  pendingUploadId ??= typeof activity["_pendingUploadId"] === "string" ? activity["_pendingUploadId"] : void 0;
1645
1565
  delete activity["_pendingUploadId"];
1566
+ require_resolve_allowlist.assertMSTeamsSendHandoff(params);
1646
1567
  providerDispatchStarted = true;
1647
1568
  return await sendFn(activity);
1648
1569
  }, {
@@ -1650,10 +1571,11 @@ async function sendMSTeamsMessages(params) {
1650
1571
  messageCount: messages.length
1651
1572
  });
1652
1573
  } catch (error) {
1653
- if (!providerDispatchStarted) throw new openclaw_plugin_sdk_error_runtime.PlatformMessageNotDispatchedError(error instanceof Error ? error.message : "Teams activity preparation failed", { cause: error });
1574
+ if (!providerDispatchStarted && !(error instanceof openclaw_plugin_sdk_error_runtime.PlatformMessageNotDispatchedError)) throw new openclaw_plugin_sdk_error_runtime.PlatformMessageNotDispatchedError(error instanceof Error ? error.message : "Teams activity preparation failed", { cause: error });
1654
1575
  throw error;
1655
1576
  }
1656
1577
  const messageId = extractMessageId(response) ?? "unknown";
1578
+ await params.onMessageSent?.(messageId, messageIndex);
1657
1579
  if (pendingUploadId && messageId !== "unknown") setPendingUploadActivityId(pendingUploadId, messageId);
1658
1580
  return messageId;
1659
1581
  };
@@ -1672,6 +1594,8 @@ async function sendMSTeamsMessages(params) {
1672
1594
  }
1673
1595
  const isChannel = params.conversationRef.conversation?.conversationType === "channel";
1674
1596
  const sendFn = (activity) => sendMSTeamsActivityWithReference(params.app, baseRef, activity, {
1597
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff,
1598
+ onPlatformSendDispatch: params.onPlatformSendDispatch,
1675
1599
  threadActivityId: isChannel ? threadActivityId : void 0,
1676
1600
  serviceUrlBoundary: params.serviceUrlBoundary
1677
1601
  });
@@ -1681,7 +1605,7 @@ async function sendMSTeamsMessages(params) {
1681
1605
  if (params.replyStyle === "thread") {
1682
1606
  const ctx = params.context;
1683
1607
  if (!ctx) return await sendProactively(messages, 0, resolvedThreadId);
1684
- const sendFn = ctx.sendActivity;
1608
+ const sendFn = (activity) => require_resolve_allowlist.withMSTeamsConnectorHandoff(params, () => ctx.sendActivity(activity));
1685
1609
  const messageIds = [];
1686
1610
  for (const [idx, message] of messages.entries()) {
1687
1611
  const result = await withRevokedProxyFallback({
@@ -1,4 +1,4 @@
1
- const require_resolve_allowlist = require("./resolve-allowlist-DmUTQxEz.cjs");
1
+ const require_resolve_allowlist = require("./resolve-allowlist-CHzaaWiC.cjs");
2
2
  let openclaw_plugin_sdk_runtime_env = require("openclaw/plugin-sdk/runtime-env");
3
3
  let openclaw_plugin_sdk_provider_auth = require("openclaw/plugin-sdk/provider-auth");
4
4
  let openclaw_plugin_sdk_provider_auth_runtime = require("openclaw/plugin-sdk/provider-auth-runtime");
@@ -224,7 +224,6 @@ const MSTEAMS_SQLITE_MAX_POLL_ROWS = 2e3;
224
224
  const MSTEAMS_POLL_VOTE_BUCKET_COUNT = 32;
225
225
  const MSTEAMS_MAX_POLL_VOTE_BUCKET_ROWS = 1001 * MSTEAMS_POLL_VOTE_BUCKET_COUNT;
226
226
  const MSTEAMS_POLL_TTL_MS = 2592e6;
227
- const POLL_MUTATION_KEY = "polls";
228
227
  function normalizeChoiceValue(value) {
229
228
  if (typeof value === "string") {
230
229
  const trimmed = value.trim();
@@ -404,8 +403,11 @@ function createMSTeamsPollStoreState(params) {
404
403
  for (const row of await voteBucketStore.entries()) if (row.value.pollId === pollId) Object.assign(votes, row.value.votes);
405
404
  return votes;
406
405
  };
407
- const deletePollVotes = async (pollId) => {
408
- for (const row of await voteBucketStore.entries()) if (row.value.pollId === pollId) await voteBucketStore.delete(row.key);
406
+ const deletePollVotes = async (pollId, cachedRows) => {
407
+ for (const row of cachedRows?.values() ?? await voteBucketStore.entries()) if (row.value.pollId === pollId) {
408
+ await voteBucketStore.delete(row.key);
409
+ cachedRows?.delete(row.key);
410
+ }
409
411
  };
410
412
  const registerPollVotes = async (pollId, votes, updatedAt) => {
411
413
  const buckets = /* @__PURE__ */ new Map();
@@ -450,11 +452,16 @@ function createMSTeamsPollStoreState(params) {
450
452
  };
451
453
  };
452
454
  const prunePollStoreToLimit = async () => {
455
+ let voteRows;
456
+ const deletePrunedPollVotes = async (pollId) => {
457
+ voteRows ??= new Map((await voteBucketStore.entries()).map((row) => [row.key, row]));
458
+ await deletePollVotes(pollId, voteRows);
459
+ };
453
460
  const rows = [];
454
461
  for (const row of await pollStore.entries()) {
455
462
  if (!pruneExpired({ [row.key]: row.value })[row.key]) {
456
463
  await pollStore.delete(row.key);
457
- await deletePollVotes(row.value.id);
464
+ await deletePrunedPollVotes(row.value.id);
458
465
  continue;
459
466
  }
460
467
  rows.push(row);
@@ -465,11 +472,11 @@ function createMSTeamsPollStoreState(params) {
465
472
  });
466
473
  for (const row of sorted.slice(0, rows.length - MSTEAMS_MAX_POLLS)) {
467
474
  await pollStore.delete(row.key);
468
- await deletePollVotes(row.value.id);
475
+ await deletePrunedPollVotes(row.value.id);
469
476
  }
470
477
  };
471
478
  const createPoll = async (poll) => {
472
- await withMSTeamsSqliteMutationLock(params, POLL_MUTATION_KEY, async () => {
479
+ await withMSTeamsSqliteMutationLock(params, MSTEAMS_POLLS_NAMESPACE, async () => {
473
480
  const { metadata, votes } = splitMSTeamsPoll(poll);
474
481
  await pollStore.register(buildMSTeamsPollStateKey(poll.id), toPluginJsonValue(metadata));
475
482
  await deletePollVotes(poll.id);
@@ -484,7 +491,7 @@ function createMSTeamsPollStoreState(params) {
484
491
  return await reconstructPoll(poll);
485
492
  };
486
493
  const recordVote = async (vote) => {
487
- return await withMSTeamsSqliteMutationLock(params, POLL_MUTATION_KEY, async () => {
494
+ return await withMSTeamsSqliteMutationLock(params, MSTEAMS_POLLS_NAMESPACE, async () => {
488
495
  const pollKey = buildMSTeamsPollStateKey(vote.pollId);
489
496
  const poll = await pollStore.lookup(pollKey);
490
497
  if (!poll) return null;
@@ -1,4 +1,4 @@
1
- const require_resolve_allowlist = require("./resolve-allowlist-DmUTQxEz.cjs");
1
+ const require_resolve_allowlist = require("./resolve-allowlist-CHzaaWiC.cjs");
2
2
  let openclaw_plugin_sdk_html_entity_runtime = require("openclaw/plugin-sdk/html-entity-runtime");
3
3
  //#region extensions/msteams/src/graph-thread.ts
4
4
  /**