@openclaw/msteams 2026.11.3 → 2026.11.5-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.
Files changed (32) hide show
  1. package/dist/.setup/{adapter.runtime-B-6Q3nMI.cjs → adapter.runtime-BB38Rdj_.cjs} +20 -23
  2. package/dist/.setup/{approval-card-DWPDItvq.cjs → approval-card-BHAKsdAM.cjs} +5 -9
  3. package/dist/.setup/{approval-handler.runtime-D0jbDypb.cjs → approval-handler.runtime-C16UvawQ.cjs} +4 -4
  4. package/dist/.setup/{channel-Ci6K9Njb.cjs → channel-BaTw0JW3.cjs} +35 -254
  5. package/dist/.setup/{channel.runtime-DeHVAVxL.cjs → channel.runtime-Ch2M_-jN.cjs} +65 -107
  6. package/dist/.setup/{channel.setup-BN3uhq3r.cjs → channel.setup-B9yKGEse.cjs} +161 -262
  7. package/dist/.setup/{config-schema-CkKdphSp.cjs → config-schema-3lVZ45cB.cjs} +120 -125
  8. package/dist/.setup/{conversation-store-state-B-4g4uq5.cjs → conversation-store-state-CVhMM0QY.cjs} +411 -601
  9. package/dist/.setup/{delegated-state-Bv5cWuec.cjs → delegated-state-ilYzKwD8.cjs} +68 -6
  10. package/dist/.setup/{monitor-cuifGELM.cjs → monitor-b0zPpnmW.cjs} +1883 -2310
  11. package/dist/.setup/{oauth-BBUQCwmq.cjs → oauth-CUtfkgKE.cjs} +10 -21
  12. package/dist/.setup/presentation-Duoi9Ijn.cjs +213 -0
  13. package/dist/.setup/{reaction-types-CwP5RVQB.cjs → reaction-types-C6F6g2nk.cjs} +1 -1
  14. package/dist/.setup/{resolve-allowlist-gI34MnpE.cjs → resolve-allowlist-7mIEepXB.cjs} +214 -90
  15. package/dist/.setup/{secret-contract-api-wAyY5UHv.cjs → secret-contract-api-D554HIrH.cjs} +4 -6
  16. package/dist/.setup/{secret-contract-api-YY3v6MP8.cjs → secret-contract-api-r3hRtbBW.cjs} +1 -1
  17. package/dist/.setup/{send-C9JY42T6.cjs → send-D1w7a_ip.cjs} +86 -182
  18. package/dist/.setup/{setup-plugin-api-B-NYBLkF.cjs → setup-plugin-api-ChovZJU6.cjs} +1 -1
  19. package/dist/.setup/{thread-session-BWVCoYza.cjs → thread-session-CeWYiol_.cjs} +4 -6
  20. package/dist/.setup/{token-config-bEpua_Q7.cjs → token-config-DICiuJWr.cjs} +0 -2
  21. package/dist/.source-module-urls/7e43ee3ae4faab430a47269c8217f8d99ec60ecd85233f40e81d03efeb543068.json +1 -0
  22. package/dist/api.cjs +2 -2
  23. package/dist/channel-config-api.cjs +1 -1
  24. package/dist/channel-plugin-api.cjs +1 -1
  25. package/dist/directory-contract-api.cjs +1 -1
  26. package/dist/doctor-contract-api.cjs +3 -11
  27. package/dist/qa-runner-api.cjs +1 -1
  28. package/dist/secret-contract-api.cjs +1 -1
  29. package/dist/setup-entry.cjs +2 -2
  30. package/dist/setup-plugin-api.cjs +1 -1
  31. package/openclaw.plugin.json +0 -30
  32. package/package.json +11 -11
@@ -1,10 +1,11 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-BloeesfJ.cjs");
2
2
  const require_bot_framework_service_url = require("./bot-framework-service-url-B3FWn2SV.cjs");
3
- const require_resolve_allowlist = require("./resolve-allowlist-gI34MnpE.cjs");
3
+ const require_resolve_allowlist = require("./resolve-allowlist-7mIEepXB.cjs");
4
4
  const require_runtime = require("./runtime-gZTAuFux.cjs");
5
5
  require("../runtime-api.cjs");
6
- const require_delegated_state = require("./delegated-state-Bv5cWuec.cjs");
7
- const require_channel_setup = require("./channel.setup-BN3uhq3r.cjs");
6
+ const require_delegated_state = require("./delegated-state-ilYzKwD8.cjs");
7
+ const require_presentation = require("./presentation-Duoi9Ijn.cjs");
8
+ const require_channel_setup = require("./channel.setup-B9yKGEse.cjs");
8
9
  let openclaw_plugin_sdk_lazy_runtime = require("openclaw/plugin-sdk/lazy-runtime");
9
10
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
10
11
  let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
