@openclaw/msteams 2026.10.10 → 2026.11.5-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/.setup/{adapter.runtime-BMvpm7sS.cjs → adapter.runtime-UOD_H_ph.cjs} +20 -23
  2. package/dist/.setup/{approval-card-Bo8kHYbj.cjs → approval-card-CD5q0Nla.cjs} +5 -9
  3. package/dist/.setup/{approval-handler.runtime-BS3avHFu.cjs → approval-handler.runtime-DbPxvynk.cjs} +4 -4
  4. package/dist/.setup/{channel-CdCSk3Rg.cjs → channel-DQrXOfhf.cjs} +24 -224
  5. package/dist/.setup/{channel.runtime--ZjTYgkY.cjs → channel.runtime-Du4GPMES.cjs} +65 -106
  6. package/dist/.setup/{channel.setup-nSTtK77J.cjs → channel.setup--7ko_Kxv.cjs} +161 -262
  7. package/dist/.setup/{config-schema-Bm15lF-j.cjs → config-schema-BEXDIOC-.cjs} +120 -123
  8. package/dist/.setup/{conversation-store-state-NJU9osd-.cjs → conversation-store-state-AVgRyNE9.cjs} +411 -598
  9. package/dist/.setup/{delegated-state-CbMd3rIh.cjs → delegated-state-DhIHahf1.cjs} +68 -6
  10. package/dist/.setup/{monitor-BxLDs5fk.cjs → monitor-CXjIKz7o.cjs} +1883 -2284
  11. package/dist/.setup/{oauth-B-Y5pNM4.cjs → oauth-BWG6qWCd.cjs} +10 -20
  12. package/dist/.setup/presentation-CaLawc5j.cjs +211 -0
  13. package/dist/.setup/{reaction-types-BaL1EVBS.cjs → reaction-types-DOzN5oTl.cjs} +1 -1
  14. package/dist/.setup/{resolve-allowlist-CqNzS9bZ.cjs → resolve-allowlist-Drj1jaq0.cjs} +213 -90
  15. package/dist/.setup/{secret-contract-api-BOzdWSn9.cjs → secret-contract-api-DFqDvc_a.cjs} +4 -6
  16. package/dist/.setup/{secret-contract-api-5aH1Fl_c.cjs → secret-contract-api-YGHrfAT0.cjs} +1 -1
  17. package/dist/.setup/{send-BXjI3ufq.cjs → send-BRUztzVJ.cjs} +86 -182
  18. package/dist/.setup/{setup-plugin-api-lPpin6Gg.cjs → setup-plugin-api-BHEfter5.cjs} +1 -1
  19. package/dist/.setup/{thread-session-DZMoCsSR.cjs → thread-session-BUUV1zk_.cjs} +4 -6
  20. package/dist/.setup/{token-config-DTZ00Ibq.cjs → token-config-7K6UbNCo.cjs} +0 -2
  21. package/dist/.source-module-urls/7e43ee3ae4faab430a47269c8217f8d99ec60ecd85233f40e81d03efeb543068.json +1 -1
  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 +6 -13
  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-CiKaZLfC.cjs");
2
2
  const require_bot_framework_service_url = require("./bot-framework-service-url-CgVuKiIR.cjs");
3
- const require_resolve_allowlist = require("./resolve-allowlist-CqNzS9bZ.cjs");
3
+ const require_resolve_allowlist = require("./resolve-allowlist-Drj1jaq0.cjs");
4
4
  const require_runtime = require("./runtime-CbKazLGv.cjs");
5
5
  require("../runtime-api.cjs");
6
- const require_delegated_state = require("./delegated-state-CbMd3rIh.cjs");
7
- const require_channel_setup = require("./channel.setup-nSTtK77J.cjs");
6
+ const require_delegated_state = require("./delegated-state-DhIHahf1.cjs");
7
+ const require_presentation = require("./presentation-CaLawc5j.cjs");
8
+ const require_channel_setup = require("./channel.setup--7ko_Kxv.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}`);
@@ -145,13 +139,13 @@ const PENDING_UPLOAD_META_NAMESPACE = "pending-uploads";
145
139
  const PENDING_UPLOAD_CHUNKS_NAMESPACE = "pending-upload-chunks";
146
140
  const PENDING_UPLOAD_MUTATION_KEY = "pending-uploads";
147
141
  function createMetaStore() {
148
- return require_runtime.getMSTeamsRuntime().state.openKeyedStore({
142
+ return require_runtime.getMSTeamsRuntime().state.openKeyedStoreV2({
149
143
  namespace: PENDING_UPLOAD_META_NAMESPACE,
150
144
  maxEntries: PENDING_UPLOAD_META_MAX_ENTRIES
151
145
  });
152
146
  }
153
147
  function createChunkStore() {
154
- return require_runtime.getMSTeamsRuntime().state.openKeyedStore({
148
+ return require_runtime.getMSTeamsRuntime().state.openKeyedStoreV2({
155
149
  namespace: PENDING_UPLOAD_CHUNKS_NAMESPACE,
156
150
  maxEntries: MAX_PENDING_UPLOAD_CHUNK_ROWS
157
151
  });
@@ -165,17 +159,6 @@ function buildMetaKey(id) {
165
159
  function buildChunkKey(id, index) {
166
160
  return `${buildUploadKey(id)}:chunk:${String(index).padStart(4, "0")}`;
167
161
  }
168
- function recordToUpload(record, buffer) {
169
- return {
170
- id: record.id,
171
- buffer,
172
- filename: record.filename,
173
- contentType: record.contentType,
174
- conversationId: record.conversationId,
175
- consentCardActivityId: record.consentCardActivityId,
176
- createdAt: record.createdAt
177
- };
178
- }
179
162
  async function deleteUploadRows(id, metaStore, chunkStore) {
180
163
  const existing = await metaStore.lookup(buildMetaKey(id));
181
164
  await metaStore.delete(buildMetaKey(id));
@@ -191,11 +174,11 @@ async function registerUploadRows(record, metaStore, chunkStore) {
191
174
  await pruneUploadStore(metaStore, chunkStore, chunkCount);
192
175
  for (let index = 0; index < chunkCount; index += 1) {
193
176
  const chunk = buffer.subarray(index * RAW_CHUNK_BYTES, (index + 1) * RAW_CHUNK_BYTES);
194
- await chunkStore.register(buildChunkKey(record.id, index), require_delegated_state.toPluginJsonValue({
177
+ await chunkStore.register(buildChunkKey(record.id, index), {
195
178
  id: record.id,
196
179
  index,
197
180
  dataBase64: chunk.toString("base64")
198
- }));
181
+ });
199
182
  }
200
183
  await metaStore.register(buildMetaKey(record.id), require_delegated_state.toPluginJsonValue({
201
184
  id: record.id,
@@ -212,16 +195,25 @@ async function readUploadRows(id, metaStore, chunkStore) {
212
195
  const meta = await metaStore.lookup(buildMetaKey(id));
213
196
  if (!meta) return;
214
197
  if (!Number.isSafeInteger(meta.chunkCount) || meta.chunkCount < 1 || meta.chunkCount > MAX_CHUNKS_PER_UPLOAD) return;
215
- const records = await chunkStore.lookupMany?.(Array.from({ length: meta.chunkCount }, (_, index) => buildChunkKey(id, index)));
198
+ const records = await chunkStore.lookupMany(Array.from({ length: meta.chunkCount }, (_, index) => buildChunkKey(id, index)));
216
199
  const chunks = [];
217
200
  for (let index = 0; index < meta.chunkCount; index += 1) {
218
- const result = records?.[index];
201
+ const result = records[index];
219
202
  if (result && !result.ok) throw result.error;
220
- const chunk = records ? result?.value : await chunkStore.lookup(buildChunkKey(id, index));
203
+ const chunk = result?.value;
221
204
  if (!chunk || chunk.id !== id || chunk.index !== index) return;
222
205
  chunks.push(Buffer.from(chunk.dataBase64, "base64"));
223
206
  }
224
- return recordToUpload(meta, Buffer.concat(chunks, meta.byteLength));
207
+ const buffer = Buffer.concat(chunks, meta.byteLength);
208
+ return {
209
+ id: meta.id,
210
+ buffer,
211
+ filename: meta.filename,
212
+ contentType: meta.contentType,
213
+ conversationId: meta.conversationId,
214
+ consentCardActivityId: meta.consentCardActivityId,
215
+ createdAt: meta.createdAt
216
+ };
225
217
  }
226
218
  async function pruneUploadStore(metaStore, chunkStore, extraChunkRows = 0) {
227
219
  const rows = await metaStore.entries();
@@ -289,10 +281,10 @@ async function setPendingUploadActivityIdFs(id, activityId) {
289
281
  await require_delegated_state.withMSTeamsSqliteMutationLock(PENDING_UPLOAD_MUTATION_KEY, async () => {
290
282
  const record = await metaStore.lookup(buildMetaKey(id));
291
283
  if (!record || Date.now() - record.createdAt > PENDING_UPLOAD_TTL_MS$1) return;
292
- await metaStore.register(buildMetaKey(id), require_delegated_state.toPluginJsonValue({
284
+ await metaStore.register(buildMetaKey(id), {
293
285
  ...record,
294
286
  consentCardActivityId: activityId
295
- }));
287
+ });
296
288
  });
297
289
  }
298
290
  // extensions/msteams/src/pending-uploads.ts
@@ -303,14 +295,9 @@ async function setPendingUploadActivityIdFs(id, activityId) {
303
295
  * before upload. This module stores the file data temporarily until the user
304
296
  * accepts or declines, or until the TTL expires.
305
297
  */
306
- const { pendingUploads, pendingUploadTimers } = (0, openclaw_plugin_sdk_global_singleton.resolveGlobalSingleton)(Symbol.for("openclaw.msteams.pendingUploadState"), () => ({
307
- pendingUploads: /* @__PURE__ */ new Map(),
308
- /** Timer handles keyed by upload ID, cleared on explicit removal to prevent ghost cleanup. */
309
- pendingUploadTimers: /* @__PURE__ */ new Map()
310
- }), (state) => {
311
- for (const timer of state.pendingUploadTimers.values()) clearTimeout(timer);
312
- state.pendingUploadTimers.clear();
313
- state.pendingUploads.clear();
298
+ const pendingUploads = (0, openclaw_plugin_sdk_global_singleton.resolveGlobalSingleton)(Symbol.for("openclaw.msteams.pendingUploadState"), () => /* @__PURE__ */ new Map(), (state) => {
299
+ for (const { timer } of state.values()) clearTimeout(timer);
300
+ state.clear();
314
301
  });
315
302
  const PENDING_UPLOAD_TTL_MS = 3e5;
316
303
  /**
@@ -324,17 +311,18 @@ function storePendingUpload(upload) {
324
311
  id,
325
312
  createdAt: Date.now()
326
313
  };
327
- pendingUploads.set(id, entry);
328
314
  const timer = setTimeout(() => {
329
315
  pendingUploads.delete(id);
330
- pendingUploadTimers.delete(id);
331
316
  }, PENDING_UPLOAD_TTL_MS);
332
- pendingUploadTimers.set(id, timer);
317
+ pendingUploads.set(id, {
318
+ upload: entry,
319
+ timer
320
+ });
333
321
  return id;
334
322
  }
335
323
  function getPendingUpload(id) {
336
324
  if (!id) return;
337
- const entry = pendingUploads.get(id);
325
+ const entry = pendingUploads.get(id)?.upload;
338
326
  if (!entry) return;
339
327
  if (Date.now() - entry.createdAt > PENDING_UPLOAD_TTL_MS) {
340
328
  removePendingUpload(id);
@@ -348,16 +336,12 @@ function getPendingUpload(id) {
348
336
  */
349
337
  function removePendingUpload(id) {
350
338
  if (id) {
339
+ clearTimeout(pendingUploads.get(id)?.timer);
351
340
  pendingUploads.delete(id);
352
- const timer = pendingUploadTimers.get(id);
353
- if (timer !== void 0) {
354
- clearTimeout(timer);
355
- pendingUploadTimers.delete(id);
356
- }
357
341
  }
358
342
  }
359
343
  function setPendingUploadActivityId(uploadId, activityId) {
360
- const entry = pendingUploads.get(uploadId);
344
+ const entry = pendingUploads.get(uploadId)?.upload;
361
345
  if (entry) entry.consentCardActivityId = activityId;
362
346
  }
363
347
  // extensions/msteams/src/file-consent-helpers.ts
@@ -466,33 +450,6 @@ function rewriteMarkdownIR(ir, edits) {
466
450
  })) } : {}
467
451
  };
468
452
  }
469
- function prefixMSTeamsBlockquotes(ir) {
470
- const rewritten = rewriteMarkdownIR(ir, ir.styles.filter((span) => span.style === "blockquote").flatMap((span) => {
471
- const positions = [span.start];
472
- for (let index = span.start; index < span.end; index += 1) if (ir.text[index] === "\n" && index + 1 < span.end) positions.push(index + 1);
473
- return positions.map((position) => ({
474
- start: position,
475
- end: position,
476
- text: "> "
477
- }));
478
- }));
479
- return {
480
- ...rewritten,
481
- styles: rewritten.styles.filter((span) => span.style !== "blockquote")
482
- };
483
- }
484
- function longestBacktickRun(text) {
485
- return Math.max(0, ...text.match(/`+/gu)?.map((run) => run.length) ?? []);
486
- }
487
- function renderMSTeamsCode(style, text) {
488
- const marker = "`".repeat(Math.max(style === "code_block" ? 3 : 1, longestBacktickRun(text) + 1));
489
- if (style === "code_block") return `${marker}\n${text}${marker}`;
490
- const needsPadding = text.startsWith("`") || text.endsWith("`") || text.startsWith(" ") && text.endsWith(" ") && text.trim().length > 0;
491
- return `${marker}${needsPadding ? " " : ""}${text}${needsPadding ? " " : ""}${marker}`;
492
- }
493
- function serializeMarkdownDestination(href) {
494
- return `<${href.replace(/([\\<>])/gu, "\\$1")}>`;
495
- }
496
453
  function blankBlockEnd(text, index) {
497
454
  if (text[index] !== "\n" && text[index] !== "\r") return;
498
455
  const match = /^(?:\r?\n)[ \t]*(?:\r?\n)/u.exec(text.slice(index));
@@ -552,74 +509,6 @@ function protectDelimitedMarkdown(text, opener, tokenPrefix, values) {
552
509
  }
553
510
  return protectedText + text.slice(cursor);
554
511
  }
555
- function protectRawTables(text, tokenPrefix, rawTables) {
556
- if (!text.includes("|")) return text;
557
- const { tables } = (0, openclaw_plugin_sdk_text_chunking.markdownToIRWithMeta)(text, {
558
- autolink: false,
559
- linkify: false,
560
- tableMode: "block"
561
- });
562
- let result = "";
563
- let cursor = 0;
564
- for (const table of tables) {
565
- const source = (0, openclaw_plugin_sdk_expect_runtime.expectDefined)((0, openclaw_plugin_sdk_markdown_table_runtime.getMarkdownTableSource)(table), "Markdown table source");
566
- const lineStart = Math.max(text.lastIndexOf("\n", source.start - 1), text.lastIndexOf("\r", source.start - 1)) + 1;
567
- const start = lineStart + (/^(?: {0,3}>[ \t]?)*/u.exec(text.slice(lineStart, source.start))?.[0] ?? "").length;
568
- const end = source.end + (text.slice(source.end, source.end + 2) === "\r\n" ? 1 : 0);
569
- const index = rawTables.push(text.slice(start, end)) - 1;
570
- result += text.slice(cursor, start) + `${tokenPrefix}t${index}${TOKEN_END}`;
571
- cursor = end;
572
- }
573
- return result + text.slice(cursor);
574
- }
575
- function protectMSTeamsCode(ir, tokenPrefix, code, protectedValues) {
576
- const codeSpans = ir.styles.filter((span) => span.style === "code" || span.style === "code_block");
577
- const codeBlocks = codeSpans.filter((span) => span.style === "code_block");
578
- const adjustedStyles = ir.styles.flatMap((span) => {
579
- if (span.style !== "blockquote") return [span];
580
- let segments = [span];
581
- for (const codeBlock of codeBlocks) segments = segments.flatMap((segment) => {
582
- if (codeBlock.end <= segment.start || codeBlock.start >= segment.end) return [segment];
583
- return [...segment.start < codeBlock.start ? [{
584
- ...segment,
585
- end: codeBlock.start
586
- }] : [], ...codeBlock.end < segment.end ? [{
587
- ...segment,
588
- start: codeBlock.end
589
- }] : []];
590
- });
591
- return segments;
592
- });
593
- const rewritten = rewriteMarkdownIR({
594
- ...ir,
595
- styles: adjustedStyles
596
- }, codeSpans.map((span) => {
597
- const codeStyle = span.style === "code_block" ? "code_block" : "code";
598
- const source = protectedValues.reduce((text, protectedValue) => restoreTokens(text, protectedValue.prefix, protectedValue.values), ir.text.slice(span.start, span.end));
599
- let quoteDepth = 0;
600
- let hasTrailingQuotedText = false;
601
- if (codeStyle === "code_block") {
602
- for (const candidate of ir.styles) if (candidate.style === "blockquote" && span.start >= candidate.start) {
603
- if (span.start < candidate.end) quoteDepth += 1;
604
- hasTrailingQuotedText ||= candidate.end > span.end;
605
- }
606
- }
607
- const rendered = renderMSTeamsCode(codeStyle, source);
608
- let quoted = quoteDepth > 0 ? `${"> ".repeat(quoteDepth)}${rendered.replaceAll("\n", `\n${"> ".repeat(quoteDepth)}`)}` : rendered;
609
- const followedByText = codeStyle === "code_block" && span.end < ir.text.length && ir.text[span.end] !== "\n";
610
- if ((hasTrailingQuotedText || followedByText) && !quoted.endsWith("\n")) quoted += "\n";
611
- const index = code.push(quoted) - 1;
612
- return {
613
- start: span.start,
614
- end: span.end,
615
- text: `${tokenPrefix}c${index}${TOKEN_END}`
616
- };
617
- }));
618
- return {
619
- ...rewritten,
620
- styles: rewritten.styles.filter((span) => span.style !== "code" && span.style !== "code_block")
621
- };
622
- }
623
512
  function formatMSTeamsMarkdown(markdown, tableMode) {
624
513
  if (markdown === "") return markdown;
625
514
  const rawTables = [];
@@ -634,7 +523,27 @@ function formatMSTeamsMarkdown(markdown, tableMode) {
634
523
  return `${tokenPrefix}h${index}${TOKEN_END}`;
635
524
  }), "![", `${tokenPrefix}i`, images), "@[", `${tokenPrefix}m`, mentions);
636
525
  const tableInput = (0, openclaw_plugin_sdk_text_chunking.convertMarkdownTables)(mentionsProtected, tableMode);
637
- const protectedMarkdown = (tableMode === "off" ? protectRawTables(tableInput, tokenPrefix, rawTables) : tableInput).replace(ESCAPED_MARKDOWN_RE, (escaped) => {
526
+ let converted = tableInput;
527
+ if (tableMode === "off" && tableInput.includes("|")) {
528
+ const { tables } = (0, openclaw_plugin_sdk_text_chunking.markdownToIRWithMeta)(tableInput, {
529
+ autolink: false,
530
+ linkify: false,
531
+ tableMode: "block"
532
+ });
533
+ let result = "";
534
+ let cursor = 0;
535
+ for (const table of tables) {
536
+ const source = (0, openclaw_plugin_sdk_expect_runtime.expectDefined)((0, openclaw_plugin_sdk_markdown_table_runtime.getMarkdownTableSource)(table), "Markdown table source");
537
+ const lineStart = Math.max(tableInput.lastIndexOf("\n", source.start - 1), tableInput.lastIndexOf("\r", source.start - 1)) + 1;
538
+ const start = lineStart + (/^(?: {0,3}>[ \t]?)*/u.exec(tableInput.slice(lineStart, source.start))?.[0] ?? "").length;
539
+ const end = source.end + (tableInput.slice(source.end, source.end + 2) === "\r\n" ? 1 : 0);
540
+ const index = rawTables.push(tableInput.slice(start, end)) - 1;
541
+ result += tableInput.slice(cursor, start) + `${tokenPrefix}t${index}${TOKEN_END}`;
542
+ cursor = end;
543
+ }
544
+ converted = result + tableInput.slice(cursor);
545
+ }
546
+ const protectedMarkdown = converted.replace(ESCAPED_MARKDOWN_RE, (escaped) => {
638
547
  const index = escapedMarkdown.push(escaped) - 1;
639
548
  return `${tokenPrefix}e${index}${TOKEN_END}`;
640
549
  });
@@ -668,7 +577,71 @@ function formatMSTeamsMarkdown(markdown, tableMode) {
668
577
  values: entities
669
578
  }
670
579
  ];
671
- const ir = prefixMSTeamsBlockquotes(protectMSTeamsCode(parsed, tokenPrefix, codeRegions, protectedValues));
580
+ const codeSpans = parsed.styles.filter((span) => span.style === "code" || span.style === "code_block");
581
+ const codeBlocks = codeSpans.filter((span) => span.style === "code_block");
582
+ const adjustedStyles = parsed.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 codeRewritten = rewriteMarkdownIR({
598
+ ...parsed,
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), parsed.text.slice(span.start, span.end));
603
+ let quoteDepth = 0;
604
+ let hasTrailingQuotedText = false;
605
+ if (codeStyle === "code_block") {
606
+ for (const candidate of parsed.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 marker = "`".repeat(Math.max(codeStyle === "code_block" ? 3 : 1, Math.max(0, ...source.match(/`+/gu)?.map((run) => run.length) ?? []) + 1));
612
+ let rendered;
613
+ if (codeStyle === "code_block") rendered = `${marker}\n${source}${marker}`;
614
+ else {
615
+ const needsPadding = source.startsWith("`") || source.endsWith("`") || source.startsWith(" ") && source.endsWith(" ") && source.trim().length > 0;
616
+ rendered = `${marker}${needsPadding ? " " : ""}${source}${needsPadding ? " " : ""}${marker}`;
617
+ }
618
+ let quoted = quoteDepth > 0 ? `${"> ".repeat(quoteDepth)}${rendered.replaceAll("\n", `\n${"> ".repeat(quoteDepth)}`)}` : rendered;
619
+ const followedByText = codeStyle === "code_block" && span.end < parsed.text.length && parsed.text[span.end] !== "\n";
620
+ if ((hasTrailingQuotedText || followedByText) && !quoted.endsWith("\n")) quoted += "\n";
621
+ const index = codeRegions.push(quoted) - 1;
622
+ return {
623
+ start: span.start,
624
+ end: span.end,
625
+ text: `${tokenPrefix}c${index}${TOKEN_END}`
626
+ };
627
+ }));
628
+ const codeProtected = {
629
+ ...codeRewritten,
630
+ styles: codeRewritten.styles.filter((span) => span.style !== "code" && span.style !== "code_block")
631
+ };
632
+ const rewritten = rewriteMarkdownIR(codeProtected, codeProtected.styles.filter((span) => span.style === "blockquote").flatMap((span) => {
633
+ const positions = [span.start];
634
+ for (let index = span.start; index < span.end; index += 1) if (codeProtected.text[index] === "\n" && index + 1 < span.end) positions.push(index + 1);
635
+ return positions.map((position) => ({
636
+ start: position,
637
+ end: position,
638
+ text: "> "
639
+ }));
640
+ }));
641
+ const ir = {
642
+ ...rewritten,
643
+ styles: rewritten.styles.filter((span) => span.style !== "blockquote")
644
+ };
672
645
  const rendered = (0, openclaw_plugin_sdk_text_chunking.renderMarkdownWithMarkers)(ir, {
673
646
  styleMarkers: MSTEAMS_MARKERS,
674
647
  escapeText: (text) => text,
@@ -676,7 +649,7 @@ function formatMSTeamsMarkdown(markdown, tableMode) {
676
649
  start: link.start,
677
650
  end: link.end,
678
651
  open: "[",
679
- close: `](${serializeMarkdownDestination(link.href)})`
652
+ close: `](<${link.href.replace(/([\\<>])/gu, "\\$1")}>)`
680
653
  })
681
654
  }, MSTEAMS_FORMAT_CAPABILITIES);
682
655
  return protectedValues.reduce((text, { prefix, values }) => restoreTokens(text, prefix, values), restoreTokens(rendered, `${tokenPrefix}c`, codeRegions));
@@ -741,26 +714,6 @@ async function requestSharePointJson(params, request) {
741
714
  }
742
715
  });
743
716
  }
744
- async function uploadToSharePoint(params) {
745
- const uploadPath = `/OpenClawShared/${encodeURIComponent(params.filename)}`;
746
- const data = await requestSharePointJson(params, {
747
- url: `${GRAPH_ROOT}/sites/${params.siteId}/drive/root:${uploadPath}:/content?@microsoft.graph.conflictBehavior=rename`,
748
- timeoutMs: require_resolve_allowlist.resolveMSTeamsSharePointUploadTimeoutMs(params.buffer.length),
749
- init: () => ({
750
- method: "PUT",
751
- headers: { "Content-Type": params.contentType ?? "application/octet-stream" },
752
- body: new Blob([(0, openclaw_plugin_sdk_blob_runtime.bufferToBlobPart)(params.buffer)])
753
- }),
754
- error: "SharePoint upload failed",
755
- label: "msteams.graph-upload.uploadSharePointFile"
756
- });
757
- if (!data.id || !data.webUrl || !data.name) throw new Error("SharePoint upload response missing required fields");
758
- return {
759
- id: data.id,
760
- webUrl: data.webUrl,
761
- name: data.name
762
- };
763
- }
764
717
  async function getDriveItemProperties(params) {
765
718
  const data = await requestSharePointJson(params, {
766
719
  url: `${GRAPH_ROOT}/sites/${params.siteId}/drive/items/${params.itemId}?$select=eTag,webDavUrl,name`,
@@ -774,27 +727,44 @@ async function getDriveItemProperties(params) {
774
727
  name: data.name
775
728
  };
776
729
  }
777
- async function getChatMemberIds(params) {
778
- return ((await requestSharePointJson(params, {
779
- url: `${GRAPH_ROOT}/chats/${params.chatId}/members`,
780
- error: (res) => res.status === 403 ? "Get chat members failed; verify Graph chat-member permissions and tenant access policies" : "Get chat members failed",
781
- label: "msteams.graph-upload.getChatMembers"
782
- })).value ?? []).flatMap((member) => member.userId ? [member.userId] : []);
783
- }
784
730
  /**
785
- * Create a sharing link for a SharePoint drive item.
786
- * For organization scope (default), uses v1.0 API.
787
- * For per-user scope, uses beta API with recipients.
731
+ * Upload a file to SharePoint and create a sharing link.
732
+ *
733
+ * For group chats, this creates a per-user sharing link scoped to chat members.
734
+ * For channels, this creates an organization-wide sharing link.
788
735
  */
789
- async function createSharePointSharingLink(params) {
790
- const apiRoot = params.recipientObjectIds ? GRAPH_BETA : GRAPH_ROOT;
736
+ async function uploadAndShareSharePoint(params) {
737
+ const uploadPath = `/OpenClawShared/${encodeURIComponent(params.filename)}`;
738
+ const data = await requestSharePointJson(params, {
739
+ url: `${GRAPH_ROOT}/sites/${params.siteId}/drive/root:${uploadPath}:/content?@microsoft.graph.conflictBehavior=rename`,
740
+ timeoutMs: require_resolve_allowlist.resolveMSTeamsSharePointUploadTimeoutMs(params.buffer.length),
741
+ init: () => ({
742
+ method: "PUT",
743
+ headers: { "Content-Type": params.contentType ?? "application/octet-stream" },
744
+ body: new Blob([(0, openclaw_plugin_sdk_blob_runtime.bufferToBlobPart)(params.buffer)])
745
+ }),
746
+ error: "SharePoint upload failed",
747
+ label: "msteams.graph-upload.uploadSharePointFile"
748
+ });
749
+ if (!data.id) throw new Error("SharePoint upload response missing required fields");
750
+ const itemId = data.id;
751
+ let recipientObjectIds;
752
+ if (params.usePerUserSharing && params.chatId) {
753
+ recipientObjectIds = ((await requestSharePointJson(params, {
754
+ url: `${GRAPH_ROOT}/chats/${params.chatId}/members`,
755
+ error: (res) => res.status === 403 ? "Get chat members failed; verify Graph chat-member permissions and tenant access policies" : "Get chat members failed",
756
+ label: "msteams.graph-upload.getChatMembers"
757
+ })).value ?? []).flatMap((member) => member.userId ? [member.userId] : []);
758
+ if (recipientObjectIds.length === 0) throw new Error("MS Teams chat member lookup returned no recipients");
759
+ }
760
+ const apiRoot = recipientObjectIds ? GRAPH_BETA : GRAPH_ROOT;
791
761
  const body = {
792
762
  type: "view",
793
- scope: params.recipientObjectIds ? "users" : "organization"
763
+ scope: recipientObjectIds ? "users" : "organization"
794
764
  };
795
- if (params.recipientObjectIds) body.recipients = params.recipientObjectIds.map((id) => ({ objectId: id }));
796
- const data = await requestSharePointJson(params, {
797
- url: `${apiRoot}/sites/${params.siteId}/drive/items/${params.itemId}/createLink`,
765
+ if (recipientObjectIds) body.recipients = recipientObjectIds.map((id) => ({ objectId: id }));
766
+ await requestSharePointJson(params, {
767
+ url: `${apiRoot}/sites/${params.siteId}/drive/items/${itemId}/createLink`,
798
768
  init: () => ({
799
769
  method: "POST",
800
770
  headers: { "Content-Type": "application/json" },
@@ -803,39 +773,7 @@ async function createSharePointSharingLink(params) {
803
773
  error: "Create SharePoint sharing link failed",
804
774
  label: "msteams.graph-upload.createSharePointSharingLink"
805
775
  });
806
- if (!data.link?.webUrl) throw new Error("Create SharePoint sharing link response missing webUrl");
807
- return data.link.webUrl;
808
- }
809
- /**
810
- * Upload a file to SharePoint and create a sharing link.
811
- *
812
- * For group chats, this creates a per-user sharing link scoped to chat members.
813
- * For channels, this creates an organization-wide sharing link.
814
- */
815
- async function uploadAndShareSharePoint(params) {
816
- const uploaded = await uploadToSharePoint(params);
817
- let recipientObjectIds;
818
- if (params.usePerUserSharing && params.chatId) {
819
- recipientObjectIds = await getChatMemberIds({
820
- chatId: params.chatId,
821
- tokenProvider: params.tokenProvider,
822
- assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
823
- });
824
- if (recipientObjectIds.length === 0) throw new Error("MS Teams chat member lookup returned no recipients");
825
- }
826
- const shareUrl = await createSharePointSharingLink({
827
- siteId: params.siteId,
828
- itemId: uploaded.id,
829
- tokenProvider: params.tokenProvider,
830
- recipientObjectIds,
831
- assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
832
- });
833
- return {
834
- itemId: uploaded.id,
835
- webUrl: uploaded.webUrl,
836
- shareUrl,
837
- name: uploaded.name
838
- };
776
+ return { itemId };
839
777
  }
840
778
  // extensions/msteams/src/media-helpers.ts
841
779
  const MSTEAMS_MAX_MEDIA_BYTES = 104857600;
@@ -882,7 +820,7 @@ function extractMessageId(response) {
882
820
  const { id } = response;
883
821
  return typeof id === "string" && id ? id : null;
884
822
  }
885
- // extensions/msteams/src/mentions.ts
823
+ // extensions/msteams/src/message-activity.ts
886
824
  /**
887
825
  * Check whether an ID looks like a valid Teams user/bot identifier.
888
826
  * Accepts:
@@ -894,38 +832,6 @@ function extractMessageId(response) {
894
832
  */
895
833
  const TEAMS_BOT_ID_PATTERN = /^\d+:[a-z0-9._=-]+(?::[a-z0-9._=-]+)*$/i;
896
834
  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;
897
- function isValidTeamsId(id) {
898
- return TEAMS_BOT_ID_PATTERN.test(id) || AAD_OBJECT_ID_PATTERN.test(id);
899
- }
900
- /**
901
- * Convert @[Name](id) into matching <at> text and mention entities.
902
- * Only matches where the id looks like a real Teams user/bot ID are treated
903
- * as mentions. This avoids false positives from documentation or code samples
904
- * embedded in the message (e.g. `@[表示名](ユーザーID)` in backticks).
905
- */
906
- function parseMentions(text) {
907
- const mentionPattern = /@\[((?:\\[\s\S]|[^\]\\])+)\]\(([^)]+)\)/g;
908
- const entities = [];
909
- return {
910
- text: text.replace(mentionPattern, (match, name, id) => {
911
- const trimmedId = id.trim();
912
- if (!isValidTeamsId(trimmedId)) return match;
913
- const trimmedName = name.replace(/\\([\\[\]])/g, "$1").trim();
914
- const mentionTag = `<at>${trimmedName}</at>`;
915
- entities.push({
916
- type: "mention",
917
- text: mentionTag,
918
- mentioned: {
919
- id: trimmedId,
920
- name: trimmedName
921
- }
922
- });
923
- return mentionTag;
924
- }),
925
- entities
926
- };
927
- }
928
- // extensions/msteams/src/message-activity.ts
929
835
  const AI_GENERATED_ENTITY = {
930
836
  type: "https://schema.org/Message",
931
837
  "@type": "Message",
@@ -933,13 +839,36 @@ const AI_GENERATED_ENTITY = {
933
839
  "@id": "",
934
840
  additionalType: ["AIGeneratedContent"]
935
841
  };
842
+ /**
843
+ * Convert @[Name](id) into matching <at> text and mention entities.
844
+ * Only matches where the id looks like a real Teams user/bot ID are treated
845
+ * as mentions. This avoids false positives from documentation or code samples
846
+ * embedded in the message (e.g. `@[表示名](ユーザーID)` in backticks).
847
+ */
936
848
  /** Build final transport text and its matching entities after Markdown rendering. */
937
849
  function buildMSTeamsMessageActivity(text) {
938
- const parsed = parseMentions(text ?? "");
850
+ const sourceText = text ?? "";
851
+ const mentionPattern = /@\[((?:\\[\s\S]|[^\]\\])+)\]\(([^)]+)\)/g;
852
+ const entities = [];
853
+ const formattedText = sourceText.replace(mentionPattern, (match, name, id) => {
854
+ const trimmedId = id.trim();
855
+ if (!(TEAMS_BOT_ID_PATTERN.test(trimmedId) || AAD_OBJECT_ID_PATTERN.test(trimmedId))) return match;
856
+ const trimmedName = name.replace(/\\([\\[\]])/g, "$1").trim();
857
+ const mentionTag = `<at>${trimmedName}</at>`;
858
+ entities.push({
859
+ type: "mention",
860
+ text: mentionTag,
861
+ mentioned: {
862
+ id: trimmedId,
863
+ name: trimmedName
864
+ }
865
+ });
866
+ return mentionTag;
867
+ });
939
868
  return {
940
869
  type: "message",
941
- ...text === void 0 ? {} : { text: parsed.text },
942
- entities: [...parsed.entities, AI_GENERATED_ENTITY]
870
+ ...text === void 0 ? {} : { text: formattedText },
871
+ entities: [...entities, AI_GENERATED_ENTITY]
943
872
  };
944
873
  }
945
874
  function buildMSTeamsAdaptiveCardActivity(card) {
@@ -951,16 +880,6 @@ function buildMSTeamsAdaptiveCardActivity(card) {
951
880
  }]
952
881
  };
953
882
  }
954
- // extensions/msteams/src/revoked-context.ts
955
- async function withRevokedProxyFallback(params) {
956
- try {
957
- return await params.run();
958
- } catch (err) {
959
- if (!require_channel_setup.isRevokedProxyError(err)) throw err;
960
- params.onRevokedLog?.();
961
- return await params.onRevoked();
962
- }
963
- }
964
883
  // extensions/msteams/src/sent-message-cache.ts
965
884
  const TTL_MS = 864e5;
966
885
  const MAX_ENTRIES = 2e4;
@@ -970,7 +889,7 @@ const MSTEAMS_SENT_MESSAGES_KEY = Symbol.for("openclaw.msteamsSentMessages");
970
889
  const namedAccountCaches = /* @__PURE__ */ new Map();
971
890
  function createSentMessageCache(accountId) {
972
891
  const isDefault = accountId === "default";
973
- const accountDigest = isDefault ? void 0 : (0, node_crypto.createHash)("sha256").update(accountId).digest("hex");
892
+ const accountDigest = isDefault ? void 0 : (0, openclaw_plugin_sdk_node_crypto_runtime.sha256Hex)(accountId);
974
893
  return (0, openclaw_plugin_sdk_dedupe_runtime.createPersistentDedupeCache)({
975
894
  globalKey: isDefault ? MSTEAMS_SENT_MESSAGES_KEY : Symbol.for(`openclaw.msteamsSentMessages.account.v1.${accountDigest}`),
976
895
  ttlMs: TTL_MS,
@@ -1018,20 +937,15 @@ async function quoteMSTeamsActivity(activity, messageId) {
1018
937
  if (activity.type !== "message") return activity;
1019
938
  return MessageActivityInput.from(activity).prependQuote(messageId);
1020
939
  }
1021
- function resolveThreadedConversationId(conversationId, threadActivityId) {
1022
- const baseId = conversationId.split(";")[0] ?? conversationId;
1023
- return threadActivityId ? `${baseId};messageid=${threadActivityId}` : baseId;
1024
- }
1025
- function normalizeRequiredServiceUrl(ref) {
1026
- if (!ref.serviceUrl) throw new Error("Invalid stored reference: missing serviceUrl");
1027
- return require_bot_framework_service_url.normalizeBotFrameworkServiceUrl(ref.serviceUrl);
1028
- }
1029
940
  function buildSdkConversationReference(source, options) {
1030
- const bot = source.agent ?? source.bot ?? void 0;
941
+ const bot = source.agent ?? source.bot;
1031
942
  if (!bot?.id) throw new Error("Invalid stored reference: missing agent.id");
1032
- const conversationId = resolveThreadedConversationId(source.conversation.id, options?.threadActivityId);
943
+ const baseId = source.conversation.id.split(";")[0] ?? source.conversation.id;
944
+ const threadActivityId = options?.threadActivityId;
945
+ const conversationId = threadActivityId ? `${baseId};messageid=${threadActivityId}` : baseId;
1033
946
  const tenantId = source.tenantId ?? source.conversation.tenantId;
1034
- const serviceUrl = normalizeRequiredServiceUrl(source);
947
+ if (!source.serviceUrl) throw new Error("Invalid stored reference: missing serviceUrl");
948
+ const serviceUrl = require_bot_framework_service_url.normalizeBotFrameworkServiceUrl(source.serviceUrl);
1035
949
  if (options?.serviceUrlBoundary) require_resolve_allowlist.validateMSTeamsProactiveServiceUrlBoundary({
1036
950
  cloud: options.serviceUrlBoundary.cloud,
1037
951
  conversationId,
@@ -1059,74 +973,52 @@ function buildSdkConversationReference(source, options) {
1059
973
  ...source.aadObjectId ? { aadObjectId: source.aadObjectId } : {}
1060
974
  };
1061
975
  }
1062
- function sameServiceUrl(left, right) {
1063
- if (!left) return false;
1064
- try {
1065
- return require_bot_framework_service_url.normalizeBotFrameworkServiceUrl(left) === right;
1066
- } catch {
1067
- return false;
1068
- }
1069
- }
1070
- function stringifyReferenceFallbackActivity(activity) {
1071
- if (typeof activity === "string") return activity;
1072
- if (activity == null) return "";
1073
- if (typeof activity === "number" || typeof activity === "boolean" || typeof activity === "bigint") return String(activity);
1074
- return "";
1075
- }
1076
- async function getApiClientForReference(app, ref) {
1077
- const api = app.api;
1078
- if (sameServiceUrl(api.serviceUrl, ref.serviceUrl)) return api;
1079
- const appInternals = app;
1080
- const httpClient = appInternals.api?.http ?? appInternals.client;
1081
- if (!httpClient) return api;
1082
- const { Client } = await loadMSTeamsApiModule();
1083
- return new Client(ref.serviceUrl, httpClient);
1084
- }
1085
- function mergeReferenceIntoActivity(activity, ref) {
1086
- const source = (0, openclaw_plugin_sdk_string_coerce_runtime.asOptionalRecord)(activity) ?? {
1087
- type: "message",
1088
- text: stringifyReferenceFallbackActivity(activity)
1089
- };
1090
- const existingChannelData = (0, openclaw_plugin_sdk_string_coerce_runtime.asOptionalRecord)(source.channelData);
1091
- const existingTenant = (0, openclaw_plugin_sdk_string_coerce_runtime.asOptionalRecord)(existingChannelData?.tenant);
1092
- let channelData = existingChannelData ? { ...existingChannelData } : void 0;
1093
- if (ref.tenantId) {
1094
- channelData ??= {};
1095
- channelData.tenant = existingTenant ? {
1096
- ...existingTenant,
1097
- id: ref.tenantId
1098
- } : { id: ref.tenantId };
1099
- }
1100
- return {
1101
- ...source,
1102
- channelId: ref.channelId,
1103
- from: ref.bot,
1104
- recipient: ref.user,
1105
- conversation: ref.conversation,
1106
- ...channelData ? { channelData } : {},
1107
- locale: ref.locale,
1108
- ...ref.tenantId ? { tenantId: ref.tenantId } : {},
1109
- ...ref.aadObjectId ? { aadObjectId: ref.aadObjectId } : {}
1110
- };
976
+ function getApiClientForReference(app, ref) {
977
+ return app.api.serviceUrl === ref.serviceUrl ? app.api : app.api.fromServiceUrl({ serviceUrl: ref.serviceUrl });
1111
978
  }
1112
979
  async function sendMSTeamsActivityWithReference(app, source, activity, options) {
1113
980
  return require_resolve_allowlist.withMSTeamsConnectorHandoff(options ?? {}, async (handoff) => {
1114
981
  require_resolve_allowlist.assertMSTeamsSendHandoff(handoff);
1115
982
  const ref = buildSdkConversationReference(source, options);
1116
- const activities = (await getApiClientForReference(app, ref)).conversations.activities(ref.conversation.id);
1117
- const activityWithRef = mergeReferenceIntoActivity(options?.quoteActivityId ? await quoteMSTeamsActivity(activity, options.quoteActivityId) : activity, ref);
983
+ const activities = getApiClientForReference(app, ref).conversations.activities(ref.conversation.id);
984
+ const quotedActivity = options?.quoteActivityId ? await quoteMSTeamsActivity(activity, options.quoteActivityId) : activity;
985
+ const activitySource = typeof quotedActivity === "string" ? {
986
+ type: "message",
987
+ text: quotedActivity
988
+ } : quotedActivity;
989
+ const existingChannelData = (0, openclaw_plugin_sdk_string_coerce_runtime.asOptionalRecord)(activitySource.channelData);
990
+ const existingTenant = (0, openclaw_plugin_sdk_string_coerce_runtime.asOptionalRecord)(existingChannelData?.tenant);
991
+ let channelData = existingChannelData ? { ...existingChannelData } : void 0;
992
+ if (ref.tenantId) {
993
+ channelData ??= {};
994
+ channelData.tenant = existingTenant ? {
995
+ ...existingTenant,
996
+ id: ref.tenantId
997
+ } : { id: ref.tenantId };
998
+ }
999
+ const activityWithRef = {
1000
+ ...activitySource,
1001
+ channelId: ref.channelId,
1002
+ from: ref.bot,
1003
+ recipient: ref.user,
1004
+ conversation: ref.conversation,
1005
+ ...channelData ? { channelData } : {},
1006
+ locale: ref.locale,
1007
+ ...ref.tenantId ? { tenantId: ref.tenantId } : {},
1008
+ ...ref.aadObjectId ? { aadObjectId: ref.aadObjectId } : {}
1009
+ };
1118
1010
  const isTargeted = activityWithRef.recipient?.isTargeted === true;
1119
1011
  if (isTargeted && ref.conversation.conversationType === "personal") throw new Error("Targeted messages are not supported in 1:1 (personal) chats.");
1120
1012
  const activityId = typeof activityWithRef.id === "string" ? activityWithRef.id : void 0;
1121
1013
  require_resolve_allowlist.assertMSTeamsSendHandoff(handoff);
1122
1014
  if (activityId) {
1123
- const res = isTargeted && activities.updateTargeted ? await activities.updateTargeted(activityId, activityWithRef) : await activities.update(activityId, activityWithRef);
1015
+ const res = isTargeted ? await activities.updateTargeted(activityId, activityWithRef) : await activities.update(activityId, activityWithRef);
1124
1016
  return {
1125
1017
  ...activityWithRef,
1126
1018
  ...res && typeof res === "object" ? res : {}
1127
1019
  };
1128
1020
  }
1129
- const res = isTargeted && activities.createTargeted ? await activities.createTargeted(activityWithRef) : await activities.create(activityWithRef);
1021
+ const res = isTargeted ? await activities.createTargeted(activityWithRef) : await activities.create(activityWithRef);
1130
1022
  const conversationId = ref.conversation.id.split(";")[0] ?? ref.conversation.id;
1131
1023
  if (res.id && activityWithRef.type === "message" && ref.conversation.conversationType === "channel" && ref.conversation.id === conversationId) recordMSTeamsSentMessage(conversationId, res.id, {
1132
1024
  accountId: options?.accountId,
@@ -1140,11 +1032,11 @@ async function sendMSTeamsActivityWithReference(app, source, activity, options)
1140
1032
  }
1141
1033
  async function updateMSTeamsActivityWithReference(app, source, activityId, activity, options) {
1142
1034
  const ref = buildSdkConversationReference(source, options);
1143
- return (await getApiClientForReference(app, ref)).conversations.activities(ref.conversation.id).update(activityId, activity);
1035
+ return getApiClientForReference(app, ref).conversations.activities(ref.conversation.id).update(activityId, activity);
1144
1036
  }
1145
1037
  async function deleteMSTeamsActivityWithReference(app, source, activityId, options) {
1146
1038
  const ref = buildSdkConversationReference(source, options);
1147
- return (await getApiClientForReference(app, ref)).conversations.activities(ref.conversation.id).delete(activityId);
1039
+ return getApiClientForReference(app, ref).conversations.activities(ref.conversation.id).delete(activityId);
1148
1040
  }
1149
1041
  // extensions/msteams/src/messenger.ts
1150
1042
  const MSTEAMS_SEND_ATTEMPTS = 3;
@@ -1152,7 +1044,7 @@ const MSTEAMS_SEND_RETRY_MAX_DELAY_MS = 1e4;
1152
1044
  function buildConversationReference(ref) {
1153
1045
  const conversationId = ref.conversation?.id?.trim();
1154
1046
  if (!conversationId) throw new Error("Invalid stored reference: missing conversation.id");
1155
- const agent = ref.agent ?? ref.bot ?? void 0;
1047
+ const agent = ref.agent ?? ref.bot;
1156
1048
  if (agent == null || !agent.id) throw new Error("Invalid stored reference: missing agent.id");
1157
1049
  const user = ref.user;
1158
1050
  if (!user?.id) throw new Error("Invalid stored reference: missing user.id");
@@ -1183,105 +1075,109 @@ function renderReplyPayloadsToMessages(replies, options) {
1183
1075
  const chunkMode = options.chunkMode ?? "length";
1184
1076
  for (const payload of replies) {
1185
1077
  const reply = (0, openclaw_plugin_sdk_reply_payload.resolveSendableOutboundReplyParts)(payload, { text: formatMSTeamsMarkdown(payload.text ?? "", options.tableMode) });
1186
- if (!reply.hasContent) continue;
1078
+ if (!reply.hasContent && !payload.presentation) continue;
1187
1079
  if (reply.text) {
1188
1080
  const chunks = require_runtime.getMSTeamsRuntime().channel.text.chunkMarkdownTextWithMode(reply.text, chunkLimit, chunkMode);
1189
1081
  for (const chunk of chunks) {
1190
1082
  const text = chunk.trim();
1191
- if (text && !(0, openclaw_plugin_sdk_reply_chunking.isSilentReplyText)(text, openclaw_plugin_sdk_reply_chunking.SILENT_REPLY_TOKEN)) out.push({ text });
1083
+ if (text) out.push({ text });
1192
1084
  }
1193
1085
  }
1086
+ if (payload.presentation) out.push({ adaptiveCard: require_presentation.buildMSTeamsPresentationCard({ presentation: payload.presentation }) });
1194
1087
  out.push(...reply.mediaUrls.map((mediaUrl) => ({ mediaUrl })));
1195
1088
  }
1196
1089
  return out;
1197
1090
  }
1198
- async function buildActivity(msg, conversationRef, tokenProvider, sharePointSiteId, mediaMaxBytes, options) {
1199
- const activity = buildMSTeamsMessageActivity(msg.text);
1200
- activity.channelData = { feedbackLoopEnabled: options?.feedbackLoopEnabled ?? false };
1201
- if (msg.mediaUrl) {
1202
- let contentUrl = msg.mediaUrl;
1203
- let contentType = await getMimeType(msg.mediaUrl);
1204
- let fileName = await extractFilename(msg.mediaUrl);
1205
- if (isLocalPath(msg.mediaUrl)) {
1206
- const maxBytes = mediaMaxBytes ?? 104857600;
1207
- const media = await (0, openclaw_plugin_sdk_web_media.loadWebMedia)(msg.mediaUrl, maxBytes);
1208
- contentType = media.contentType ?? contentType;
1209
- fileName = media.fileName ?? fileName;
1210
- const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(conversationRef.conversation?.conversationType);
1211
- const isPersonal = conversationType === "personal";
1212
- const isImage = media.kind === "image";
1213
- if (requiresFileConsent({
1214
- conversationType,
1215
- contentType,
1216
- bufferSize: media.buffer.length
1217
- })) {
1218
- const conversationId = conversationRef.conversation?.id ?? "unknown";
1219
- require_resolve_allowlist.assertMSTeamsSendHandoff(options);
1220
- const { activity: consentActivity, uploadId } = prepareFileConsentActivity({
1221
- media: {
1222
- buffer: media.buffer,
1223
- filename: fileName,
1224
- contentType
1225
- },
1226
- conversationId,
1227
- description: msg.text || void 0
1228
- });
1229
- consentActivity["_pendingUploadId"] = uploadId;
1230
- return consentActivity;
1231
- }
1232
- if (!isPersonal && !isImage) {
1233
- const siteId = requireMSTeamsSharePointSiteId(sharePointSiteId);
1234
- if (!tokenProvider) throw new Error("MS Teams Graph token provider unavailable for SharePoint file send");
1235
- const chatId = conversationRef.conversation?.id;
1236
- const uploaded = await uploadAndShareSharePoint({
1237
- assertDirectAdapterHandoff: options?.assertDirectAdapterHandoff,
1238
- buffer: media.buffer,
1239
- filename: fileName,
1240
- contentType,
1241
- tokenProvider,
1242
- siteId,
1243
- chatId: chatId ?? void 0,
1244
- usePerUserSharing: conversationType === "groupchat"
1245
- });
1246
- activity.attachments = [buildTeamsFileInfoCard(await getDriveItemProperties({
1247
- assertDirectAdapterHandoff: options?.assertDirectAdapterHandoff,
1248
- siteId,
1249
- itemId: uploaded.itemId,
1250
- tokenProvider
1251
- }))];
1252
- return activity;
1253
- }
1254
- const base64 = media.buffer.toString("base64");
1255
- contentUrl = `data:${media.contentType};base64,${base64}`;
1256
- }
1257
- activity.attachments = [{
1258
- name: fileName,
1259
- contentType,
1260
- contentUrl
1261
- }];
1262
- }
1263
- return activity;
1264
- }
1265
1091
  async function sendMSTeamsMessages(params) {
1266
- const messages = params.messages.filter((m) => m.text && m.text.trim().length > 0 || m.mediaUrl);
1092
+ const messages = params.messages.filter((m) => m.text && m.text.trim().length > 0 || m.mediaUrl || m.adaptiveCard);
1267
1093
  if (messages.length === 0) return [];
1268
1094
  let providerDispatchStarted = false;
1269
1095
  const sendMessageInContext = async (sendFn, message, messageIndex) => {
1270
- let activity;
1271
- let pendingUploadId;
1096
+ async function buildActivity() {
1097
+ const msg = message;
1098
+ const { conversationRef, tokenProvider, sharePointSiteId, mediaMaxBytes } = params;
1099
+ const options = {
1100
+ feedbackLoopEnabled: params.feedbackLoopEnabled,
1101
+ assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
1102
+ };
1103
+ const activity = buildMSTeamsMessageActivity(msg.text);
1104
+ if (msg.adaptiveCard) Object.assign(activity, buildMSTeamsAdaptiveCardActivity(msg.adaptiveCard));
1105
+ activity.channelData = { feedbackLoopEnabled: options?.feedbackLoopEnabled ?? false };
1106
+ if (msg.mediaUrl) {
1107
+ let contentUrl = msg.mediaUrl;
1108
+ let contentType = await getMimeType(msg.mediaUrl);
1109
+ let fileName = await extractFilename(msg.mediaUrl);
1110
+ if (isLocalPath(msg.mediaUrl)) {
1111
+ const maxBytes = mediaMaxBytes ?? 104857600;
1112
+ const media = await (0, openclaw_plugin_sdk_web_media.loadWebMedia)(msg.mediaUrl, maxBytes);
1113
+ contentType = media.contentType ?? contentType;
1114
+ fileName = media.fileName ?? fileName;
1115
+ const conversationType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalLowercaseString)(conversationRef.conversation?.conversationType);
1116
+ const isPersonal = conversationType === "personal";
1117
+ const isImage = media.kind === "image";
1118
+ if (requiresFileConsent({
1119
+ conversationType,
1120
+ contentType,
1121
+ bufferSize: media.buffer.length
1122
+ })) {
1123
+ const conversationId = conversationRef.conversation?.id ?? "unknown";
1124
+ require_resolve_allowlist.assertMSTeamsSendHandoff(options);
1125
+ const { activity: consentActivity, uploadId } = prepareFileConsentActivity({
1126
+ media: {
1127
+ buffer: media.buffer,
1128
+ filename: fileName,
1129
+ contentType
1130
+ },
1131
+ conversationId,
1132
+ description: msg.text || void 0
1133
+ });
1134
+ return {
1135
+ activity: consentActivity,
1136
+ pendingUploadId: uploadId
1137
+ };
1138
+ }
1139
+ if (!isPersonal && !isImage) {
1140
+ const siteId = requireMSTeamsSharePointSiteId(sharePointSiteId);
1141
+ if (!tokenProvider) throw new Error("MS Teams Graph token provider unavailable for SharePoint file send");
1142
+ const chatId = conversationRef.conversation?.id;
1143
+ const uploaded = await uploadAndShareSharePoint({
1144
+ assertDirectAdapterHandoff: options?.assertDirectAdapterHandoff,
1145
+ buffer: media.buffer,
1146
+ filename: fileName,
1147
+ contentType,
1148
+ tokenProvider,
1149
+ siteId,
1150
+ chatId,
1151
+ usePerUserSharing: conversationType === "groupchat"
1152
+ });
1153
+ activity.attachments = [buildTeamsFileInfoCard(await getDriveItemProperties({
1154
+ assertDirectAdapterHandoff: options?.assertDirectAdapterHandoff,
1155
+ siteId,
1156
+ itemId: uploaded.itemId,
1157
+ tokenProvider
1158
+ }))];
1159
+ return { activity };
1160
+ }
1161
+ const base64 = media.buffer.toString("base64");
1162
+ contentUrl = `data:${media.contentType};base64,${base64}`;
1163
+ }
1164
+ activity.attachments = [{
1165
+ name: fileName,
1166
+ contentType,
1167
+ contentUrl
1168
+ }];
1169
+ }
1170
+ return { activity };
1171
+ }
1172
+ let prepared;
1272
1173
  let response;
1273
1174
  try {
1274
1175
  response = await (0, openclaw_plugin_sdk_retry_runtime.retryAsync)(async () => {
1275
1176
  require_resolve_allowlist.assertMSTeamsSendHandoff(params);
1276
- activity ??= await buildActivity(message, params.conversationRef, params.tokenProvider, params.sharePointSiteId, params.mediaMaxBytes, {
1277
- feedbackLoopEnabled: params.feedbackLoopEnabled,
1278
- assertDirectAdapterHandoff: params.assertDirectAdapterHandoff
1279
- });
1280
- pendingUploadId ??= typeof activity["_pendingUploadId"] === "string" ? activity["_pendingUploadId"] : void 0;
1281
- delete activity["_pendingUploadId"];
1177
+ prepared ??= await buildActivity();
1282
1178
  require_resolve_allowlist.assertMSTeamsSendHandoff(params);
1283
1179
  providerDispatchStarted = true;
1284
- return await sendFn(activity);
1180
+ return await sendFn(prepared.activity);
1285
1181
  }, {
1286
1182
  attempts: MSTEAMS_SEND_ATTEMPTS,
1287
1183
  minDelayMs: 0,
@@ -1310,54 +1206,33 @@ async function sendMSTeamsMessages(params) {
1310
1206
  }
1311
1207
  const messageId = extractMessageId(response) ?? "unknown";
1312
1208
  await params.onMessageSent?.(messageId, messageIndex);
1209
+ const pendingUploadId = prepared?.pendingUploadId;
1313
1210
  if (pendingUploadId && messageId !== "unknown") setPendingUploadActivityId(pendingUploadId, messageId);
1314
1211
  return messageId;
1315
1212
  };
1316
- const sendProactively = async (batch, startIndex, threadActivityId) => {
1213
+ let sendFn;
1214
+ const ctx = params.replyStyle === "thread" ? params.context : void 0;
1215
+ if (ctx) sendFn = (activity) => require_resolve_allowlist.withMSTeamsConnectorHandoff(params, () => ctx.sendActivity(activity));
1216
+ else {
1317
1217
  let baseRef;
1318
1218
  try {
1319
1219
  baseRef = buildConversationReference(params.conversationRef);
1320
1220
  } catch (error) {
1321
- if (providerDispatchStarted) throw error;
1322
1221
  throw new openclaw_plugin_sdk_error_runtime.PlatformMessageNotDispatchedError(error instanceof Error ? error.message : "Teams conversation preparation failed", { cause: error });
1323
1222
  }
1324
1223
  const isChannel = params.conversationRef.conversation?.conversationType === "channel";
1325
- const sendFn = (activity) => sendMSTeamsActivityWithReference(params.app, baseRef, activity, {
1224
+ const threadActivityId = params.replyStyle === "thread" && isChannel ? params.conversationRef.threadId ?? params.conversationRef.activityId : void 0;
1225
+ sendFn = (activity) => sendMSTeamsActivityWithReference(params.app, baseRef, activity, {
1326
1226
  accountId: params.accountId,
1327
1227
  assertDirectAdapterHandoff: params.assertDirectAdapterHandoff,
1328
1228
  onPlatformSendDispatch: params.onPlatformSendDispatch,
1329
- threadActivityId: isChannel ? threadActivityId : void 0,
1229
+ threadActivityId,
1330
1230
  serviceUrlBoundary: params.serviceUrlBoundary
1331
1231
  });
1332
- const messageIds = [];
1333
- for (const [idx, message] of batch.entries()) messageIds.push(await sendMessageInContext(sendFn, message, startIndex + idx));
1334
- return messageIds;
1335
- };
1336
- const resolvedThreadId = params.conversationRef.threadId ?? params.conversationRef.activityId;
1337
- if (params.replyStyle === "thread") {
1338
- const ctx = params.context;
1339
- if (!ctx) return await sendProactively(messages, 0, resolvedThreadId);
1340
- const sendFn = (activity) => require_resolve_allowlist.withMSTeamsConnectorHandoff(params, () => ctx.sendActivity(activity));
1341
- const messageIds = [];
1342
- for (const [idx, message] of messages.entries()) {
1343
- const result = await withRevokedProxyFallback({
1344
- run: async () => ({
1345
- ids: [await sendMessageInContext(sendFn, message, idx)],
1346
- fellBack: false
1347
- }),
1348
- onRevoked: async () => {
1349
- return {
1350
- ids: await sendProactively(messages.slice(idx), idx, resolvedThreadId),
1351
- fellBack: true
1352
- };
1353
- }
1354
- });
1355
- messageIds.push(...result.ids);
1356
- if (result.fellBack) return messageIds;
1357
- }
1358
- return messageIds;
1359
1232
  }
1360
- return await sendProactively(messages, 0);
1233
+ const messageIds = [];
1234
+ for (const [idx, message] of messages.entries()) messageIds.push(await sendMessageInContext(sendFn, message, idx));
1235
+ return messageIds;
1361
1236
  }
1362
1237
  // extensions/msteams/src/polls.ts
1363
1238
  const MSTEAMS_POLLS_NAMESPACE = "polls";
@@ -1431,57 +1306,51 @@ function extractMSTeamsPollVote(activity) {
1431
1306
  }
1432
1307
  function buildMSTeamsPollCard(params) {
1433
1308
  const pollId = node_crypto.default.randomUUID();
1434
- const maxSelections = typeof params.maxSelections === "number" && params.maxSelections > 1 ? Math.floor(params.maxSelections) : 1;
1309
+ const maxSelections = params.maxSelections !== void 0 && params.maxSelections > 1 ? Math.floor(params.maxSelections) : 1;
1435
1310
  const cappedMaxSelections = Math.min(Math.max(1, maxSelections), params.options.length);
1436
1311
  const choices = params.options.map((option, index) => ({
1437
1312
  title: option,
1438
1313
  value: String(index)
1439
1314
  }));
1440
1315
  const hint = cappedMaxSelections > 1 ? `Select up to ${cappedMaxSelections} options.` : "Select one option.";
1441
- const card = {
1442
- type: "AdaptiveCard",
1443
- version: "1.5",
1444
- body: [
1445
- {
1446
- type: "TextBlock",
1447
- text: params.question,
1448
- wrap: true,
1449
- weight: "Bolder",
1450
- size: "Medium"
1451
- },
1452
- {
1453
- type: "Input.ChoiceSet",
1454
- id: "choices",
1455
- isMultiSelect: cappedMaxSelections > 1,
1456
- style: "expanded",
1457
- choices
1458
- },
1459
- {
1460
- type: "TextBlock",
1461
- text: hint,
1462
- wrap: true,
1463
- isSubtle: true,
1464
- spacing: "Small"
1465
- }
1466
- ],
1467
- actions: [{
1468
- type: "Action.Execute",
1469
- title: "Vote",
1470
- verb: "openclaw.poll.vote",
1471
- data: {
1472
- openclawPollId: pollId,
1473
- pollId
1474
- }
1475
- }]
1476
- };
1477
- const fallbackLines = [`Poll: ${params.question}`, ...params.options.map((option, index) => `${index + 1}. ${option}`)];
1478
1316
  return {
1479
1317
  pollId,
1480
- question: params.question,
1481
- options: params.options,
1482
- maxSelections: cappedMaxSelections,
1483
- card,
1484
- fallbackText: fallbackLines.join("\n")
1318
+ card: {
1319
+ type: "AdaptiveCard",
1320
+ version: "1.5",
1321
+ body: [
1322
+ {
1323
+ type: "TextBlock",
1324
+ text: params.question,
1325
+ wrap: true,
1326
+ weight: "Bolder",
1327
+ size: "Medium"
1328
+ },
1329
+ {
1330
+ type: "Input.ChoiceSet",
1331
+ id: "choices",
1332
+ isMultiSelect: cappedMaxSelections > 1,
1333
+ style: "expanded",
1334
+ choices
1335
+ },
1336
+ {
1337
+ type: "TextBlock",
1338
+ text: hint,
1339
+ wrap: true,
1340
+ isSubtle: true,
1341
+ spacing: "Small"
1342
+ }
1343
+ ],
1344
+ actions: [{
1345
+ type: "Action.Execute",
1346
+ title: "Vote",
1347
+ verb: "openclaw.poll.vote",
1348
+ data: {
1349
+ openclawPollId: pollId,
1350
+ pollId
1351
+ }
1352
+ }]
1353
+ }
1485
1354
  };
1486
1355
  }
1487
1356
  function isPollExpired(poll) {
@@ -1516,59 +1385,12 @@ function createMSTeamsPollStoreState(params) {
1516
1385
  namespace: require_delegated_state.resolveMSTeamsAccountStateNamespace(MSTEAMS_POLL_VOTE_BUCKETS_NAMESPACE, params?.accountId),
1517
1386
  maxEntries: MSTEAMS_MAX_POLL_VOTE_BUCKET_ROWS
1518
1387
  });
1519
- const readPollVotes = async (pollId) => {
1520
- const votes = {};
1521
- for (const row of await voteBucketStore.entries()) if (row.value.pollId === pollId) Object.assign(votes, row.value.votes);
1522
- return votes;
1523
- };
1524
1388
  const deletePollVotes = async (pollId, cachedRows) => {
1525
1389
  for (const row of cachedRows?.values() ?? await voteBucketStore.entries()) if (row.value.pollId === pollId) {
1526
1390
  await voteBucketStore.delete(row.key);
1527
1391
  cachedRows?.delete(row.key);
1528
1392
  }
1529
1393
  };
1530
- const registerPollVotes = async (pollId, votes, updatedAt) => {
1531
- const buckets = /* @__PURE__ */ new Map();
1532
- for (const [voterId, selections] of Object.entries(votes)) {
1533
- const bucket = selectMSTeamsPollVoteBucket(pollId, voterId);
1534
- const bucketVotes = buckets.get(bucket) ?? {};
1535
- bucketVotes[voterId] = selections;
1536
- buckets.set(bucket, bucketVotes);
1537
- }
1538
- for (const [bucket, bucketVotes] of buckets) {
1539
- const key = buildMSTeamsPollVoteBucketKey(pollId, bucket);
1540
- const existing = await voteBucketStore.lookup(key);
1541
- await voteBucketStore.register(key, require_delegated_state.toPluginJsonValue({
1542
- pollId,
1543
- bucket,
1544
- votes: {
1545
- ...bucketVotes,
1546
- ...existing?.votes
1547
- },
1548
- updatedAt
1549
- }));
1550
- }
1551
- };
1552
- const registerPollVote = async (pollId, voterId, selections, updatedAt) => {
1553
- const bucket = selectMSTeamsPollVoteBucket(pollId, voterId);
1554
- const key = buildMSTeamsPollVoteBucketKey(pollId, bucket);
1555
- const existing = await voteBucketStore.lookup(key);
1556
- await voteBucketStore.register(key, require_delegated_state.toPluginJsonValue({
1557
- pollId,
1558
- bucket,
1559
- votes: {
1560
- ...existing?.votes,
1561
- [voterId]: selections
1562
- },
1563
- updatedAt
1564
- }));
1565
- };
1566
- const reconstructPoll = async (metadata) => {
1567
- return {
1568
- ...metadata,
1569
- votes: await readPollVotes(metadata.id)
1570
- };
1571
- };
1572
1394
  const prunePollStoreToLimit = async () => {
1573
1395
  let voteRows;
1574
1396
  const deletePrunedPollVotes = async (pollId) => {
@@ -1595,10 +1417,8 @@ function createMSTeamsPollStoreState(params) {
1595
1417
  };
1596
1418
  const createPoll = async (poll) => {
1597
1419
  await require_delegated_state.withMSTeamsSqliteMutationLock(namespace, async () => {
1598
- const { votes, ...metadata } = poll;
1599
- await pollStore.register(buildMSTeamsPollStateKey(poll.id), require_delegated_state.toPluginJsonValue(metadata));
1420
+ await pollStore.register(buildMSTeamsPollStateKey(poll.id), require_delegated_state.toPluginJsonValue(poll));
1600
1421
  await deletePollVotes(poll.id);
1601
- await registerPollVotes(poll.id, votes, poll.updatedAt ?? poll.createdAt);
1602
1422
  await prunePollStoreToLimit();
1603
1423
  });
1604
1424
  };
@@ -1606,7 +1426,7 @@ function createMSTeamsPollStoreState(params) {
1606
1426
  const poll = await pollStore.lookup(buildMSTeamsPollStateKey(pollId));
1607
1427
  if (!poll) return null;
1608
1428
  if (isPollExpired(poll)) return null;
1609
- return await reconstructPoll(poll);
1429
+ return poll;
1610
1430
  };
1611
1431
  const recordVote = async (vote) => {
1612
1432
  return await require_delegated_state.withMSTeamsSqliteMutationLock(namespace, async () => {
@@ -1618,13 +1438,26 @@ function createMSTeamsPollStoreState(params) {
1618
1438
  await deletePollVotes(vote.pollId);
1619
1439
  return null;
1620
1440
  }
1621
- const normalized = normalizeMSTeamsPollSelections(await reconstructPoll(poll), vote.selections);
1441
+ const normalized = normalizeMSTeamsPollSelections(poll, vote.selections);
1622
1442
  const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1623
1443
  poll.updatedAt = updatedAt;
1624
- await pollStore.register(pollKey, require_delegated_state.toPluginJsonValue(poll));
1625
- await registerPollVote(vote.pollId, vote.voterId, normalized, updatedAt);
1444
+ await pollStore.register(pollKey, poll);
1445
+ const pollId = vote.pollId;
1446
+ const voterId = vote.voterId;
1447
+ const bucket = selectMSTeamsPollVoteBucket(pollId, voterId);
1448
+ const key = buildMSTeamsPollVoteBucketKey(pollId, bucket);
1449
+ const existing = await voteBucketStore.lookup(key);
1450
+ await voteBucketStore.register(key, {
1451
+ pollId,
1452
+ bucket,
1453
+ votes: {
1454
+ ...existing?.votes,
1455
+ [voterId]: normalized
1456
+ },
1457
+ updatedAt
1458
+ });
1626
1459
  await prunePollStoreToLimit();
1627
- return await reconstructPoll(poll);
1460
+ return true;
1628
1461
  });
1629
1462
  };
1630
1463
  return {
@@ -1633,44 +1466,11 @@ function createMSTeamsPollStoreState(params) {
1633
1466
  recordVote
1634
1467
  };
1635
1468
  }
1636
- // extensions/msteams/src/conversation-store-helpers.ts
1637
- function mergeStoredConversationReference(existing, incoming, nowIso) {
1638
- return {
1639
- ...existing?.timezone && !incoming.timezone ? { timezone: existing.timezone } : {},
1640
- ...existing?.tenantId && !incoming.tenantId ? { tenantId: existing.tenantId } : {},
1641
- ...existing?.aadObjectId && !incoming.aadObjectId ? { aadObjectId: existing.aadObjectId } : {},
1642
- ...incoming,
1643
- lastSeenAt: nowIso
1644
- };
1645
- }
1646
- function findPreferredDmConversationByUserId(entries, id) {
1647
- const target = id.trim();
1648
- if (!target) return null;
1649
- let preferred = null;
1650
- let preferredIsPersonal = false;
1651
- let preferredTimestamp = 0;
1652
- for (const entry of entries) {
1653
- if (entry.reference.user?.aadObjectId !== target && entry.reference.user?.id !== target) continue;
1654
- const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeLowercaseStringOrEmpty)(entry.reference.conversation?.conversationType);
1655
- if (convType === "channel" || convType === "groupchat") continue;
1656
- const isPersonal = convType === "personal";
1657
- const timestamp = (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(entry.reference.lastSeenAt) ?? 0;
1658
- if (!preferred || isPersonal && !preferredIsPersonal || isPersonal === preferredIsPersonal && timestamp > preferredTimestamp) {
1659
- preferred = entry;
1660
- preferredIsPersonal = isPersonal;
1661
- preferredTimestamp = timestamp;
1662
- }
1663
- }
1664
- return preferred;
1665
- }
1666
1469
  // extensions/msteams/src/conversation-store-state.ts
1667
1470
  const MSTEAMS_CONVERSATIONS_NAMESPACE = "conversations";
1668
1471
  const MSTEAMS_MAX_CONVERSATIONS = 1e3;
1669
1472
  const MSTEAMS_SQLITE_MAX_CONVERSATION_ROWS = 2e3;
1670
1473
  const MSTEAMS_CONVERSATION_TTL_MS = 31536e6;
1671
- function buildMSTeamsConversationStateKey(conversationId) {
1672
- return node_crypto.default.createHash("sha256").update(conversationId).digest("hex");
1673
- }
1674
1474
  function getStoredConversationId(reference) {
1675
1475
  const rawId = reference.conversation?.id;
1676
1476
  return rawId ? require_resolve_allowlist.normalizeMSTeamsConversationId(rawId) : null;
@@ -1687,7 +1487,7 @@ function createMSTeamsConversationStoreState(params) {
1687
1487
  };
1688
1488
  const lookupStored = async (conversationId) => {
1689
1489
  const normalizedId = require_resolve_allowlist.normalizeMSTeamsConversationId(conversationId);
1690
- const value = await conversationStore.lookup(buildMSTeamsConversationStateKey(normalizedId));
1490
+ const value = await conversationStore.lookup((0, openclaw_plugin_sdk_node_crypto_runtime.sha256Hex)(normalizedId));
1691
1491
  return value && !isExpired(value) ? value : null;
1692
1492
  };
1693
1493
  const list = async () => {
@@ -1705,7 +1505,7 @@ function createMSTeamsConversationStoreState(params) {
1705
1505
  };
1706
1506
  const register = async (conversationId, reference) => {
1707
1507
  const normalizedId = require_resolve_allowlist.normalizeMSTeamsConversationId(conversationId);
1708
- await conversationStore.register(buildMSTeamsConversationStateKey(normalizedId), require_delegated_state.toPluginJsonValue({
1508
+ await conversationStore.register((0, openclaw_plugin_sdk_node_crypto_runtime.sha256Hex)(normalizedId), require_delegated_state.toPluginJsonValue({
1709
1509
  ...reference,
1710
1510
  conversation: {
1711
1511
  ...reference.conversation,
@@ -1721,29 +1521,54 @@ function createMSTeamsConversationStoreState(params) {
1721
1521
  rows.push(row);
1722
1522
  }
1723
1523
  if (rows.length <= MSTEAMS_MAX_CONVERSATIONS) return;
1724
- const sorted = rows.toSorted((a, b) => {
1524
+ rows.sort((a, b) => {
1725
1525
  const aTs = (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(a.value.lastSeenAt) ?? 0;
1726
1526
  const bTs = (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(b.value.lastSeenAt) ?? 0;
1727
1527
  const aId = getStoredConversationId(a.value) ?? a.key;
1728
1528
  const bId = getStoredConversationId(b.value) ?? b.key;
1729
1529
  return aTs - bTs || aId.localeCompare(bId);
1730
1530
  });
1731
- for (const row of sorted.slice(0, rows.length - MSTEAMS_MAX_CONVERSATIONS)) await conversationStore.delete(row.key);
1531
+ for (const row of rows.slice(0, rows.length - MSTEAMS_MAX_CONVERSATIONS)) await conversationStore.delete(row.key);
1732
1532
  };
1733
1533
  const findPreferredDmByUserId = async (id) => {
1734
- return findPreferredDmConversationByUserId(await list(), id);
1534
+ const entries = await list();
1535
+ const target = id.trim();
1536
+ if (!target) return null;
1537
+ let preferred = null;
1538
+ let preferredIsPersonal = false;
1539
+ let preferredTimestamp = 0;
1540
+ for (const entry of entries) {
1541
+ if (entry.reference.user?.aadObjectId !== target && entry.reference.user?.id !== target) continue;
1542
+ const convType = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeLowercaseStringOrEmpty)(entry.reference.conversation?.conversationType);
1543
+ if (convType === "channel" || convType === "groupchat") continue;
1544
+ const isPersonal = convType === "personal";
1545
+ const timestamp = (0, openclaw_plugin_sdk_number_runtime.parseDateStringTimestampMs)(entry.reference.lastSeenAt) ?? 0;
1546
+ if (!preferred || isPersonal && !preferredIsPersonal || isPersonal === preferredIsPersonal && timestamp > preferredTimestamp) {
1547
+ preferred = entry;
1548
+ preferredIsPersonal = isPersonal;
1549
+ preferredTimestamp = timestamp;
1550
+ }
1551
+ }
1552
+ return preferred;
1735
1553
  };
1736
1554
  const upsert = async (conversationId, reference) => {
1737
1555
  const normalizedId = require_resolve_allowlist.normalizeMSTeamsConversationId(conversationId);
1738
1556
  await require_delegated_state.withMSTeamsSqliteMutationLock(namespace, async () => {
1739
- const existing = await lookupStored(normalizedId);
1740
- await register(normalizedId, mergeStoredConversationReference(existing ?? void 0, reference, (/* @__PURE__ */ new Date()).toISOString()));
1557
+ const previous = await lookupStored(normalizedId) ?? void 0;
1558
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
1559
+ await register(normalizedId, {
1560
+ ...previous?.timezone && !reference.timezone ? { timezone: previous.timezone } : {},
1561
+ ...previous?.tenantId && !reference.tenantId ? { tenantId: previous.tenantId } : {},
1562
+ ...previous?.aadObjectId && !reference.aadObjectId ? { aadObjectId: previous.aadObjectId } : {},
1563
+ ...reference,
1564
+ lastSeenAt: nowIso
1565
+ });
1741
1566
  });
1742
1567
  };
1743
1568
  const remove = async (conversationId) => {
1744
1569
  const normalizedId = require_resolve_allowlist.normalizeMSTeamsConversationId(conversationId);
1745
1570
  return await require_delegated_state.withMSTeamsSqliteMutationLock(namespace, async () => {
1746
- return await conversationStore.delete(buildMSTeamsConversationStateKey(normalizedId));
1571
+ return await conversationStore.delete((0, openclaw_plugin_sdk_node_crypto_runtime.sha256Hex)(normalizedId));
1747
1572
  });
1748
1573
  };
1749
1574
  return {
@@ -1754,12 +1579,6 @@ function createMSTeamsConversationStoreState(params) {
1754
1579
  findPreferredDmByUserId
1755
1580
  };
1756
1581
  }
1757
- Object.defineProperty(exports, "FILE_CONSENT_THRESHOLD_BYTES", {
1758
- enumerable: true,
1759
- get: function() {
1760
- return FILE_CONSENT_THRESHOLD_BYTES;
1761
- }
1762
- });
1763
1582
  Object.defineProperty(exports, "MSTEAMS_MAX_MEDIA_BYTES", {
1764
1583
  enumerable: true,
1765
1584
  get: function() {
@@ -1958,9 +1777,3 @@ Object.defineProperty(exports, "wasMSTeamsMessageSentWithPersistence", {
1958
1777
  return wasMSTeamsMessageSentWithPersistence;
1959
1778
  }
1960
1779
  });
1961
- Object.defineProperty(exports, "withRevokedProxyFallback", {
1962
- enumerable: true,
1963
- get: function() {
1964
- return withRevokedProxyFallback;
1965
- }
1966
- });