@@ -13,7 +14,6 @@ let openclaw_plugin_sdk_provider_http = require("openclaw/plugin-sdk/provider-ht
13
14
  let openclaw_plugin_sdk_reply_payload = require("openclaw/plugin-sdk/reply-payload");
14
15
  let openclaw_plugin_sdk_ssrf_runtime = require("openclaw/plugin-sdk/ssrf-runtime");
15
16
  let openclaw_plugin_sdk_text_chunking = require("openclaw/plugin-sdk/text-chunking");
16
- let node_dns_promises = require("node:dns/promises");
17
17
  let openclaw_plugin_sdk_ssrf_policy = require("openclaw/plugin-sdk/ssrf-policy");
18
18
  let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-runtime");
19
19
  let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text-utility-runtime");
@@ -22,13 +22,14 @@ let node_crypto = require("node:crypto");
22
22
  node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
23
23
  let node_path = require("node:path");
24
24
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
25
+ let openclaw_plugin_sdk_node_crypto_runtime = require("openclaw/plugin-sdk/node-crypto-runtime");
25
26
  let openclaw_plugin_sdk_retry_runtime = require("openclaw/plugin-sdk/retry-runtime");
26
27
  let openclaw_plugin_sdk_runtime_env = require("openclaw/plugin-sdk/runtime-env");
27
28
  let openclaw_plugin_sdk_markdown_table_runtime = require("openclaw/plugin-sdk/markdown-table-runtime");
28
- let openclaw_plugin_sdk_blob_runtime = require("openclaw/plugin-sdk/blob-runtime");
29
+ let node_dns_promises = require("node:dns/promises");
29
30
  let openclaw_plugin_sdk_global_singleton = require("openclaw/plugin-sdk/global-singleton");
30
31
  let openclaw_plugin_sdk_expect_runtime = require("openclaw/plugin-sdk/expect-runtime");
31
- let openclaw_plugin_sdk_reply_chunking = require("openclaw/plugin-sdk/reply-chunking");
32
+ let openclaw_plugin_sdk_blob_runtime = require("openclaw/plugin-sdk/blob-runtime");
32
33
  let openclaw_plugin_sdk_web_media = require("openclaw/plugin-sdk/web-media");
33
34
  let openclaw_plugin_sdk_dedupe_runtime = require("openclaw/plugin-sdk/dedupe-runtime");
34
35
  let openclaw_plugin_sdk_plugin_state_runtime = require("openclaw/plugin-sdk/plugin-state-runtime");
@@ -40,18 +41,11 @@ let openclaw_plugin_sdk_media_runtime = require("openclaw/plugin-sdk/media-runti
40
41
  * as upload destinations in the FileConsentCard flow.
41
42
  */
42
43
  const CONSENT_UPLOAD_HOST_ALLOWLIST = [
43
- "sharepoint.com",
44
- "sharepoint.us",
45
- "sharepoint.de",
46
- "sharepoint.cn",
47
- "sharepoint-df.com",
44
+ ...require_resolve_allowlist.SHAREPOINT_HOSTS,
48
45
  "storage.live.com",
49
46
  "onedrive.com",
50
47
  "1drv.ms",
51
- "graph.microsoft.com",
52
- "graph.microsoft.us",
53
- "graph.microsoft.de",
54
- "graph.microsoft.cn"
48
+ ...require_resolve_allowlist.GRAPH_HOSTS
55
49
  ];
56
50
  async function validateConsentUploadUrl(url) {
57
51
  const parsed = URL.parse(url);
@@ -127,7 +121,7 @@ async function uploadToConsentUrl(params) {
127
121
  "Content-Type": params.contentType ?? "application/octet-stream",
128
122
  "Content-Range": `bytes 0-${params.buffer.length - 1}/${params.buffer.length}`
129
123
  },
130
- body: new Blob([(0, openclaw_plugin_sdk_blob_runtime.bufferToBlobPart)(params.buffer)])
124
+ body: Uint8Array.from(params.buffer)
131
125
  }, require_resolve_allowlist.resolveMSTeamsSharePointUploadTimeoutMs(params.buffer.length));
132
126
  await res.body?.cancel().catch(() => void 0);
133
127
  if (!res.ok) throw new Error(`File upload to consent URL failed: ${res.status} ${res.statusText}`);
@@ -146,13 +140,13 @@ const PENDING_UPLOAD_META_NAMESPACE = "pending-uploads";
146
140
  const PENDING_UPLOAD_CHUNKS_NAMESPACE = "pending-upload-chunks";
147
141
  const PENDING_UPLOAD_MUTATION_KEY = "pending-uploads";
148
142
  function createMetaStore() {
149
- return require_runtime.getMSTeamsRuntime().state.openKeyedStore({
143
+ return require_runtime.getMSTeamsRuntime().state.openKeyedStoreV2({
150
144
  namespace: PENDING_UPLOAD_META_NAMESPACE,
151
145
  maxEntries: PENDING_UPLOAD_META_MAX_ENTRIES
152
146
  });
153
147
  }
154
148
  function createChunkStore() {
155
- return require_runtime.getMSTeamsRuntime().state.openKeyedStore({
149
+ return require_runtime.getMSTeamsRuntime().state.openKeyedStoreV2({
156
150
  namespace: PENDING_UPLOAD_CHUNKS_NAMESPACE,
157
151
  maxEntries: MAX_PENDING_UPLOAD_CHUNK_ROWS
158
152
  });
@@ -166,17 +160,6 @@ function buildMetaKey(id) {
166
160
  function buildChunkKey(id, index) {
167
161
  return `${buildUploadKey(id)}:chunk:${String(index).padStart(4, "0")}`;
168
162
  }
169
- function recordToUpload(record, buffer) {
170
- return {
171
- id: record.id,
172
- buffer,
173
- filename: record.filename,
174
- contentType: record.contentType,
175
- conversationId: record.conversationId,
176
- consentCardActivityId: record.consentCardActivityId,
177
- createdAt: record.createdAt
178
- };
179
- }
180
163
  async function deleteUploadRows(id, metaStore, chunkStore) {
181
164
  const existing = await metaStore.lookup(buildMetaKey(id));
182
165
  await metaStore.delete(buildMetaKey(id));
@@ -192,11 +175,11 @@ async function registerUploadRows(record, metaStore, chunkStore) {
192
175
  await pruneUploadStore(metaStore, chunkStore, chunkCount);
193
176
  for (let index = 0; index < chunkCount; index += 1) {
194
177
  const chunk = buffer.subarray(index * RAW_CHUNK_BYTES, (index + 1) * RAW_CHUNK_BYTES);
195
- await chunkStore.register(buildChunkKey(record.id, index), require_delegated_state.toPluginJsonValue({
178
+ await chunkStore.register(buildChunkKey(record.id, index), {
196
179
  id: record.id,
197
180
  index,
198
181
  dataBase64: chunk.toString("base64")
199
- }));
182
+ });
200
183
  }
201
184
  await metaStore.register(buildMetaKey(record.id), require_delegated_state.toPluginJsonValue({
202
185
  id: record.id,
@@ -213,16 +196,25 @@ async function readUploadRows(id, metaStore, chunkStore) {
213
196
  const meta = await metaStore.lookup(buildMetaKey(id));
214
197
  if (!meta) return;
215
198
  if (!Number.isSafeInteger(meta.chunkCount) || meta.chunkCount < 1 || meta.chunkCount > MAX_CHUNKS_PER_UPLOAD) return;
216
- const records = await chunkStore.lookupMany?.(Array.from({ length: meta.chunkCount }, (_, index) => buildChunkKey(id, index)));
199
+ const records = await chunkStore.lookupMany(Array.from({ length: meta.chunkCount }, (_, index) => buildChunkKey(id, index)));
217
200
  const chunks = [];
218
201
  for (let index = 0; index < meta.chunkCount; index += 1) {
219
- const result = records?.[index];
202
+ const result = records[index];
220
203
  if (result && !result.ok) throw result.error;
221
- const chunk = records ? result?.value : await chunkStore.lookup(buildChunkKey(id, index));
204
+ const chunk = result?.value;
222
205
  if (!chunk || chunk.id !== id || chunk.index !== index) return;
223
206
  chunks.push(Buffer.from(chunk.dataBase64, "base64"));
224
207
  }
225
- return recordToUpload(meta, Buffer.concat(chunks, meta.byteLength));
208
+ const buffer = Buffer.concat(chunks, meta.byteLength);
209
+ return {
210
+ id: meta.id,
211
+ buffer,
212
+ filename: meta.filename,
213
+ contentType: meta.contentType,
214
+ conversationId: meta.conversationId,
215
+ consentCardActivityId: meta.consentCardActivityId,
216
+ createdAt: meta.createdAt
217
+ };
226
218
  }
227
219
  async function pruneUploadStore(metaStore, chunkStore, extraChunkRows = 0) {
228
220
  const rows = await metaStore.entries();
@@ -290,10 +282,10 @@ async function setPendingUploadActivityIdFs(id, activityId) {
290
282
  await require_delegated_state.withMSTeamsSqliteMutationLock(PENDING_UPLOAD_MUTATION_KEY, async () => {
291
283
  const record = await metaStore.lookup(buildMetaKey(id));
292
284
  if (!record || Date.now() - record.createdAt > PENDING_UPLOAD_TTL_MS$1) return;
293
- await metaStore.register(buildMetaKey(id), require_delegated_state.toPluginJsonValue({
285
+ await metaStore.register(buildMetaKey(id), {
294
286
  ...record,
295
287
  consentCardActivityId: activityId
296
- }));
288
+ });
297
289
  });
298
290
  }
299
291
  //#endregion
@@ -305,14 +297,9 @@ async function setPendingUploadActivityIdFs(id, activityId) {
305
297
  * before upload. This module stores the file data temporarily until the user
306
298
  * accepts or declines, or until the TTL expires.
307
299
  */
308
- const { pendingUploads, pendingUploadTimers } = (0, openclaw_plugin_sdk_global_singleton.resolveGlobalSingleton)(Symbol.for("openclaw.msteams.pendingUploadState"), () => ({
309
- pendingUploads: /* @__PURE__ */ new Map(),
310
- /** Timer handles keyed by upload ID, cleared on explicit removal to prevent ghost cleanup. */
311
- pendingUploadTimers: /* @__PURE__ */ new Map()
312
- }), (state) => {
313
- for (const timer of state.pendingUploadTimers.values()) clearTimeout(timer);
314
- state.pendingUploadTimers.clear();
315
- state.pendingUploads.clear();
300
+ const pendingUploads = (0, openclaw_plugin_sdk_global_singleton.resolveGlobalSingleton)(Symbol.for("openclaw.msteams.pendingUploadState"), () => /* @__PURE__ */ new Map(), (state) => {
301
+ for (const { timer } of state.values()) clearTimeout(timer);
302
+ state.clear();
316
303
  });
317
304
  const PENDING_UPLOAD_TTL_MS = 3e5;
318
305
  /**
@@ -326,17 +313,18 @@ function storePendingUpload(upload) {
326
313
  id,
327
314
  createdAt: Date.now()
328
315
  };
329
- pendingUploads.set(id, entry);
330
316
  const timer = setTimeout(() => {
331
317
  pendingUploads.delete(id);
332
- pendingUploadTimers.delete(id);
333
318
  }, PENDING_UPLOAD_TTL_MS);
334
- pendingUploadTimers.set(id, timer);
319
+ pendingUploads.set(id, {
320
+ upload: entry,
321
+ timer
322
+ });
335
323
  return id;
336
324
  }
337
325
  function getPendingUpload(id) {
338
326
  if (!id) return;
339
- const entry = pendingUploads.get(id);
327
+ const entry = pendingUploads.get(id)?.upload;
340
328
  if (!entry) return;
341
329
  if (Date.now() - entry.createdAt > PENDING_UPLOAD_TTL_MS) {
342
330
  removePendingUpload(id);
@@ -350,16 +338,12 @@ function getPendingUpload(id) {
350
338
  */
351
339
  function removePendingUpload(id) {
352
340
  if (id) {
341
+ clearTimeout(pendingUploads.get(id)?.timer);
353
342
  pendingUploads.delete(id);
354
- const timer = pendingUploadTimers.get(id);
355
- if (timer !== void 0) {
356
- clearTimeout(timer);
357
- pendingUploadTimers.delete(id);
358
- }
359
343
  }
360
344
  }
361
345
  function setPendingUploadActivityId(uploadId, activityId) {
362
- const entry = pendingUploads.get(uploadId);
346
+ const entry = pendingUploads.get(uploadId)?.upload;
363
347
  if (entry) entry.consentCardActivityId = activityId;
364
348
  }
365
349
  //#endregion
@@ -470,33 +454,6 @@ function rewriteMarkdownIR(ir, edits) {
470
454
  })) } : {}
471
455
  };
472
456
  }
473
- function prefixMSTeamsBlockquotes(ir) {
474
- const rewritten = rewriteMarkdownIR(ir, ir.styles.filter((span) => span.style === "blockquote").flatMap((span) => {
475
- const positions = [span.start];
476
- for (let index = span.start; index < span.end; index += 1) if (ir.text[index] === "\n" && index + 1 < span.end) positions.push(index + 1);
477
- return positions.map((position) => ({
478
- start: position,
479
- end: position,
480
- text: "> "
481
- }));
482
- }));
483
- return {
484
- ...rewritten,
485
- styles: rewritten.styles.filter((span) => span.style !== "blockquote")
486
- };
487
- }
488
- function longestBacktickRun(text) {
489
- return Math.max(0, ...text.match(/`+/gu)?.map((run) => run.length) ?? []);
490
- }
491
- function renderMSTeamsCode(style, text) {
492
- const marker = "`".repeat(Math.max(style === "code_block" ? 3 : 1, longestBacktickRun(text) + 1));
493
- if (style === "code_block") return `${marker}\n${text}${marker}`;
494
- const needsPadding = text.startsWith("`") || text.endsWith("`") || text.startsWith(" ") && text.endsWith(" ") && text.trim().length > 0;
495
- return `${marker}${needsPadding ? " " : ""}${text}${needsPadding ? " " : ""}${marker}`;
496
- }
497
- function serializeMarkdownDestination(href) {
498
- return `<${href.replace(/([\\<>])/gu, "\\$1")}>`;
499
- }
500
457
  function blankBlockEnd(text, index) {
501
458
  if (text[index] !== "\n" && text[index] !== "\r") return;
502
459
  const match = /^(?:\r?\n)[ \t]*(?:\r?\n)/u.exec(text.slice(index));
@@ -556,74 +513,6 @@ function protectDelimitedMarkdown(text, opener, tokenPrefix, values) {
556
513
  }
557
514
  return protectedText + text.slice(cursor);
558
515
  }
559
- function protectRawTables(text, tokenPrefix, rawTables) {
560
- if (!text.includes("|")) return text;
561
- const { tables } = (0, openclaw_plugin_sdk_text_chunking.markdownToIRWithMeta)(text, {
562
- autolink: false,
563
- linkify: false,
564
- tableMode: "block"
565
- });
566
- let result = "";
567
- let cursor = 0;
568
- for (const table of tables) {
569
- const source = (0, openclaw_plugin_sdk_expect_runtime.expectDefined)((0, openclaw_plugin_sdk_markdown_table_runtime.getMarkdownTableSource)(table), "Markdown table source");
570
- const lineStart = Math.max(text.lastIndexOf("\n", source.start - 1), text.lastIndexOf("\r", source.start - 1)) + 1;
571
- const start = lineStart + (/^(?: {0,3}>[ \t]?)*/u.exec(text.slice(lineStart, source.start))?.[0] ?? "").length;
572
- const end = source.end + (text.slice(source.end, source.end + 2) === "\r\n" ? 1 : 0);
573
- const index = rawTables.push(text.slice(start, end)) - 1;
574
- result += text.slice(cursor, start) + `${tokenPrefix}t${index}${TOKEN_END}`;
575
- cursor = end;
576
- }
577
- return result + text.slice(cursor);
578
- }
579
- function protectMSTeamsCode(ir, tokenPrefix, code, protectedValues) {
580
- const codeSpans = ir.styles.filter((span) => span.style === "code" || span.style === "code_block");
581
- const codeBlocks = codeSpans.filter((span) => span.style === "code_block");
582
- const adjustedStyles = ir.styles.flatMap((span) => {
583
- if (span.style !== "blockquote") return [span];
584
- let segments = [span];
585
- for (const codeBlock of codeBlocks) segments = segments.flatMap((segment) => {
586
- if (codeBlock.end <= segment.start || codeBlock.start >= segment.end) return [segment];
587
- return [...segment.start < codeBlock.start ? [{
588
- ...segment,
589
- end: codeBlock.start
590
- }] : [], ...codeBlock.end < segment.end ? [{
591
- ...segment,
592
- start: codeBlock.end
593
- }] : []];
594
- });
595
- return segments;
596
- });
597
- const rewritten = rewriteMarkdownIR({
598
- ...ir,
599
- styles: adjustedStyles
600
- }, codeSpans.map((span) => {
601
- const codeStyle = span.style === "code_block" ? "code_block" : "code";
602
- const source = protectedValues.reduce((text, protectedValue) => restoreTokens(text, protectedValue.prefix, protectedValue.values), ir.text.slice(span.start, span.end));
603
- let quoteDepth = 0;
604
- let hasTrailingQuotedText = false;
605
- if (codeStyle === "code_block") {
606
- for (const candidate of ir.styles) if (candidate.style === "blockquote" && span.start >= candidate.start) {
607
- if (span.start < candidate.end) quoteDepth += 1;
608
- hasTrailingQuotedText ||= candidate.end > span.end;
609
- }
610
- }
611
- const rendered = renderMSTeamsCode(codeStyle, source);
612
- let quoted = quoteDepth > 0 ? `${"> ".repeat(quoteDepth)}${rendered.replaceAll("\n", `\n${"> ".repeat(quoteDepth)}`)}` : rendered;
613
- const followedByText = codeStyle === "code_block" && span.end < ir.text.length && ir.text[span.end] !== "\n";
614
- if ((hasTrailingQuotedText || followedByText) && !quoted.endsWith("\n")) quoted += "\n";
615
- const index = code.push(quoted) - 1;
616
- return {
617
- start: span.start,
618
- end: span.end,
619
- text: `${tokenPrefix}c${index}${TOKEN_END}`
620
- };
621
- }));
622
- return {
623
- ...rewritten,
624
- styles: rewritten.styles.filter((span) => span.style !== "code" && span.style !== "code_block")
625
- };
626
- }
627
516
  function formatMSTeamsMarkdown(markdown, tableMode) {
628
517
  if (markdown === "") return markdown;
629
518
  const rawTables = [];
@@ -638,7 +527,27 @@ function formatMSTeamsMarkdown(markdown, tableMode) {
638
527
  return `${tokenPrefix}h${index}${TOKEN_END}`;
639
528
  }), "![", `${tokenPrefix}i`, images), "@[", `${tokenPrefix}m`, mentions);
640
529
  const tableInput = (0, openclaw_plugin_sdk_text_chunking.convertMarkdownTables)(mentionsProtected, tableMode);
641
- const protectedMarkdown = (tableMode === "off" ? protectRawTables(tableInput, tokenPrefix, rawTables) : tableInput).replace(ESCAPED_MARKDOWN_RE, (escaped) => {
530
+ let converted = tableInput;
531
+ if (tableMode === "off" && tableInput.includes("|")) {
532
+ const { tables } = (0, openclaw_plugin_sdk_text_chunking.markdownToIRWithMeta)(tableInput, {
533
+ autolink: false,
534
+ linkify: false,
535
+ tableMode: "block"
536
+ });
537
+ let result = "";
538
+ let cursor = 0;
539
+ for (const table of tables) {
540
+ const source = (0, openclaw_plugin_sdk_expect_runtime.expectDefined)((0, openclaw_plugin_sdk_markdown_table_runtime.getMarkdownTableSource)(table), "Markdown table source");
541
+ const lineStart = Math.max(tableInput.lastIndexOf("\n", source.start - 1), tableInput.lastIndexOf("\r", source.start - 1)) + 1;
542
+ const start = lineStart + (/^(?: {0,3}>[ \t]?)*/u.exec(tableInput.slice(lineStart, source.start))?.[0] ?? "").length;
543
+ const end = source.end + (tableInput.slice(source.end, source.end + 2) === "\r\n" ? 1 : 0);
544
+ const index = rawTables.push(tableInput.slice(start, end)) - 1;
545
+ result += tableInput.slice(cursor, start) + `${tokenPrefix}t${index}${TOKEN_END}`;
546
+ cursor = end;
547
+ }
548
+ converted = result + tableInput.slice(cursor);
549
+ }
550
+ const protectedMarkdown = converted.replace(ESCAPED_MARKDOWN_RE, (escaped) => {
642
551
  const index = escapedMarkdown.push(escaped) - 1;
643
552
  return `${tokenPrefix}e${index}${TOKEN_END}`;
644
553
  });
@@ -672,7 +581,71 @@ function formatMSTeamsMarkdown(markdown, tableMode) {
672
581
  values: entities
673
582
  }
674
583
  ];
675
- const ir = prefixMSTeamsBlockquotes(protectMSTeamsCode(parsed, tokenPrefix, codeRegions, protectedValues));
584
+ const codeSpans = parsed.styles.filter((span) => span.style === "code" || span.style === "code_block");
585
+ const codeBlocks = codeSpans.filter((span) => span.style === "code_block");
586
+ const adjustedStyles = parsed.styles.flatMap((span) => {
587
+ if (span.style !== "blockquote") return [span];
588
+ let segments = [span];
589
+ for (const codeBlock of codeBlocks) segments = segments.flatMap((segment) => {
590
+ if (codeBlock.end <= segment.start || codeBlock.start >= segment.end) return [segment];
591
+ return [...segment.start < codeBlock.start ? [{
592
+ ...segment,
593
+ end: codeBlock.start
594
+ }] : [], ...codeBlock.end < segment.end ? [{
595
+ ...segment,
596
+ start: codeBlock.end
597
+ }] : []];
598
+ });
599
+ return segments;
600
+ });
601
+ const codeRewritten = rewriteMarkdownIR({
602
+ ...parsed,
603
+ styles: adjustedStyles
604
+ }, codeSpans.map((span) => {
605
+ const codeStyle = span.style === "code_block" ? "code_block" : "code";
606
+ const source = protectedValues.reduce((text, protectedValue) => restoreTokens(text, protectedValue.prefix, protectedValue.values), parsed.text.slice(span.start, span.end));
607
+ let quoteDepth = 0;
608
+ let hasTrailingQuotedText = false;
609
+ if (codeStyle === "code_block") {
610
+ for (const candidate of parsed.styles) if (candidate.style === "blockquote" && span.start >= candidate.start) {
611
+ if (span.start < candidate.end) quoteDepth += 1;
612
+ hasTrailingQuotedText ||= candidate.end > span.end;
613
+ }
614
+ }
615
+ const marker = "`".repeat(Math.max(codeStyle === "code_block" ? 3 : 1, Math.max(0, ...source.match(/`+/gu)?.map((run) => run.length) ?? []) + 1));
616
+ let rendered;
617
+ if (codeStyle === "code_block") rendered = `${marker}\n${source}${marker}`;
618
+ else {
619
+ const needsPadding = source.startsWith("`") || source.endsWith("`") || source.startsWith(" ") && source.endsWith(" ") && source.trim().length > 0;
620
+ rendered = `${marker}${needsPadding ? " " : ""}${source}${needsPadding ? " " : ""}${marker}`;
621
+ }
622
+ let quoted = quoteDepth > 0 ? `${"> ".repeat(quoteDepth)}${rendered.replaceAll("\n", `\n${"> ".repeat(quoteDepth)}`)}` : rendered;
623
+ const followedByText = codeStyle === "code_block" && span.end < parsed.text.length && parsed.text[span.end] !== "\n";
624
+ if ((hasTrailingQuotedText || followedByText) && !quoted.endsWith("\n")) quoted += "\n";
625
+ const index = codeRegions.push(quoted) - 1;
626
+ return {
627
+ start: span.start,
628
+ end: span.end,
629
+ text: `${tokenPrefix}c${index}${TOKEN_END}`
630
+ };
631
+ }));
632
+ const codeProtected = {
633
+ ...codeRewritten,
634
+ styles: codeRewritten.styles.filter((span) => span.style !== "code" && span.style !== "code_block")
635
+ };
636
+ const rewritten = rewriteMarkdownIR(codeProtected, codeProtected.styles.filter((span) => span.style === "blockquote").flatMap((span) => {
637
+ const positions = [span.start];
638
+ for (let index = span.start; index < span.end; index += 1) if (codeProtected.text[index] === "\n" && index + 1 < span.end) positions.push(index + 1);
639
+ return positions.map((position) => ({
640
+ start: position,
641
+ end: position,
642
+ text: "> "
643
+ }));
644
+ }));
645
+ const ir = {
646
+ ...rewritten,
647
+ styles: rewritten.styles.filter((span) => span.style !== "blockquote")
648
+ };
676
649
  const rendered = (0, openclaw_plugin_sdk_text_chunking.renderMarkdownWithMarkers)(ir, {
677
650
  styleMarkers: MSTEAMS_MARKERS,
678
651
  escapeText: (text) => text,
@@ -680,7 +653,7 @@ function formatMSTeamsMarkdown(markdown, tableMode) {
680
653
  start: link.start,
681
654
  end: link.end,
682
655
  open: "[",
683
- close: `](${serializeMarkdownDestination(link.href)})`
656
+ close: `](<${link.href.replace(/([\\<>])/gu, "\\$1")}>)`
684
657
  })
685
658
  }, MSTEAMS_FORMAT_CAPABILITIES);
686
659
  return protectedValues.reduce((text, { prefix, values }) => restoreTokens(text, prefix, values), restoreTokens(rendered, `${tokenPrefix}c`, codeRegions));
@@ -747,26 +720,6 @@ async function requestSharePointJson(params, request) {
747
720
  }
748
721
  });
749
722
  }
750
- async function uploadToSharePoint(params) {
751
- const uploadPath = `/OpenClawShared/${encodeURIComponent(params.filename)}`;
752
- const data = await requestSharePointJson(params, {
753
- url: `${GRAPH_ROOT}/sites/${params.siteId}/drive/root:${uploadPath}:/content?@microsoft.graph.conflictBehavior=rename`,
754
- timeoutMs: require_resolve_allowlist.resolveMSTeamsSharePointUploadTimeoutMs(params.buffer.length),
755
- init: () => ({
756
- method: "PUT",
757
- headers: { "Content-Type": params.contentType ?? "application/octet-stream" },
758
- body: new Blob([(0, openclaw_plugin_sdk_blob_runtime.bufferToBlobPart)(params.buffer)])
759
- }),
760
- error: "SharePoint upload failed",
761
- label: "msteams.graph-upload.uploadSharePointFile"
762
- });
763
- if (!data.id || !data.webUrl || !data.name) throw new Error("SharePoint upload response missing required fields");
764
- return {
765
- id: data.id,
766
- webUrl: data.webUrl,
767
- name: data.name
768
- };
769
- }
770
723
  async function getDriveItemProperties(params) {
771
724
  const data = await requestSharePointJson(params, {
772
725
  url: `${GRAPH_ROOT}/sites/${params.siteId}/drive/items/${params.itemId}?$select=eTag,webDavUrl,name`,
@@ -780,27 +733,44 @@ async function getDriveItemProperties(params) {
780
733
  name: data.name
781
734
  };
782
735
  }
783
- async function getChatMemberIds(params) {
784
- return ((await requestSharePointJson(params, {
785
- url: `${GRAPH_ROOT}/chats/${params.chatId}/members`,
786
- error: (res) => res.status === 403 ? "Get chat members failed; verify Graph chat-member permissions and tenant access policies" : "Get chat members failed",
787
- label: "msteams.graph-upload.getChatMembers"
788
- })).value ?? []).flatMap((member) => member.userId ? [member.userId] : []);
789
- }
790
736
  /**
791
- * Create a sharing link for a SharePoint drive item.
792
- * For organization scope (default), uses v1.0 API.
793
- * For per-user scope, uses beta API with recipients.
737
+ * Upload a file to SharePoint and create a sharing link.
738
+ *
739
+ * For group chats, this creates a per-user sharing link scoped to chat members.
740
+ * For channels, this creates an organization-wide sharing link.
794
741
  */
795
- async function createSharePointSharingLink(params) {
796
- const apiRoot = params.recipientObjectIds ? GRAPH_BETA : GRAPH_ROOT;
742
+ async function uploadAndShareSharePoint(params) {
743
+ const uploadPath = `/OpenClawShared/${encodeURIComponent(params.filename)}`;
744
+ const data = await requestSharePointJson(params, {
745
+ url: `${GRAPH_ROOT}/sites/${params.siteId}/drive/root:${uploadPath}:/content?@microsoft.graph.conflictBehavior=rename`,
746
+ timeoutMs: require_resolve_allowlist.resolveMSTeamsSharePointUploadTimeoutMs(params.buffer.length),
747
+ init: () => ({
748
+ method: "PUT",
749
+ headers: { "Content-Type": params.contentType ?? "application/octet-stream" },
750
+ body: new Blob([(0, openclaw_plugin_sdk_blob_runtime.bufferToBlobPart)(params.buffer)])
751
+ }),
752
+ error: "SharePoint upload failed",
753
+ label: "msteams.graph-upload.uploadSharePointFile"
754
+ });
755
+ if (!data.id) throw new Error("SharePoint upload response missing required fields");
756
+ const itemId = data.id;
757
+ let recipientObjectIds;
758
+ if (params.usePerUserSharing && params.chatId) {
759
+ recipientObjectIds = ((await requestSharePointJson(params, {
760
+ url: `${GRAPH_ROOT}/chats/${params.chatId}/members`,
761
+ error: (res) => res.status === 403 ? "Get chat members failed; verify Graph chat-member permissions and tenant access policies" : "Get chat members failed",
762
+ label: "msteams.graph-upload.getChatMembers"
763
+ })).value ?? []).flatMap((member) => member.userId ? [member.userId] : []);
764
+ if (recipientObjectIds.length === 0) throw new Error("MS Teams chat member lookup returned no recipients");
765
+ }
766
+ const apiRoot = recipientObjectIds ? GRAPH_BETA : GRAPH_ROOT;
797
767
  const body = {
798
768
  type: "view",
799
- scope: params.recipientObjectIds ? "users" : "organization"
769
+ scope: recipientObjectIds ? "users" : "organization"
800
770
  };
801
- if (params.recipientObjectIds) body.recipients = params.recipientObjectIds.map((id) => ({ objectId: id }));
802
- const data = await requestSharePointJson(params, {
803
- url: `${apiRoot}/sites/${params.siteId}/drive/items/${params.itemId}/createLink`,
771
+ if (recipientObjectIds) body.recipients = recipientObjectIds.map((id) => ({ objectId: id }));
772
+ await requestSharePointJson(params, {
773
+ url: `${apiRoot}/sites/${params.siteId}/drive/items/${itemId}/createLink`,
804
774
  init: () => ({
805
775
  method: "POST",
806
776
  headers: { "Content-Type": "application/json" },
@@ -809,39 +779,7 @@ async function createSharePointSharingLink(params) {
809
779
  error: "Create SharePoint sharing link failed",
810
780
  label: "msteams.graph-upload.createSharePointSharingLink"
811
781
  });
812
- if (!data.link?.webUrl) throw new Error("Create SharePoint sharing link response missing webUrl");
813
- return data.link.webUrl;
814
- }
815
- /**
816
- * Upload a file to SharePoint and create a sharing link.
817
- *
818
- * For group chats, this creates a per-user sharing link scoped to chat members.
819
- * For channels, this creates an organization-wide sharing link.
820
- */
821
- async function uploadAndShareSharePoint(params) {
822
- const uploaded = await uploadToSharePoint(params);
823
- let recipientObjectIds;
824
- if (params.usePerUserSharing && params.chatId) {
825
- recipientObjectIds = await getChatMemberIds({
826
- chatId: params.chatId,
827
- tokenProvider: params.tokenProvider,
828
- assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
829
- });
830
- if (recipientObjectIds.length === 0) throw new Error("MS Teams chat member lookup returned no recipients");
831
- }
832
- const shareUrl = await createSharePointSharingLink({
833
- siteId: params.siteId,
834
- itemId: uploaded.id,
835
- tokenProvider: params.tokenProvider,
836
- recipientObjectIds,
837
- assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
838
- });
839
- return {
840
- itemId: uploaded.id,
841
- webUrl: uploaded.webUrl,
842
- shareUrl,
843
- name: uploaded.name
844
- };
782
+ return { itemId };
845
783
  }
846
784
  //#endregion
847
785
  //#region extensions/msteams/src/media-helpers.ts
@@ -890,7 +828,7 @@ function extractMessageId(response) {
890
828
  return typeof id === "string" && id ? id : null;
891
829
  }
892
830
  //#endregion
893
- //#region extensions/msteams/src/mentions.ts
831
+ //#region extensions/msteams/src/message-activity.ts
894
832
  /**
895
833
  * Check whether an ID looks like a valid Teams user/bot identifier.
896
834
  * Accepts:
@@ -902,39 +840,6 @@ function extractMessageId(response) {
902
840
  */
903
841
  const TEAMS_BOT_ID_PATTERN = /^\d+:[a-z0-9._=-]+(?::[a-z0-9._=-]+)*$/i;
904
842
  const AAD_OBJECT_ID_PATTERN = /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i;
905
- function isValidTeamsId(id) {
906
- return TEAMS_BOT_ID_PATTERN.test(id) || AAD_OBJECT_ID_PATTERN.test(id);
907
- }
908
- /**
909
- * Convert @[Name](id) into matching <at> text and mention entities.
910
- * Only matches where the id looks like a real Teams user/bot ID are treated
911
- * as mentions. This avoids false positives from documentation or code samples
912
- * embedded in the message (e.g. `@[表示名](ユーザーID)` in backticks).
913
- */
914
- function parseMentions(text) {
915
- const mentionPattern = /@\[((?:\\[\s\S]|[^\]\\])+)\]\(([^)]+)\)/g;
916
- const entities = [];
917
- return {
918
- text: text.replace(mentionPattern, (match, name, id) => {
919
- const trimmedId = id.trim();
920
- if (!isValidTeamsId(trimmedId)) return match;
921
- const trimmedName = name.replace(/\\([\\[\]])/g, "$1").trim();
922
- const mentionTag = `<at>${trimmedName}</at>`;
923
- entities.push({
924
- type: "mention",
925
- text: mentionTag,
926
- mentioned: {
927
- id: trimmedId,
928
- name: trimmedName
929
- }
930
- });
931
- return mentionTag;
932
- }),
933
- entities
934
- };
935
- }
936
- //#endregion
937
- //#region extensions/msteams/src/message-activity.ts
938
843
  const AI_GENERATED_ENTITY = {
939
844
  type: "https://schema.org/Message",
940
845
  "@type": "Message",
@@ -942,13 +847,36 @@ const AI_GENERATED_ENTITY = {
942
847
  "@id": "",
943
848
  additionalType: ["AIGeneratedContent"]
944
849
  };
850
+ /**
851
+ * Convert @[Name](id) into matching <at> text and mention entities.
852
+ * Only matches where the id looks like a real Teams user/bot ID are treated
853
+ * as mentions. This avoids false positives from documentation or code samples
854
+ * embedded in the message (e.g. `@[表示名](ユーザーID)` in backticks).
855
+ */
945
856
  /** Build final transport text and its matching entities after Markdown rendering. */
946
857
  function buildMSTeamsMessageActivity(text) {
947
- const parsed = parseMentions(text ?? "");
858
+ const sourceText = text ?? "";
859
+ const mentionPattern = /@\[((?:\\[\s\S]|[^\]\\])+)\]\(([^)]+)\)/g;
860
+ const entities = [];
861
+ const formattedText = sourceText.replace(mentionPattern, (match, name, id) => {
862
+ const trimmedId = id.trim();
863
+ if (!(TEAMS_BOT_ID_PATTERN.test(trimmedId) || AAD_OBJECT_ID_PATTERN.test(trimmedId))) return match;
864
+ const trimmedName = name.replace(/\\([\\[\]])/g, "$1").trim();
865
+ const mentionTag = `<at>${trimmedName}</at>`;
866
+ entities.push({
867
+ type: "mention",
868
+ text: mentionTag,
869
+ mentioned: {
870
+ id: trimmedId,
871
+ name: trimmedName
872
+ }
873
+ });
874
+ return mentionTag;
875
+ });
948
876
  return {
949
877
  type: "message",
950
- ...text === void 0 ? {} : { text: parsed.text },
951
- entities: [...parsed.entities, AI_GENERATED_ENTITY]
878
+ ...text === void 0 ? {} : { text: formattedText },
879
+ entities: [...entities, AI_GENERATED_ENTITY]
952
880
  };
953
881
  }
954
882
  function buildMSTeamsAdaptiveCardActivity(card) {
@@ -961,17 +889,6 @@ function buildMSTeamsAdaptiveCardActivity(card) {
961
889
  };
962
890
  }
963
891
  //#endregion
964
- //#region extensions/msteams/src/revoked-context.ts
965
- async function withRevokedProxyFallback(params) {
966
- try {
967
- return await params.run();
968
- } catch (err) {
969
- if (!require_channel_setup.isRevokedProxyError(err)) throw err;
970
- params.onRevokedLog?.();
971
- return await params.onRevoked();
972
- }
973
- }
974
- //#endregion
975
892
  //#region extensions/msteams/src/sent-message-cache.ts
976
893
  const TTL_MS = 864e5;
977
894
  const MAX_ENTRIES = 2e4;
@@ -981,7 +898,7 @@ const MSTEAMS_SENT_MESSAGES_KEY = Symbol.for("openclaw.msteamsSentMessages");
981
898
  const namedAccountCaches = /* @__PURE__ */ new Map();
982
899
  function createSentMessageCache(accountId) {
983
900
  const isDefault = accountId === "default";
984
- const accountDigest = isDefault ? void 0 : (0, node_crypto.createHash)("sha256").update(accountId).digest("hex");
901
+ const accountDigest = isDefault ? void 0 : (0, openclaw_plugin_sdk_node_crypto_runtime.sha256Hex)(accountId);
985
902
  return (0, openclaw_plugin_sdk_dedupe_runtime.createPersistentDedupeCache)({
986
903
  globalKey: isDefault ? MSTEAMS_SENT_MESSAGES_KEY : Symbol.for(`openclaw.msteamsSentMessages.account.v1.${accountDigest}`),
987
904
  ttlMs: TTL_MS,
@@ -1030,20 +947,15 @@ async function quoteMSTeamsActivity(activity, messageId) {
1030
947
  if (activity.type !== "message") return activity;
1031
948
  return MessageActivityInput.from(activity).prependQuote(messageId);
1032
949
  }
1033
- function resolveThreadedConversationId(conversationId, threadActivityId) {
1034
- const baseId = conversationId.split(";")[0] ?? conversationId;
1035
- return threadActivityId ? `${baseId};messageid=${threadActivityId}` : baseId;
1036
- }
1037
- function normalizeRequiredServiceUrl(ref) {
1038
- if (!ref.serviceUrl) throw new Error("Invalid stored reference: missing serviceUrl");
1039
- return require_bot_framework_service_url.normalizeBotFrameworkServiceUrl(ref.serviceUrl);
1040
- }
1041
950
  function buildSdkConversationReference(source, options) {
1042
- const bot = source.agent ?? source.bot ?? void 0;
951
+ const bot = source.agent ?? source.bot;
1043
952
  if (!bot?.id) throw new Error("Invalid stored reference: missing agent.id");
1044
- const conversationId = resolveThreadedConversationId(source.conversation.id, options?.threadActivityId);
953
+ const baseId = source.conversation.id.split(";")[0] ?? source.conversation.id;
954
+ const threadActivityId = options?.threadActivityId;
955
+ const conversationId = threadActivityId ? `${baseId};messageid=${threadActivityId}` : baseId;
1045
956
  const tenantId = source.tenantId ?? source.conversation.tenantId;
1046
- const serviceUrl = normalizeRequiredServiceUrl(source);
957
+ if (!source.serviceUrl) throw new Error("Invalid stored reference: missing serviceUrl");
958
+ const serviceUrl = require_bot_framework_service_url.normalizeBotFrameworkServiceUrl(source.serviceUrl);
1047
959
  if (options?.serviceUrlBoundary) require_resolve_allowlist.validateMSTeamsProactiveServiceUrlBoundary({
1048
960
  cloud: options.serviceUrlBoundary.cloud,
1049
961
  conversationId,
@@ -1071,74 +983,52 @@ function buildSdkConversationReference(source, options) {
1071
983
  ...source.aadObjectId ? { aadObjectId: source.aadObjectId } : {}
1072
984
  };
1073
985
  }
1074
- function sameServiceUrl(left, right) {
1075
- if (!left) return false;
1076
- try {
1077
- return require_bot_framework_service_url.normalizeBotFrameworkServiceUrl(left) === right;
1078
- } catch {
1079
- return false;
1080
- }
1081
- }
1082
- function stringifyReferenceFallbackActivity(activity) {
1083
- if (typeof activity === "string") return activity;
1084
- if (activity == null) return "";
1085
- if (typeof activity === "number" || typeof activity === "boolean" || typeof activity === "bigint") return String(activity);
1086
- return "";
1087
- }
1088
- async function getApiClientForReference(app, ref) {
1089
- const api = app.api;
1090
- if (sameServiceUrl(api.serviceUrl, ref.serviceUrl)) return api;
1091
- const appInternals = app;
1092
- const httpClient = appInternals.api?.http ?? appInternals.client;
1093
- if (!httpClient) return api;
1094
- const { Client } = await loadMSTeamsApiModule();
1095
- return new Client(ref.serviceUrl, httpClient);
1096
- }
1097
- function mergeReferenceIntoActivity(activity, ref) {
1098
- const source = (0, openclaw_plugin_sdk_string_coerce_runtime.asOptionalRecord)(activity) ?? {
1099
- type: "message",
1100
- text: stringifyReferenceFallbackActivity(activity)
1101
- };
1102
- const existingChannelData = (0, openclaw_plugin_sdk_string_coerce_runtime.asOptionalRecord)(source.channelData);
1103
- const existingTenant = (0, openclaw_plugin_sdk_string_coerce_runtime.asOptionalRecord)(existingChannelData?.tenant);
1104
- let channelData = existingChannelData ? { ...existingChannelData } : void 0;
1105
- if (ref.tenantId) {
1106
- channelData ??= {};
1107
- channelData.tenant = existingTenant ? {
1108
- ...existingTenant,
1109
- id: ref.tenantId
1110
- } : { id: ref.tenantId };
1111
- }
1112
- return {
1113
- ...source,
1114
- channelId: ref.channelId,
1115
- from: ref.bot,
1116
- recipient: ref.user,
1117
- conversation: ref.conversation,
1118
- ...channelData ? { channelData } : {},
1119
- locale: ref.locale,
1120
- ...ref.tenantId ? { tenantId: ref.tenantId } : {},
1121
- ...ref.aadObjectId ? { aadObjectId: ref.aadObjectId } : {}
1122
- };
986
+ function getApiClientForReference(app, ref) {
987
+ return app.api.serviceUrl === ref.serviceUrl ? app.api : app.api.fromServiceUrl({ serviceUrl: ref.serviceUrl });
1123
988
  }
1124
989
  async function sendMSTeamsActivityWithReference(app, source, activity, options) {
1125
990
  return require_resolve_allowlist.withMSTeamsConnectorHandoff(options ?? {}, async (handoff) => {
1126
991
  require_resolve_allowlist.assertMSTeamsSendHandoff(handoff);
1127
992
  const ref = buildSdkConversationReference(source, options);
1128
- const activities = (await getApiClientForReference(app, ref)).conversations.activities(ref.conversation.id);
1129
- const activityWithRef = mergeReferenceIntoActivity(options?.quoteActivityId ? await quoteMSTeamsActivity(activity, options.quoteActivityId) : activity, ref);
993
+ const activities = getApiClientForReference(app, ref).conversations.activities(ref.conversation.id);
994
+ const quotedActivity = options?.quoteActivityId ? await quoteMSTeamsActivity(activity, options.quoteActivityId) : activity;
995
+ const activitySource = typeof quotedActivity === "string" ? {
996
+ type: "message",
997
+ text: quotedActivity
998
+ } : quotedActivity;
999
+ const existingChannelData = (0, openclaw_plugin_sdk_string_coerce_runtime.asOptionalRecord)(activitySource.channelData);
1000
+ const existingTenant = (0, openclaw_plugin_sdk_string_coerce_runtime.asOptionalRecord)(existingChannelData?.tenant);
1001
+ let channelData = existingChannelData ? { ...existingChannelData } : void 0;
1002
+ if (ref.tenantId) {
1003
+ channelData ??= {};
1004
+ channelData.tenant = existingTenant ? {
1005
+ ...existingTenant,
1006
+ id: ref.tenantId
1007
+ } : { id: ref.tenantId };
1008
+ }
1009
+ const activityWithRef = {
1010
+ ...activitySource,
1011
+ channelId: ref.channelId,
1012
+ from: ref.bot,
1013
+ recipient: ref.user,
1014
+ conversation: ref.conversation,
1015
+ ...channelData ? { channelData } : {},
1016
+ locale: ref.locale,
1017
+ ...ref.tenantId ? { tenantId: ref.tenantId } : {},
1018
+ ...ref.aadObjectId ? { aadObjectId: ref.aadObjectId } : {}
1019
+ };
1130
1020
  const isTargeted = activityWithRef.recipient?.isTargeted === true;
1131
1021
  if (isTargeted && ref.conversation.conversationType === "personal") throw new Error("Targeted messages are not supported in 1:1 (personal) chats.");
1132
1022
  const activityId = typeof activityWithRef.id === "string" ? activityWithRef.id : void 0;
1133
1023
  require_resolve_allowlist.assertMSTeamsSendHandoff(handoff);
1134
1024
  if (activityId) {
1135
- const res = isTargeted && activities.updateTargeted ? await activities.updateTargeted(activityId, activityWithRef) : await activities.update(activityId, activityWithRef);
1025
+ const res = isTargeted ? await activities.updateTargeted(activityId, activityWithRef) : await activities.update(activityId, activityWithRef);
1136
1026
  return {
1137
1027
  ...activityWithRef,
1138
1028
  ...res && typeof res === "object" ? res : {}
1139
1029
  };
1140
1030
  }
1141
- const res = isTargeted && activities.createTargeted ? await activities.createTargeted(activityWithRef) : await activities.create(activityWithRef);
1031
+ const res = isTargeted ? await activities.createTargeted(activityWithRef) : await activities.create(activityWithRef);
1142
1032
  const conversationId = ref.conversation.id.split(";")[0] ?? ref.conversation.id;
1143
1033
  if (res.id && activityWithRef.type === "message" && ref.conversation.conversationType === "channel" && ref.conversation.id === conversationId) recordMSTeamsSentMessage(conversationId, res.id, {
1144
1034
  accountId: options?.accountId,
@@ -1152,11 +1042,11 @@ async function sendMSTeamsActivityWithReference(app, source, activity, options)
1152
1042
  }
1153
1043
  async function updateMSTeamsActivityWithReference(app, source, activityId, activity, options) {
1154
1044
  const ref = buildSdkConversationReference(source, options);
1155
- return (await getApiClientForReference(app, ref)).conversations.activities(ref.conversation.id).update(activityId, activity);
1045
+ return getApiClientForReference(app, ref).conversations.activities(ref.conversation.id).update(activityId, activity);
1156
1046
  }
1157
1047
  async function deleteMSTeamsActivityWithReference(app, source, activityId, options) {
1158
1048
  const ref = buildSdkConversationReference(source, options);
1159
- return (await getApiClientForReference(app, ref)).conversations.activities(ref.conversation.id).delete(activityId);
1049
+ return getApiClientForReference(app, ref).conversations.activities(ref.conversation.id).delete(activityId);
1160
1050
  }
1161
1051
  //#endregion
1162
1052
  //#region extensions/msteams/src/messenger.ts
@@ -1165,7 +1055,7 @@ const MSTEAMS_SEND_RETRY_MAX_DELAY_MS = 1e4;
1165
1055
  function buildConversationReference(ref) {
1166
1056
  const conversationId = ref.conversation?.id?.trim();
1167
1057
  if (!conversationId) throw new Error("Invalid stored reference: missing conversation.id");
1168
- const agent = ref.agent ?? ref.bot ?? void 0;
1058
+ const agent = ref.agent ?? ref.bot;
1169
1059
  if (agent == null || !agent.id) throw new Error("Invalid stored reference: missing agent.id");
1170
1060
  const user = ref.user;
1171
1061
  if (!user?.id) throw new Error("Invalid stored reference: missing user.id");
@@ -1196,105 +1086,109 @@ function renderReplyPayloadsToMessages(replies, options) {
1196
1086
  const chunkMode = options.chunkMode ?? "length";
1197
1087
  for (const payload of replies) {
1198
1088
  const reply = (0, openclaw_plugin_sdk_reply_payload.resolveSendableOutboundReplyParts)(payload, { text: formatMSTeamsMarkdown(payload.text ?? "", options.tableMode) });
1199
- if (!reply.hasContent) continue;
1089
+ if (!reply.hasContent && !payload.presentation) continue;
1200
1090
  if (reply.text) {
1201
1091
  const chunks = require_runtime.getMSTeamsRuntime().channel.text.chunkMarkdownTextWithMode(reply.text, chunkLimit, chunkMode);
1202
1092
  for (const chunk of chunks) {
1203
1093
  const text = chunk.trim();
1204
- if (text && !(0, openclaw_plugin_sdk_reply_chunking.isSilentReplyText)(text, openclaw_plugin_sdk_reply_chunking.SILENT_REPLY_TOKEN)) out.push({ text });
1094
+ if (text) out.push({ text });
1205
1095
  }
1206
1096
  }
1097
+ if (payload.presentation) out.push({ adaptiveCard: require_presentation.buildMSTeamsPresentationCard({ presentation: payload.presentation }) });
1207
1098
  out.push(...reply.mediaUrls.map((mediaUrl) => ({ mediaUrl })));
1208
1099
  }
1209
1100
  return out;
1210
1101
  }
1211
- async function buildActivity(msg, conversationRef, tokenProvider, sharePointSiteId, mediaMaxBytes, options) {
1212
- const activity = buildMSTeamsMessageActivity(msg.text);
1213
- activity.channelData = { feedbackLoopEnabled: options?.feedbackLoopEnabled ?? false };
1214
- if (msg.mediaUrl) {
1215
- let contentUrl = msg.mediaUrl;
1216
- let contentType = await getMimeType(msg.mediaUrl);
1217
- let fileName = await extractFilename(msg.mediaUrl);
1218
- if (isLocalPath(msg.mediaUrl)) {
1219
- const maxBytes = mediaMaxBytes ?? 104857600;
1220
- const media = await (0, openclaw_plugin_sdk_web_media.loadWebMedia)(msg.mediaUrl, maxBytes);
1221
- contentType = media.contentType ?? contentType;
1222
- fileName = media.fileName ?? fileName;
1223
- const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(conversationRef.conversation?.conversationType);
1224
- const isPersonal = conversationType === "personal";
1225
- const isImage = media.kind === "image";
1226
- if (requiresFileConsent({
1227
- conversationType,
1228
- contentType,
1229
- bufferSize: media.buffer.length
1230
- })) {
1231
- const conversationId = conversationRef.conversation?.id ?? "unknown";
1232
- require_resolve_allowlist.assertMSTeamsSendHandoff(options);
1233
- const { activity: consentActivity, uploadId } = prepareFileConsentActivity({
1234
- media: {
1235
- buffer: media.buffer,
1236
- filename: fileName,
1237
- contentType
1238
- },
1239
- conversationId,
1240
- description: msg.text || void 0
1241
- });
1242
- consentActivity["_pendingUploadId"] = uploadId;
1243
- return consentActivity;
1244
- }
1245
- if (!isPersonal && !isImage) {
1246
- const siteId = requireMSTeamsSharePointSiteId(sharePointSiteId);
1247
- if (!tokenProvider) throw new Error("MS Teams Graph token provider unavailable for SharePoint file send");
1248
- const chatId = conversationRef.conversation?.id;
1249
- const uploaded = await uploadAndShareSharePoint({
1250
- assertDirectAdapterHandoff: options?.assertDirectAdapterHandoff,
1251
- buffer: media.buffer,
1252
- filename: fileName,
1253
- contentType,
1254
- tokenProvider,
1255
- siteId,
1256
- chatId: chatId ?? void 0,
1257
- usePerUserSharing: conversationType === "groupchat"
1258
- });
1259
- activity.attachments = [buildTeamsFileInfoCard(await getDriveItemProperties({
1260
- assertDirectAdapterHandoff: options?.assertDirectAdapterHandoff,
1261
- siteId,
1262
- itemId: uploaded.itemId,
1263
- tokenProvider
1264
- }))];
1265
- return activity;
1266
- }
1267
- const base64 = media.buffer.toString("base64");
1268
- contentUrl = `data:${media.contentType};base64,${base64}`;
1269
- }
1270
- activity.attachments = [{
1271
- name: fileName,
1272
- contentType,
1273
- contentUrl
1274
- }];
1275
- }
1276
- return activity;
1277
- }
1278
1102
  async function sendMSTeamsMessages(params) {
1279
- const messages = params.messages.filter((m) => m.text && m.text.trim().length > 0 || m.mediaUrl);
1103
+ const messages = params.messages.filter((m) => m.text && m.text.trim().length > 0 || m.mediaUrl || m.adaptiveCard);
1280
1104
  if (messages.length === 0) return [];
1281
1105
  let providerDispatchStarted = false;
1282
1106
  const sendMessageInContext = async (sendFn, message, messageIndex) => {
1283
- let activity;
1284
- let pendingUploadId;
1107
+ async function buildActivity() {
1108
+ const msg = message;
1109
+ const { conversationRef, tokenProvider, sharePointSiteId, mediaMaxBytes } = params;
1110
+ const options = {
1111
+ feedbackLoopEnabled: params.feedbackLoopEnabled,
1112
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
1113
+ };
1114
+ const activity = buildMSTeamsMessageActivity(msg.text);
1115
+ if (msg.adaptiveCard) Object.assign(activity, buildMSTeamsAdaptiveCardActivity(msg.adaptiveCard));
1116
+ activity.channelData = { feedbackLoopEnabled: options?.feedbackLoopEnabled ?? false };
1117
+ if (msg.mediaUrl) {
1118
+ let contentUrl = msg.mediaUrl;
1119
+ let contentType = await getMimeType(msg.mediaUrl);
1120
+ let fileName = await extractFilename(msg.mediaUrl);
1121
+ if (isLocalPath(msg.mediaUrl)) {
1122
+ const maxBytes = mediaMaxBytes ?? 104857600;
1123
+ const media = await (0, openclaw_plugin_sdk_web_media.loadWebMedia)(msg.mediaUrl, maxBytes);
1124
+ contentType = media.contentType ?? contentType;
1125
+ fileName = media.fileName ?? fileName;
1126
+ const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(conversationRef.conversation?.conversationType);
1127
+ const isPersonal = conversationType === "personal";
1128
+ const isImage = media.kind === "image";
1129
+ if (requiresFileConsent({
1130
+ conversationType,
1131
+ contentType,
1132
+ bufferSize: media.buffer.length
1133
+ })) {
1134
+ const conversationId = conversationRef.conversation?.id ?? "unknown";
1135
+ require_resolve_allowlist.assertMSTeamsSendHandoff(options);
1136
+ const { activity: consentActivity, uploadId } = prepareFileConsentActivity({
1137
+ media: {
1138
+ buffer: media.buffer,
1139
+ filename: fileName,
1140
+ contentType
1141
+ },
1142
+ conversationId,
1143
+ description: msg.text || void 0
1144
+ });
1145
+ return {
1146
+ activity: consentActivity,
1147
+ pendingUploadId: uploadId
1148
+ };
1149
+ }
1150
+ if (!isPersonal && !isImage) {
1151
+ const siteId = requireMSTeamsSharePointSiteId(sharePointSiteId);
1152
+ if (!tokenProvider) throw new Error("MS Teams Graph token provider unavailable for SharePoint file send");
1153
+ const chatId = conversationRef.conversation?.id;
1154
+ const uploaded = await uploadAndShareSharePoint({
1155
+ assertDirectAdapterHandoff: options?.assertDirectAdapterHandoff,
1156
+ buffer: media.buffer,
1157
+ filename: fileName,
1158
+ contentType,
1159
+ tokenProvider,
1160
+ siteId,
1161
+ chatId,
1162
+ usePerUserSharing: conversationType === "groupchat"
1163
+ });
1164
+ activity.attachments = [buildTeamsFileInfoCard(await getDriveItemProperties({
1165
+ assertDirectAdapterHandoff: options?.assertDirectAdapterHandoff,
1166
+ siteId,
1167
+ itemId: uploaded.itemId,
1168
+ tokenProvider
1169
+ }))];
1170
+ return { activity };
1171
+ }
1172
+ const base64 = media.buffer.toString("base64");
1173
+ contentUrl = `data:${media.contentType};base64,${base64}`;
1174
+ }
1175
+ activity.attachments = [{
1176
+ name: fileName,
1177
+ contentType,
1178
+ contentUrl
1179
+ }];
1180
+ }
1181
+ return { activity };
1182
+ }
1183
+ let prepared;
1285
1184
  let response;
1286
1185
  try {
1287
1186
  response = await (0, openclaw_plugin_sdk_retry_runtime.retryAsync)(async () => {
1288
1187
  require_resolve_allowlist.assertMSTeamsSendHandoff(params);
1289
- activity ??= await buildActivity(message, params.conversationRef, params.tokenProvider, params.sharePointSiteId, params.mediaMaxBytes, {
1290
- feedbackLoopEnabled: params.feedbackLoopEnabled,
1291
- assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
1292
- });
1293
- pendingUploadId ??= typeof activity["_pendingUploadId"] === "string" ? activity["_pendingUploadId"] : void 0;
1294
- delete activity["_pendingUploadId"];
1188
+ prepared ??= await buildActivity();
1295
1189
  require_resolve_allowlist.assertMSTeamsSendHandoff(params);
1296
1190
  providerDispatchStarted = true;
1297
- return await sendFn(activity);
1191
+ return await sendFn(prepared.activity);
1298
1192
  }, {
1299
1193
  attempts: MSTEAMS_SEND_ATTEMPTS,
1300
1194
  minDelayMs: 0,
@@ -1323,54 +1217,33 @@ async function sendMSTeamsMessages(params) {
1323
1217
  }
1324
1218
  const messageId = extractMessageId(response) ?? "unknown";
1325
1219
  await params.onMessageSent?.(messageId, messageIndex);
1220
+ const pendingUploadId = prepared?.pendingUploadId;
1326
1221
  if (pendingUploadId && messageId !== "unknown") setPendingUploadActivityId(pendingUploadId, messageId);
1327
1222
  return messageId;
1328
1223
  };
1329
- const sendProactively = async (batch, startIndex, threadActivityId) => {
1224
+ let sendFn;
1225
+ const ctx = params.replyStyle === "thread" ? params.context : void 0;
1226
+ if (ctx) sendFn = (activity) => require_resolve_allowlist.withMSTeamsConnectorHandoff(params, () => ctx.sendActivity(activity));
1227
+ else {
1330
1228
  let baseRef;
1331
1229
  try {
1332
1230
  baseRef = buildConversationReference(params.conversationRef);
1333
1231
  } catch (error) {
1334
- if (providerDispatchStarted) throw error;
1335
1232
  throw new openclaw_plugin_sdk_error_runtime.PlatformMessageNotDispatchedError(error instanceof Error ? error.message : "Teams conversation preparation failed", { cause: error });
1336
1233
  }
1337
1234
  const isChannel = params.conversationRef.conversation?.conversationType === "channel";
1338
- const sendFn = (activity) => sendMSTeamsActivityWithReference(params.app, baseRef, activity, {
1235
+ const threadActivityId = params.replyStyle === "thread" && isChannel ? params.conversationRef.threadId ?? params.conversationRef.activityId : void 0;
1236
+ sendFn = (activity) => sendMSTeamsActivityWithReference(params.app, baseRef, activity, {
1339
1237
  accountId: params.accountId,
1340
1238
  assertDirectAdapterHandoff: params.assertDirectAdapterHandoff,
1341
1239
  onPlatformSendDispatch: params.onPlatformSendDispatch,
1342
- threadActivityId: isChannel ? threadActivityId : void 0,
1240
+ threadActivityId,
1343
1241
  serviceUrlBoundary: params.serviceUrlBoundary
1344
1242
  });
1345
- const messageIds = [];
1346
- for (const [idx, message] of batch.entries()) messageIds.push(await sendMessageInContext(sendFn, message, startIndex + idx));
1347
- return messageIds;
1348
- };
1349
- const resolvedThreadId = params.conversationRef.threadId ?? params.conversationRef.activityId;
1350
- if (params.replyStyle === "thread") {
1351
- const ctx = params.context;
1352
- if (!ctx) return await sendProactively(messages, 0, resolvedThreadId);
1353
- const sendFn = (activity) => require_resolve_allowlist.withMSTeamsConnectorHandoff(params, () => ctx.sendActivity(activity));
1354
- const messageIds = [];
1355
- for (const [idx, message] of messages.entries()) {
1356
- const result = await withRevokedProxyFallback({
1357
- run: async () => ({
1358
- ids: [await sendMessageInContext(sendFn, message, idx)],
1359
- fellBack: false
1360
- }),
1361
- onRevoked: async () => {
1362
- return {
1363
- ids: await sendProactively(messages.slice(idx), idx, resolvedThreadId),
1364
- fellBack: true
1365
- };
1366
- }
1367
- });
1368
- messageIds.push(...result.ids);
1369
- if (result.fellBack) return messageIds;
1370
- }
1371
- return messageIds;
1372
1243
  }
1373
- return await sendProactively(messages, 0);
1244
+ const messageIds = [];
1245
+ for (const [idx, message] of messages.entries()) messageIds.push(await sendMessageInContext(sendFn, message, idx));
1246
+ return messageIds;
1374
1247
  }
1375
1248
  //#endregion
1376
1249
  //#region extensions/msteams/src/polls.ts
@@ -1445,57 +1318,51 @@ function extractMSTeamsPollVote(activity) {
1445
1318
  }
1446
1319
  function buildMSTeamsPollCard(params) {
1447
1320
  const pollId = node_crypto.default.randomUUID();
1448
- const maxSelections = typeof params.maxSelections === "number" && params.maxSelections > 1 ? Math.floor(params.maxSelections) : 1;
1321
+ const maxSelections = params.maxSelections !== void 0 && params.maxSelections > 1 ? Math.floor(params.maxSelections) : 1;
1449
1322
  const cappedMaxSelections = Math.min(Math.max(1, maxSelections), params.options.length);
1450
1323
  const choices = params.options.map((option, index) => ({
1451
1324
  title: option,
1452
1325
  value: String(index)
1453
1326
  }));
1454
1327
  const hint = cappedMaxSelections > 1 ? `Select up to ${cappedMaxSelections} options.` : "Select one option.";
1455
- const card = {
1456
- type: "AdaptiveCard",
1457
- version: "1.5",
1458
- body: [
1459
- {
1460
- type: "TextBlock",
1461
- text: params.question,
1462
- wrap: true,
1463
- weight: "Bolder",
1464
- size: "Medium"
1465
- },
1466
- {
1467
- type: "Input.ChoiceSet",
1468
- id: "choices",
1469
- isMultiSelect: cappedMaxSelections > 1,
1470
- style: "expanded",
1471
- choices
1472
- },
1473
- {
1474
- type: "TextBlock",
1475
- text: hint,
1476
- wrap: true,
1477
- isSubtle: true,
1478
- spacing: "Small"
1479
- }
1480
- ],
1481
- actions: [{
1482
- type: "Action.Execute",
1483
- title: "Vote",
1484
- verb: "openclaw.poll.vote",
1485
- data: {
1486
- openclawPollId: pollId,
1487
- pollId
1488
- }
1489
- }]
1490
- };
1491
- const fallbackLines = [`Poll: ${params.question}`, ...params.options.map((option, index) => `${index + 1}. ${option}`)];
1492
1328
  return {
1493
1329
  pollId,
1494
- question: params.question,
1495
- options: params.options,
1496
- maxSelections: cappedMaxSelections,
1497
- card,
1498
- fallbackText: fallbackLines.join("\n")
1330
+ card: {
1331
+ type: "AdaptiveCard",
1332
+ version: "1.5",
1333
+ body: [
1334
+ {
1335
+ type: "TextBlock",
1336
+ text: params.question,
1337
+ wrap: true,
1338
+ weight: "Bolder",
1339
+ size: "Medium"
1340
+ },
1341
+ {
1342
+ type: "Input.ChoiceSet",
1343
+ id: "choices",
1344
+ isMultiSelect: cappedMaxSelections > 1,
1345
+ style: "expanded",
1346
+ choices
1347
+ },
1348
+ {
1349
+ type: "TextBlock",
1350
+ text: hint,
1351
+ wrap: true,
1352
+ isSubtle: true,
1353
+ spacing: "Small"
1354
+ }
1355
+ ],
1356
+ actions: [{
1357
+ type: "Action.Execute",
1358
+ title: "Vote",
1359
+ verb: "openclaw.poll.vote",
1360
+ data: {
1361
+ openclawPollId: pollId,
1362
+ pollId
1363
+ }
1364
+ }]
1365
+ }
1499
1366
  };
1500
1367
  }
1501
1368
  function isPollExpired(poll) {
@@ -1530,59 +1397,12 @@ function createMSTeamsPollStoreState(params) {
1530
1397
  namespace: require_delegated_state.resolveMSTeamsAccountStateNamespace(MSTEAMS_POLL_VOTE_BUCKETS_NAMESPACE, params?.accountId),
1531
1398
  maxEntries: MSTEAMS_MAX_POLL_VOTE_BUCKET_ROWS
1532
1399
  });
1533
- const readPollVotes = async (pollId) => {
1534
- const votes = {};
1535
- for (const row of await voteBucketStore.entries()) if (row.value.pollId === pollId) Object.assign(votes, row.value.votes);
1536
- return votes;
1537
- };
1538
1400
  const deletePollVotes = async (pollId, cachedRows) => {
1539
1401
  for (const row of cachedRows?.values() ?? await voteBucketStore.entries()) if (row.value.pollId === pollId) {
1540
1402
  await voteBucketStore.delete(row.key);
1541
1403
  cachedRows?.delete(row.key);
1542
1404
  }
1543
1405
  };
1544
- const registerPollVotes = async (pollId, votes, updatedAt) => {
1545
- const buckets = /* @__PURE__ */ new Map();
1546
- for (const [voterId, selections] of Object.entries(votes)) {
1547
- const bucket = selectMSTeamsPollVoteBucket(pollId, voterId);
1548
- const bucketVotes = buckets.get(bucket) ?? {};
1549
- bucketVotes[voterId] = selections;
1550
- buckets.set(bucket, bucketVotes);
1551
- }
1552
- for (const [bucket, bucketVotes] of buckets) {
1553
- const key = buildMSTeamsPollVoteBucketKey(pollId, bucket);
1554
- const existing = await voteBucketStore.lookup(key);
1555
- await voteBucketStore.register(key, require_delegated_state.toPluginJsonValue({
1556
- pollId,
1557
- bucket,
1558
- votes: {
1559
- ...bucketVotes,
1560
- ...existing?.votes
1561
- },
1562
- updatedAt
1563
- }));
1564
- }
1565
- };
1566
- const registerPollVote = async (pollId, voterId, selections, updatedAt) => {
1567
- const bucket = selectMSTeamsPollVoteBucket(pollId, voterId);
1568
- const key = buildMSTeamsPollVoteBucketKey(pollId, bucket);
1569
- const existing = await voteBucketStore.lookup(key);
1570
- await voteBucketStore.register(key, require_delegated_state.toPluginJsonValue({
1571
- pollId,
1572
- bucket,
1573
- votes: {
1574
- ...existing?.votes,
1575
- [voterId]: selections
1576
- },
1577
- updatedAt
1578
- }));
1579
- };
1580
- const reconstructPoll = async (metadata) => {
1581
- return {
1582
- ...metadata,
1583
- votes: await readPollVotes(metadata.id)
1584
- };
1585
- };
1586
1406
  const prunePollStoreToLimit = async () => {
1587
1407
  let voteRows;
1588
1408
  const deletePrunedPollVotes = async (pollId) => {
@@ -1609,10 +1429,8 @@ function createMSTeamsPollStoreState(params) {
1609
1429
  };
1610
1430
  const createPoll = async (poll) => {
1611
1431
  await require_delegated_state.withMSTeamsSqliteMutationLock(namespace, async () => {
1612
- const { votes, ...metadata } = poll;
1613
- await pollStore.register(buildMSTeamsPollStateKey(poll.id), require_delegated_state.toPluginJsonValue(metadata));
1432
+ await pollStore.register(buildMSTeamsPollStateKey(poll.id), require_delegated_state.toPluginJsonValue(poll));
1614
1433
  await deletePollVotes(poll.id);
1615
- await registerPollVotes(poll.id, votes, poll.updatedAt ?? poll.createdAt);
1616
1434
  await prunePollStoreToLimit();
1617
1435
  });
1618
1436
  };
@@ -1620,7 +1438,7 @@ function createMSTeamsPollStoreState(params) {
1620
1438
  const poll = await pollStore.lookup(buildMSTeamsPollStateKey(pollId));
1621
1439
  if (!poll) return null;
1622
1440
  if (isPollExpired(poll)) return null;
1623
- return await reconstructPoll(poll);
1441
+ return poll;
1624
1442
  };
1625
1443
  const recordVote = async (vote) => {
1626
1444
  return await require_delegated_state.withMSTeamsSqliteMutationLock(namespace, async () => {
@@ -1632,13 +1450,26 @@ function createMSTeamsPollStoreState(params) {
1632
1450
  await deletePollVotes(vote.pollId);
1633
1451
  return null;
1634
1452
  }
1635
- const normalized = normalizeMSTeamsPollSelections(await reconstructPoll(poll), vote.selections);
1453
+ const normalized = normalizeMSTeamsPollSelections(poll, vote.selections);
1636
1454
  const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1637
1455
  poll.updatedAt = updatedAt;
1638
- await pollStore.register(pollKey, require_delegated_state.toPluginJsonValue(poll));
1639
- await registerPollVote(vote.pollId, vote.voterId, normalized, updatedAt);
1456
+ await pollStore.register(pollKey, poll);
1457
+ const pollId = vote.pollId;
1458
+ const voterId = vote.voterId;
1459
+ const bucket = selectMSTeamsPollVoteBucket(pollId, voterId);
1460
+ const key = buildMSTeamsPollVoteBucketKey(pollId, bucket);
1461
+ const existing = await voteBucketStore.lookup(key);
1462
+ await voteBucketStore.register(key, {
1463
+ pollId,
1464
+ bucket,
1465
+ votes: {
1466
+ ...existing?.votes,
1467
+ [voterId]: normalized
1468
+ },
1469
+ updatedAt
1470
+ });
1640
1471
  await prunePollStoreToLimit();
1641
- return await reconstructPoll(poll);
1472
+ return true;
1642
1473
  });
1643
1474
  };
1644
1475
  return {
@@ -1648,45 +1479,11 @@ function createMSTeamsPollStoreState(params) {
1648
1479
  };
1649
1480
  }
1650
1481
  //#endregion
1651
- //#region extensions/msteams/src/conversation-store-helpers.ts
1652
- function mergeStoredConversationReference(existing, incoming, nowIso) {
1653
- return {
1654
- ...existing?.timezone && !incoming.timezone ? { timezone: existing.timezone } : {},
1655
- ...existing?.tenantId && !incoming.tenantId ? { tenantId: existing.tenantId } : {},
1656
- ...existing?.aadObjectId && !incoming.aadObjectId ? { aadObjectId: existing.aadObjectId } : {},
1657
- ...incoming,
1658
- lastSeenAt: nowIso
1659
- };
1660
- }
1661
- function findPreferredDmConversationByUserId(entries, id) {
1662
- const target = id.trim();
1663
- if (!target) return null;
1664
- let preferred = null;
1665
- let preferredIsPersonal = false;
1666
- let preferredTimestamp = 0;
1667
- for (const entry of entries) {
1668
- if (entry.reference.user?.aadObjectId !== target && entry.reference.user?.id !== target) continue;
1669
- const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeLowercaseStringOrEmpty)(entry.reference.conversation?.conversationType);
1670
- if (convType === "channel" || convType === "groupchat") continue;
1671
- const isPersonal = convType === "personal";
1672
- const timestamp = (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(entry.reference.lastSeenAt) ?? 0;
1673
- if (!preferred || isPersonal && !preferredIsPersonal || isPersonal === preferredIsPersonal && timestamp > preferredTimestamp) {
1674
- preferred = entry;
1675
- preferredIsPersonal = isPersonal;
1676
- preferredTimestamp = timestamp;
1677
- }
1678
- }
1679
- return preferred;
1680
- }
1681
- //#endregion
1682
1482
  //#region extensions/msteams/src/conversation-store-state.ts
1683
1483
  const MSTEAMS_CONVERSATIONS_NAMESPACE = "conversations";
1684
1484
  const MSTEAMS_MAX_CONVERSATIONS = 1e3;
1685
1485
  const MSTEAMS_SQLITE_MAX_CONVERSATION_ROWS = 2e3;
1686
1486
  const MSTEAMS_CONVERSATION_TTL_MS = 31536e6;
1687
- function buildMSTeamsConversationStateKey(conversationId) {
1688
- return node_crypto.default.createHash("sha256").update(conversationId).digest("hex");
1689
- }
1690
1487
  function getStoredConversationId(reference) {
1691
1488
  const rawId = reference.conversation?.id;
1692
1489
  return rawId ? require_resolve_allowlist.normalizeMSTeamsConversationId(rawId) : null;
@@ -1703,7 +1500,7 @@ function createMSTeamsConversationStoreState(params) {
1703
1500
  };
1704
1501
  const lookupStored = async (conversationId) => {
1705
1502
  const normalizedId = require_resolve_allowlist.normalizeMSTeamsConversationId(conversationId);
1706
- const value = await conversationStore.lookup(buildMSTeamsConversationStateKey(normalizedId));
1503
+ const value = await conversationStore.lookup((0, openclaw_plugin_sdk_node_crypto_runtime.sha256Hex)(normalizedId));
1707
1504
  return value && !isExpired(value) ? value : null;
1708
1505
  };
1709
1506
  const list = async () => {
@@ -1721,7 +1518,7 @@ function createMSTeamsConversationStoreState(params) {
1721
1518
  };
1722
1519
  const register = async (conversationId, reference) => {
1723
1520
  const normalizedId = require_resolve_allowlist.normalizeMSTeamsConversationId(conversationId);
1724
- await conversationStore.register(buildMSTeamsConversationStateKey(normalizedId), require_delegated_state.toPluginJsonValue({
1521
+ await conversationStore.register((0, openclaw_plugin_sdk_node_crypto_runtime.sha256Hex)(normalizedId), require_delegated_state.toPluginJsonValue({
1725
1522
  ...reference,
1726
1523
  conversation: {
1727
1524
  ...reference.conversation,
@@ -1737,29 +1534,54 @@ function createMSTeamsConversationStoreState(params) {
1737
1534
  rows.push(row);
1738
1535
  }
1739
1536
  if (rows.length <= MSTEAMS_MAX_CONVERSATIONS) return;
1740
- const sorted = rows.toSorted((a, b) => {
1537
+ rows.sort((a, b) => {
1741
1538
  const aTs = (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(a.value.lastSeenAt) ?? 0;
1742
1539
  const bTs = (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(b.value.lastSeenAt) ?? 0;
1743
1540
  const aId = getStoredConversationId(a.value) ?? a.key;
1744
1541
  const bId = getStoredConversationId(b.value) ?? b.key;
1745
1542
  return aTs - bTs || aId.localeCompare(bId);
1746
1543
  });
1747
- for (const row of sorted.slice(0, rows.length - MSTEAMS_MAX_CONVERSATIONS)) await conversationStore.delete(row.key);
1544
+ for (const row of rows.slice(0, rows.length - MSTEAMS_MAX_CONVERSATIONS)) await conversationStore.delete(row.key);
1748
1545
  };
1749
1546
  const findPreferredDmByUserId = async (id) => {
1750
- return findPreferredDmConversationByUserId(await list(), id);
1547
+ const entries = await list();
1548
+ const target = id.trim();
1549
+ if (!target) return null;
1550
+ let preferred = null;
1551
+ let preferredIsPersonal = false;
1552
+ let preferredTimestamp = 0;
1553
+ for (const entry of entries) {
1554
+ if (entry.reference.user?.aadObjectId !== target && entry.reference.user?.id !== target) continue;
1555
+ const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeLowercaseStringOrEmpty)(entry.reference.conversation?.conversationType);
1556
+ if (convType === "channel" || convType === "groupchat") continue;
1557
+ const isPersonal = convType === "personal";
1558
+ const timestamp = (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(entry.reference.lastSeenAt) ?? 0;
1559
+ if (!preferred || isPersonal && !preferredIsPersonal || isPersonal === preferredIsPersonal && timestamp > preferredTimestamp) {
1560
+ preferred = entry;
1561
+ preferredIsPersonal = isPersonal;
1562
+ preferredTimestamp = timestamp;
1563
+ }
1564
+ }
1565
+ return preferred;
1751
1566
  };
1752
1567
  const upsert = async (conversationId, reference) => {
1753
1568
  const normalizedId = require_resolve_allowlist.normalizeMSTeamsConversationId(conversationId);
1754
1569
  await require_delegated_state.withMSTeamsSqliteMutationLock(namespace, async () => {
1755
- const existing = await lookupStored(normalizedId);
1756
- await register(normalizedId, mergeStoredConversationReference(existing ?? void 0, reference, (/* @__PURE__ */ new Date()).toISOString()));
1570
+ const previous = await lookupStored(normalizedId) ?? void 0;
1571
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
1572
+ await register(normalizedId, {
1573
+ ...previous?.timezone && !reference.timezone ? { timezone: previous.timezone } : {},
1574
+ ...previous?.tenantId && !reference.tenantId ? { tenantId: previous.tenantId } : {},
1575
+ ...previous?.aadObjectId && !reference.aadObjectId ? { aadObjectId: previous.aadObjectId } : {},
1576
+ ...reference,
1577
+ lastSeenAt: nowIso
1578
+ });
1757
1579
  });
1758
1580
  };
1759
1581
  const remove = async (conversationId) => {
1760
1582
  const normalizedId = require_resolve_allowlist.normalizeMSTeamsConversationId(conversationId);
1761
1583
  return await require_delegated_state.withMSTeamsSqliteMutationLock(namespace, async () => {
1762
- return await conversationStore.delete(buildMSTeamsConversationStateKey(normalizedId));
1584
+ return await conversationStore.delete((0, openclaw_plugin_sdk_node_crypto_runtime.sha256Hex)(normalizedId));
1763
1585
  });
1764
1586
  };
1765
1587
  return {
@@ -1771,12 +1593,6 @@ function createMSTeamsConversationStoreState(params) {
1771
1593
  };
1772
1594
  }
1773
1595
  //#endregion
1774
- Object.defineProperty(exports, "FILE_CONSENT_THRESHOLD_BYTES", {
1775
- enumerable: true,
1776
- get: function() {
1777
- return FILE_CONSENT_THRESHOLD_BYTES;
1778
- }
1779
- });
1780
1596
  Object.defineProperty(exports, "MSTEAMS_MAX_MEDIA_BYTES", {
1781
1597
  enumerable: true,
1782
1598
  get: function() {
@@ -1975,9 +1791,3 @@ Object.defineProperty(exports, "wasMSTeamsMessageSentWithPersistence", {
1975
1791
  return wasMSTeamsMessageSentWithPersistence;
1976
1792
  }
1977
1793
  });
1978
- Object.defineProperty(exports, "withRevokedProxyFallback", {
1979
- enumerable: true,
1980
- get: function() {
1981
- return withRevokedProxyFallback;
1982
- }
1983
- });