@openclaw/msteams 2026.9.2 → 2026.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/api.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_channel = require("./channel-CWwr9KeE.cjs");
3
- const require_channel_setup = require("./channel.setup-v4zhsO2d.cjs");
2
+ const require_channel = require("./channel-Cdm6-HsU.cjs");
3
+ const require_channel_setup = require("./channel.setup-ChwkwdLk.cjs");
4
4
  exports.createMSTeamsSetupWizardBase = require_channel_setup.createMSTeamsSetupWizardBase;
5
5
  exports.msteamsPlugin = require_channel.msteamsPlugin;
6
6
  exports.msteamsSetupAdapter = require_channel_setup.msteamsSetupAdapter;
@@ -1,15 +1,7 @@
1
- const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
2
1
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
3
- let node_crypto = require("node:crypto");
4
- node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
5
- let openclaw_plugin_sdk_collection_runtime = require("openclaw/plugin-sdk/collection-runtime");
2
+ let openclaw_plugin_sdk_approval_runtime = require("openclaw/plugin-sdk/approval-runtime");
6
3
  //#region extensions/msteams/src/approval-card-actions.ts
7
- const approvalCardBindings = /* @__PURE__ */ new Map();
8
- const approvalCardResolvingTokens = /* @__PURE__ */ new Set();
9
- const MSTEAMS_APPROVAL_CARD_BINDING_MAX_ENTRIES = 1024;
10
- function createMSTeamsApprovalToken() {
11
- return node_crypto.default.randomBytes(18).toString("base64url");
12
- }
4
+ const msTeamsApprovalControls = (0, openclaw_plugin_sdk_approval_runtime.createNativeApprovalControlRegistry)({ releaseClaimOnLookupExpiry: true });
13
5
  function readMSTeamsApprovalActionToken(value) {
14
6
  if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value)) return null;
15
7
  const action = (0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(value.action) ? value.action : void 0;
@@ -17,43 +9,6 @@ function readMSTeamsApprovalActionToken(value) {
17
9
  if (submitted.openclawAction !== "approval") return null;
18
10
  return (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(submitted.token) ?? null;
19
11
  }
20
- function registerMSTeamsApprovalCardBinding(binding) {
21
- if (binding.expiresAtMs <= Date.now()) return false;
22
- approvalCardBindings.delete(binding.token);
23
- approvalCardBindings.set(binding.token, binding);
24
- (0, openclaw_plugin_sdk_collection_runtime.pruneMapToMaxSize)(approvalCardBindings, MSTEAMS_APPROVAL_CARD_BINDING_MAX_ENTRIES);
25
- return true;
26
- }
27
- function getMSTeamsApprovalCardBinding(token) {
28
- const binding = approvalCardBindings.get(token);
29
- if (!binding) return null;
30
- if (binding.expiresAtMs <= Date.now()) {
31
- approvalCardBindings.delete(token);
32
- approvalCardResolvingTokens.delete(token);
33
- return null;
34
- }
35
- return binding;
36
- }
37
- function claimMSTeamsApprovalCardBinding(token) {
38
- const binding = getMSTeamsApprovalCardBinding(token);
39
- if (!binding) return { kind: "missing" };
40
- if (approvalCardResolvingTokens.has(token)) return { kind: "in-flight" };
41
- approvalCardResolvingTokens.add(token);
42
- return {
43
- kind: "claimed",
44
- binding
45
- };
46
- }
47
- function completeMSTeamsApprovalCardBinding(token) {
48
- approvalCardResolvingTokens.delete(token);
49
- approvalCardBindings.delete(token);
50
- }
51
- function releaseMSTeamsApprovalCardBinding(token) {
52
- approvalCardResolvingTokens.delete(token);
53
- }
54
- function unregisterMSTeamsApprovalCardBindings(tokens) {
55
- for (const token of tokens) completeMSTeamsApprovalCardBinding(token);
56
- }
57
12
  //#endregion
58
13
  //#region extensions/msteams/src/approval-card.ts
59
14
  function buildCardHeading(title, subtitle) {
@@ -146,15 +101,12 @@ function buildAdaptiveCard(body, actions) {
146
101
  ...actions?.length ? { actions } : {}
147
102
  };
148
103
  }
149
- function formatApprovalDecision(decision) {
150
- return decision === "allow-once" ? "Allowed once" : decision === "allow-always" ? "Allowed always" : "Denied";
151
- }
152
104
  function buildMSTeamsPendingApprovalCard(params) {
153
105
  const { view, nowMs } = params;
154
106
  const kindLabel = view.approvalKind === "plugin" ? "Plugin" : view.approvalKind === "system-agent" ? "OpenClaw Change" : "Exec";
155
107
  const actionTokens = [];
156
108
  const actions = view.actions.map(({ decision, label }) => {
157
- const token = createMSTeamsApprovalToken();
109
+ const token = msTeamsApprovalControls.createToken();
158
110
  actionTokens.push({
159
111
  token,
160
112
  decision
@@ -187,7 +139,7 @@ function buildMSTeamsResolvedApprovalCard(view) {
187
139
  const kindLabel = view.approvalKind === "plugin" ? "Plugin" : view.approvalKind === "system-agent" ? "OpenClaw Change" : "Exec";
188
140
  const resolvedBy = (0, openclaw_plugin_sdk_string_coerce_runtime.normalizeOptionalString)(view.resolvedBy);
189
141
  return buildAdaptiveCard([
190
- ...buildCardHeading(`${kindLabel} Approval: ${view.approvalKind === "system-agent" && view.terminalStatus === "cancelled" ? "Cancelled" : view.approvalKind === "system-agent" && view.applicationStatus === "applied" ? "Applied" : view.approvalKind === "system-agent" && view.applicationStatus === "not-applied" ? "Not applied" : formatApprovalDecision(view.decision)}`, resolvedBy ? `Resolved by ${resolvedBy}` : "Resolved"),
142
+ ...buildCardHeading(`${kindLabel} Approval: ${(0, openclaw_plugin_sdk_approval_runtime.formatChannelApprovalResolvedLabel)(view)}`, resolvedBy ? `Resolved by ${resolvedBy}` : "Resolved"),
191
143
  ...buildApprovalSubject(view),
192
144
  buildApprovalMetadata(view.approvalId, view.metadata)
193
145
  ]);
@@ -203,7 +155,7 @@ function buildMSTeamsCanonicalApprovalTerminalCard(result) {
203
155
  const { approval } = result;
204
156
  const { presentation } = approval;
205
157
  const kindLabel = presentation.kind === "exec" ? "Exec" : presentation.kind === "plugin" ? "Plugin" : "System Agent";
206
- const outcome = approval.status === "allowed" ? formatApprovalDecision(approval.decision) : approval.status === "denied" ? "Denied" : approval.status === "expired" ? "Expired" : "Cancelled";
158
+ const outcome = approval.status === "allowed" ? (0, openclaw_plugin_sdk_approval_runtime.formatApprovalDecisionLabel)(approval.decision) : approval.status === "denied" ? "Denied" : approval.status === "expired" ? "Expired" : "Cancelled";
207
159
  const subject = presentation.kind === "exec" ? [{
208
160
  type: "TextBlock",
209
161
  text: "Command",
@@ -269,22 +221,10 @@ Object.defineProperty(exports, "buildMSTeamsResolvedApprovalCard", {
269
221
  return buildMSTeamsResolvedApprovalCard;
270
222
  }
271
223
  });
272
- Object.defineProperty(exports, "claimMSTeamsApprovalCardBinding", {
273
- enumerable: true,
274
- get: function() {
275
- return claimMSTeamsApprovalCardBinding;
276
- }
277
- });
278
- Object.defineProperty(exports, "completeMSTeamsApprovalCardBinding", {
224
+ Object.defineProperty(exports, "msTeamsApprovalControls", {
279
225
  enumerable: true,
280
226
  get: function() {
281
- return completeMSTeamsApprovalCardBinding;
282
- }
283
- });
284
- Object.defineProperty(exports, "getMSTeamsApprovalCardBinding", {
285
- enumerable: true,
286
- get: function() {
287
- return getMSTeamsApprovalCardBinding;
227
+ return msTeamsApprovalControls;
288
228
  }
289
229
  });
290
230
  Object.defineProperty(exports, "readMSTeamsApprovalActionToken", {
@@ -293,21 +233,3 @@ Object.defineProperty(exports, "readMSTeamsApprovalActionToken", {
293
233
  return readMSTeamsApprovalActionToken;
294
234
  }
295
235
  });
296
- Object.defineProperty(exports, "registerMSTeamsApprovalCardBinding", {
297
- enumerable: true,
298
- get: function() {
299
- return registerMSTeamsApprovalCardBinding;
300
- }
301
- });
302
- Object.defineProperty(exports, "releaseMSTeamsApprovalCardBinding", {
303
- enumerable: true,
304
- get: function() {
305
- return releaseMSTeamsApprovalCardBinding;
306
- }
307
- });
308
- Object.defineProperty(exports, "unregisterMSTeamsApprovalCardBindings", {
309
- enumerable: true,
310
- get: function() {
311
- return unregisterMSTeamsApprovalCardBindings;
312
- }
313
- });
@@ -1,7 +1,7 @@
1
1
  const require_resolve_allowlist = require("./resolve-allowlist-CoKmLXYe.cjs");
2
- const require_channel = require("./channel-CWwr9KeE.cjs");
3
- const require_approval_card = require("./approval-card-ClrTjCsL.cjs");
4
- const require_send = require("./send-DNTNV8Oa.cjs");
2
+ const require_channel = require("./channel-Cdm6-HsU.cjs");
3
+ const require_approval_card = require("./approval-card-CPNH_q3P.cjs");
4
+ const require_send = require("./send-Bw4W8RO7.cjs");
5
5
  let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
6
6
  let openclaw_plugin_sdk_approval_native_runtime = require("openclaw/plugin-sdk/approval-native-runtime");
7
7
  let openclaw_plugin_sdk_approval_handler_runtime = require("openclaw/plugin-sdk/approval-handler-runtime");
@@ -79,7 +79,7 @@ const msTeamsApprovalNativeRuntime = (0, openclaw_plugin_sdk_approval_handler_ru
79
79
  interactions: {
80
80
  bindPending: ({ entry, request, approvalKind, view, pendingPayload }) => {
81
81
  const tokens = [];
82
- for (const actionToken of entry.actionTokens) if (require_approval_card.registerMSTeamsApprovalCardBinding({
82
+ for (const actionToken of entry.actionTokens) if (require_approval_card.msTeamsApprovalControls.register({
83
83
  token: actionToken.token,
84
84
  accountId: entry.accountId,
85
85
  approvalId: request.id,
@@ -92,8 +92,8 @@ const msTeamsApprovalNativeRuntime = (0, openclaw_plugin_sdk_approval_handler_ru
92
92
  })) tokens.push(actionToken.token);
93
93
  return tokens.length > 0 ? tokens : null;
94
94
  },
95
- unbindPending: ({ binding }) => require_approval_card.unregisterMSTeamsApprovalCardBindings(binding),
96
- cancelDelivered: ({ entry }) => require_approval_card.unregisterMSTeamsApprovalCardBindings(entry.actionTokens.map(({ token }) => token))
95
+ unbindPending: ({ binding }) => require_approval_card.msTeamsApprovalControls.unregister(binding),
96
+ cancelDelivered: ({ entry }) => require_approval_card.msTeamsApprovalControls.unregister(entry.actionTokens.map(({ token }) => token))
97
97
  },
98
98
  observe: { onDeliveryError: ({ cfg, error, plannedTarget, request, approvalKind, pendingPayload }) => {
99
99
  log.error(`msteams approvals: failed to deliver request ${request.id}: ${String(error)}`);
@@ -2,7 +2,7 @@ require("./runtime-api.cjs");
2
2
  const require_resolve_allowlist = require("./resolve-allowlist-CoKmLXYe.cjs");
3
3
  const require_directory_contract_api = require("./directory-contract-api.cjs");
4
4
  const require_thread_session = require("./thread-session-p8FNgohv.cjs");
5
- const require_channel_setup = require("./channel.setup-v4zhsO2d.cjs");
5
+ const require_channel_setup = require("./channel.setup-ChwkwdLk.cjs");
6
6
  let openclaw_plugin_sdk_approval_handler_adapter_runtime = require("openclaw/plugin-sdk/approval-handler-adapter-runtime");
7
7
  let openclaw_plugin_sdk_channel_core = require("openclaw/plugin-sdk/channel-core");
8
8
  let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
@@ -330,7 +330,7 @@ const msTeamsLazyApprovalNativeRuntime = (0, openclaw_plugin_sdk_approval_handle
330
330
  request
331
331
  }),
332
332
  load: async () => {
333
- const { msTeamsApprovalNativeRuntime } = await Promise.resolve().then(() => require("./approval-handler.runtime-zm3viYLZ.cjs"));
333
+ const { msTeamsApprovalNativeRuntime } = await Promise.resolve().then(() => require("./approval-handler.runtime-CoTQG4FS.cjs"));
334
334
  return msTeamsApprovalNativeRuntime;
335
335
  }
336
336
  });
@@ -781,7 +781,7 @@ const collectMSTeamsSecurityFindings = openclaw_plugin_sdk_channel_policy.create
781
781
  severity: "critical",
782
782
  title: "MS Teams security warning"
783
783
  });
784
- const loadMSTeamsChannelRuntime = (0, openclaw_plugin_sdk_lazy_runtime.createLazyRuntimeNamedExport)(() => Promise.resolve().then(() => require("./channel.runtime-CoRvy4hG.cjs")), "msTeamsChannelRuntime");
784
+ const loadMSTeamsChannelRuntime = (0, openclaw_plugin_sdk_lazy_runtime.createLazyRuntimeNamedExport)(() => Promise.resolve().then(() => require("./channel.runtime-__5ZQlfG.cjs")), "msTeamsChannelRuntime");
785
785
  function jsonActionResult(data) {
786
786
  return {
787
787
  content: [{
@@ -1524,7 +1524,7 @@ const msteamsPlugin = (0, openclaw_plugin_sdk_channel_core.createChatChannelPlug
1524
1524
  })
1525
1525
  }),
1526
1526
  gateway: { startAccount: async (ctx) => {
1527
- const { monitorMSTeamsProvider } = await Promise.resolve().then(() => require("./src-B01LZcb5.cjs"));
1527
+ const { monitorMSTeamsProvider } = await Promise.resolve().then(() => require("./src-B--Qc6lj.cjs"));
1528
1528
  const port = ctx.cfg.channels?.msteams?.webhook?.port ?? 3978;
1529
1529
  const statusSink = (0, openclaw_plugin_sdk_channel_outbound.createAccountStatusSink)({
1530
1530
  accountId: ctx.accountId,
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_config_schema = require("./config-schema-MyN2lamA.cjs");
2
+ const require_config_schema = require("./config-schema-D22Lr1Qk.cjs");
3
3
  exports.MSTeamsChannelConfigSchema = require_config_schema.MSTeamsChannelConfigSchema;
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_channel = require("./channel-CWwr9KeE.cjs");
2
+ const require_channel = require("./channel-Cdm6-HsU.cjs");
3
3
  exports.msteamsPlugin = require_channel.msteamsPlugin;
@@ -1,10 +1,10 @@
1
1
  require("./runtime-api.cjs");
2
2
  const require_resolve_allowlist = require("./resolve-allowlist-CoKmLXYe.cjs");
3
- const require_channel = require("./channel-CWwr9KeE.cjs");
4
- const require_channel_setup = require("./channel.setup-v4zhsO2d.cjs");
3
+ const require_channel = require("./channel-Cdm6-HsU.cjs");
4
+ const require_channel_setup = require("./channel.setup-ChwkwdLk.cjs");
5
5
  const require_polls = require("./polls-BBdZPx_R.cjs");
6
- const require_send = require("./send-DNTNV8Oa.cjs");
7
- const require_reaction_types = require("./reaction-types-7LiO1RI1.cjs");
6
+ const require_send = require("./send-Bw4W8RO7.cjs");
7
+ const require_reaction_types = require("./reaction-types-zP91Ncwk.cjs");
8
8
  let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
9
9
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
10
10
  let openclaw_plugin_sdk_reply_payload = require("openclaw/plugin-sdk/reply-payload");
@@ -1,6 +1,6 @@
1
1
  const require_resolve_allowlist = require("./resolve-allowlist-CoKmLXYe.cjs");
2
2
  require("./secret-input-C65t6kjM.cjs");
3
- const require_config_schema = require("./config-schema-MyN2lamA.cjs");
3
+ const require_config_schema = require("./config-schema-D22Lr1Qk.cjs");
4
4
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
5
5
  let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
6
6
  let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
@@ -138,19 +138,10 @@ const MSTeamsConfigSchema = zod.z.object({
138
138
  connectionName: zod.z.string().optional()
139
139
  }).strict().optional()
140
140
  }).strict().superRefine((value, ctx) => {
141
- (0, openclaw_plugin_sdk_channel_config_schema.requireOpenAllowFrom)({
142
- policy: value.dmPolicy,
143
- allowFrom: value.allowFrom,
144
- ctx,
145
- path: ["allowFrom"],
146
- message: "channels.msteams.dmPolicy=\"open\" requires channels.msteams.allowFrom to include \"*\""
147
- });
148
- (0, openclaw_plugin_sdk_channel_config_schema.requireAllowlistAllowFrom)({
149
- policy: value.dmPolicy,
150
- allowFrom: value.allowFrom,
151
- ctx,
152
- path: ["allowFrom"],
153
- message: "channels.msteams.dmPolicy=\"allowlist\" requires channels.msteams.allowFrom to contain at least one sender ID"
141
+ (0, openclaw_plugin_sdk_channel_config_schema.refineChannelDmPolicy)({
142
+ channelId: "msteams",
143
+ value,
144
+ ctx
154
145
  });
155
146
  if (value.sso?.enabled === true && !value.sso.connectionName?.trim()) ctx.addIssue({
156
147
  code: zod.z.ZodIssueCode.custom,
@@ -2,7 +2,7 @@ const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
2
2
  const require_runtime = require("./runtime-gZTAuFux.cjs");
3
3
  require("./runtime-api.cjs");
4
4
  const require_resolve_allowlist = require("./resolve-allowlist-CoKmLXYe.cjs");
5
- const require_channel_setup = require("./channel.setup-v4zhsO2d.cjs");
5
+ const require_channel_setup = require("./channel.setup-ChwkwdLk.cjs");
6
6
  const require_polls = require("./polls-BBdZPx_R.cjs");
7
7
  let openclaw_plugin_sdk_lazy_runtime = require("openclaw/plugin-sdk/lazy-runtime");
8
8
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
@@ -18,6 +18,7 @@ node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
18
18
  let node_path = require("node:path");
19
19
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
20
20
  let openclaw_plugin_sdk_runtime_env = require("openclaw/plugin-sdk/runtime-env");
21
+ let openclaw_plugin_sdk_blob_runtime = require("openclaw/plugin-sdk/blob-runtime");
21
22
  let openclaw_plugin_sdk_global_singleton = require("openclaw/plugin-sdk/global-singleton");
22
23
  let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
23
24
  let openclaw_plugin_sdk_reply_chunking = require("openclaw/plugin-sdk/reply-chunking");
@@ -153,7 +154,7 @@ async function uploadToConsentUrl(params) {
153
154
  "Content-Type": params.contentType ?? "application/octet-stream",
154
155
  "Content-Range": `bytes 0-${params.buffer.length - 1}/${params.buffer.length}`
155
156
  },
156
- body: new Uint8Array(params.buffer)
157
+ body: new Blob([(0, openclaw_plugin_sdk_blob_runtime.bufferToBlobPart)(params.buffer)])
157
158
  }, params.timeoutMs ?? require_resolve_allowlist.resolveMSTeamsSharePointUploadTimeoutMs(params.buffer.length), fetchFn);
158
159
  await res.body?.cancel().catch(() => void 0);
159
160
  if (!res.ok) throw new Error(`File upload to consent URL failed: ${res.status} ${res.statusText}`);
@@ -244,9 +245,13 @@ async function withPendingUploadLock(options, run) {
244
245
  async function readUploadRows(id, metaStore, chunkStore) {
245
246
  const meta = await metaStore.lookup(buildMetaKey(id));
246
247
  if (!meta) return;
248
+ if (!Number.isSafeInteger(meta.chunkCount) || meta.chunkCount < 1 || meta.chunkCount > MAX_CHUNKS_PER_UPLOAD) return;
249
+ const records = await chunkStore.lookupMany?.(Array.from({ length: meta.chunkCount }, (_, index) => buildChunkKey(id, index)));
247
250
  const chunks = [];
248
251
  for (let index = 0; index < meta.chunkCount; index += 1) {
249
- const chunk = await chunkStore.lookup(buildChunkKey(id, index));
252
+ const result = records?.[index];
253
+ if (result && !result.ok) throw result.error;
254
+ const chunk = records ? result?.value : await chunkStore.lookup(buildChunkKey(id, index));
250
255
  if (!chunk || chunk.id !== id || chunk.index !== index) return;
251
256
  chunks.push(Buffer.from(chunk.dataBase64, "base64"));
252
257
  }
@@ -551,25 +556,25 @@ function createTokenPrefix(text, label) {
551
556
  return prefix;
552
557
  }
553
558
  function restoreTokens(text, prefix, values) {
559
+ if (!text.includes(prefix)) return text;
554
560
  const escapedPrefix = prefix.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
555
561
  return text.replace(new RegExp(`${escapedPrefix}(\\d+)${TOKEN_END}`, "gu"), (_token, index) => values[Number(index)] ?? "");
556
562
  }
557
563
  function rewriteMarkdownIR(ir, edits) {
558
- const ordered = [...edits].toSorted((a, b) => a.start - b.start);
564
+ const ordered = edits.toSorted((a, b) => a.start - b.start);
565
+ const cumulativeDeltas = [];
566
+ const exactEdits = /* @__PURE__ */ new Map();
559
567
  let text = "";
560
568
  let cursor = 0;
569
+ let delta = 0;
561
570
  for (const edit of ordered) {
562
571
  text += ir.text.slice(cursor, edit.start) + edit.text;
563
572
  cursor = edit.end;
564
- }
565
- text += ir.text.slice(cursor);
566
- const cumulativeDeltas = [];
567
- let delta = 0;
568
- for (const edit of ordered) {
569
573
  delta += edit.text.length - (edit.end - edit.start);
570
574
  cumulativeDeltas.push(delta);
575
+ exactEdits.set(`${edit.start}:${edit.end}`, edit);
571
576
  }
572
- const exactEdits = new Map(ordered.map((edit) => [`${edit.start}:${edit.end}`, edit]));
577
+ text += ir.text.slice(cursor);
573
578
  const mapOffset = (offset) => {
574
579
  let low = 0;
575
580
  let high = ordered.length;
@@ -666,43 +671,22 @@ function scanDelimitedMarkdown(text, start, nestedOpener) {
666
671
  }
667
672
  return fallbackNext === void 0 ? void 0 : { next: fallbackNext };
668
673
  }
669
- function protectMarkdownImages(text, tokenPrefix, images) {
670
- let protectedText = "";
671
- let cursor = 0;
672
- let searchFrom = 0;
673
- while (searchFrom < text.length) {
674
- const start = text.indexOf("![", searchFrom);
675
- if (start < 0) break;
676
- const scan = scanDelimitedMarkdown(text, start, "![");
677
- if (!scan) break;
678
- if ("next" in scan) {
679
- searchFrom = scan.next;
680
- continue;
681
- }
682
- protectedText += text.slice(cursor, start);
683
- const index = images.push(text.slice(start, scan.end)) - 1;
684
- protectedText += `${tokenPrefix}i${index}${TOKEN_END}`;
685
- cursor = scan.end;
686
- searchFrom = scan.end;
687
- }
688
- return protectedText + text.slice(cursor);
689
- }
690
- function protectMSTeamsMentions(text, tokenPrefix, mentions) {
674
+ function protectDelimitedMarkdown(text, opener, tokenPrefix, values) {
691
675
  let protectedText = "";
692
676
  let cursor = 0;
693
677
  let searchFrom = 0;
694
678
  while (searchFrom < text.length) {
695
- const start = text.indexOf("@[", searchFrom);
679
+ const start = text.indexOf(opener, searchFrom);
696
680
  if (start < 0) break;
697
- const scan = scanDelimitedMarkdown(text, start, "@[");
681
+ const scan = scanDelimitedMarkdown(text, start, opener);
698
682
  if (!scan) break;
699
683
  if ("next" in scan) {
700
684
  searchFrom = scan.next;
701
685
  continue;
702
686
  }
703
687
  protectedText += text.slice(cursor, start);
704
- const index = mentions.push(text.slice(start, scan.end)) - 1;
705
- protectedText += `${tokenPrefix}m${index}${TOKEN_END}`;
688
+ const index = values.push(text.slice(start, scan.end)) - 1;
689
+ protectedText += `${tokenPrefix}${index}${TOKEN_END}`;
706
690
  cursor = scan.end;
707
691
  searchFrom = scan.end;
708
692
  }
@@ -880,6 +864,7 @@ function protectMSTeamsCode(ir, tokenPrefix, code, protectedValues) {
880
864
  };
881
865
  }
882
866
  function formatMSTeamsMarkdown(markdown, tableMode) {
867
+ if (markdown === "") return markdown;
883
868
  const rawTables = [];
884
869
  const escapedMarkdown = [];
885
870
  const codeRegions = [];
@@ -887,10 +872,10 @@ function formatMSTeamsMarkdown(markdown, tableMode) {
887
872
  const mentions = [];
888
873
  const entities = [];
889
874
  const tokenPrefix = createTokenPrefix(markdown, "msteamsformat");
890
- const mentionsProtected = protectMSTeamsMentions(protectMarkdownImages(markdown.replace(MARKDOWN_ENTITY_RE, (entity) => {
875
+ const mentionsProtected = protectDelimitedMarkdown(protectDelimitedMarkdown(markdown.replace(MARKDOWN_ENTITY_RE, (entity) => {
891
876
  const index = entities.push(entity) - 1;
892
877
  return `${tokenPrefix}h${index}${TOKEN_END}`;
893
- }), tokenPrefix, images), tokenPrefix, mentions);
878
+ }), "![", `${tokenPrefix}i`, images), "@[", `${tokenPrefix}m`, mentions);
894
879
  const tableInput = (0, openclaw_plugin_sdk_text_chunking.convertMarkdownTables)(mentionsProtected, tableMode);
895
880
  const protectedMarkdown = (tableMode === "off" ? protectRawTablesOutsideFences(tableInput, tokenPrefix, rawTables) : tableInput).replace(ESCAPED_MARKDOWN_RE, (escaped) => {
896
881
  const index = escapedMarkdown.push(escaped) - 1;
@@ -1009,7 +994,7 @@ async function uploadToSharePoint(params) {
1009
994
  Authorization: `Bearer ${token}`,
1010
995
  "Content-Type": params.contentType ?? "application/octet-stream"
1011
996
  },
1012
- body: new Uint8Array(params.buffer),
997
+ body: new Blob([(0, openclaw_plugin_sdk_blob_runtime.bufferToBlobPart)(params.buffer)]),
1013
998
  signal
1014
999
  });
1015
1000
  if (!res.ok) throw await require_resolve_allowlist.createMSTeamsHttpError(res, "SharePoint upload failed");
@@ -1225,15 +1210,6 @@ function extractMessageId(response) {
1225
1210
  return id;
1226
1211
  }
1227
1212
  //#endregion
1228
- //#region extensions/msteams/src/ai-entity.ts
1229
- /** AI-generated content entity added to every outbound AI message. */
1230
- const AI_GENERATED_ENTITY = {
1231
- type: "https://schema.org/Message",
1232
- "@type": "Message",
1233
- "@id": "",
1234
- additionalType: ["AIGeneratedContent"]
1235
- };
1236
- //#endregion
1237
1213
  //#region extensions/msteams/src/mentions.ts
1238
1214
  /**
1239
1215
  * Check whether an ID looks like a valid Teams user/bot identifier.
@@ -1282,6 +1258,24 @@ function parseMentions(text) {
1282
1258
  };
1283
1259
  }
1284
1260
  //#endregion
1261
+ //#region extensions/msteams/src/message-activity.ts
1262
+ const AI_GENERATED_ENTITY = {
1263
+ type: "https://schema.org/Message",
1264
+ "@type": "Message",
1265
+ "@context": "https://schema.org",
1266
+ "@id": "",
1267
+ additionalType: ["AIGeneratedContent"]
1268
+ };
1269
+ /** Build final transport text and its matching entities after Markdown rendering. */
1270
+ function buildMSTeamsMessageActivity(text) {
1271
+ const parsed = parseMentions(text ?? "");
1272
+ return {
1273
+ type: "message",
1274
+ ...text === void 0 ? {} : { text: parsed.text },
1275
+ entities: [...parsed.entities, AI_GENERATED_ENTITY]
1276
+ };
1277
+ }
1278
+ //#endregion
1285
1279
  //#region extensions/msteams/src/revoked-context.ts
1286
1280
  async function withRevokedProxyFallback(params) {
1287
1281
  try {
@@ -1551,13 +1545,8 @@ function renderReplyPayloadsToMessages(replies, options) {
1551
1545
  return out;
1552
1546
  }
1553
1547
  async function buildActivity(msg, conversationRef, tokenProvider, sharePointSiteId, mediaMaxBytes, options) {
1554
- const activity = { type: "message" };
1548
+ const activity = buildMSTeamsMessageActivity(msg.text);
1555
1549
  activity.channelData = { feedbackLoopEnabled: options?.feedbackLoopEnabled ?? false };
1556
- if (msg.text) {
1557
- const { text: formattedText, entities } = parseMentions(msg.text);
1558
- activity.text = formattedText;
1559
- activity.entities = [...entities.length > 0 ? entities : [], AI_GENERATED_ENTITY];
1560
- } else activity.entities = [AI_GENERATED_ENTITY];
1561
1550
  if (msg.mediaUrl) {
1562
1551
  let contentUrl = msg.mediaUrl;
1563
1552
  let contentType = await getMimeType(msg.mediaUrl);
@@ -1728,6 +1717,12 @@ Object.defineProperty(exports, "buildFileInfoCard", {
1728
1717
  return buildFileInfoCard;
1729
1718
  }
1730
1719
  });
1720
+ Object.defineProperty(exports, "buildMSTeamsMessageActivity", {
1721
+ enumerable: true,
1722
+ get: function() {
1723
+ return buildMSTeamsMessageActivity;
1724
+ }
1725
+ });
1731
1726
  Object.defineProperty(exports, "buildTeamsFileInfoCard", {
1732
1727
  enumerable: true,
1733
1728
  get: function() {
@@ -69,12 +69,8 @@ async function fetchThreadReplies(token, groupId, channelId, messageId, limit =
69
69
  ...deadline ? { deadline } : {}
70
70
  })).value ?? [];
71
71
  }
72
- /**
73
- * Format thread messages into a context string for the agent.
74
- * Skips the current message (by id) and blank messages.
75
- */
76
- function formatThreadContext(messages, currentMessageId) {
77
- const lines = [];
72
+ function buildThreadContext(messages, currentMessageId) {
73
+ const context = [];
78
74
  for (const msg of messages) {
79
75
  if (msg.id && msg.id === currentMessageId) continue;
80
76
  const sender = msg.from?.user?.displayName ?? msg.from?.application?.displayName ?? "unknown";
@@ -82,9 +78,13 @@ function formatThreadContext(messages, currentMessageId) {
82
78
  const rawContent = msg.body?.content ?? "";
83
79
  const content = contentType === "html" ? stripHtmlFromTeamsMessage(rawContent) : rawContent.trim();
84
80
  if (!content) continue;
85
- lines.push(`${sender}: ${content}`);
81
+ context.push({
82
+ message_id: msg.id,
83
+ sender,
84
+ body: content
85
+ });
86
86
  }
87
- return lines.join("\n");
87
+ return context;
88
88
  }
89
89
  //#endregion
90
90
  //#region extensions/msteams/src/reaction-types.ts
@@ -107,6 +107,12 @@ function resolveMSTeamsReactionEmoji(raw) {
107
107
  return getMSTeamsReactionEmoji(normalized) ?? normalized;
108
108
  }
109
109
  //#endregion
110
+ Object.defineProperty(exports, "buildThreadContext", {
111
+ enumerable: true,
112
+ get: function() {
113
+ return buildThreadContext;
114
+ }
115
+ });
110
116
  Object.defineProperty(exports, "fetchChannelMessage", {
111
117
  enumerable: true,
112
118
  get: function() {
@@ -125,12 +131,6 @@ Object.defineProperty(exports, "fetchThreadReplies", {
125
131
  return fetchThreadReplies;
126
132
  }
127
133
  });
128
- Object.defineProperty(exports, "formatThreadContext", {
129
- enumerable: true,
130
- get: function() {
131
- return formatThreadContext;
132
- }
133
- });
134
134
  Object.defineProperty(exports, "getMSTeamsReactionEmoji", {
135
135
  enumerable: true,
136
136
  get: function() {
@@ -1,10 +1,10 @@
1
1
  const require_runtime = require("./runtime-gZTAuFux.cjs");
2
2
  require("./runtime-api.cjs");
3
3
  const require_resolve_allowlist = require("./resolve-allowlist-CoKmLXYe.cjs");
4
- const require_channel = require("./channel-CWwr9KeE.cjs");
5
- const require_channel_setup = require("./channel.setup-v4zhsO2d.cjs");
4
+ const require_channel = require("./channel-Cdm6-HsU.cjs");
5
+ const require_channel_setup = require("./channel.setup-ChwkwdLk.cjs");
6
6
  const require_polls = require("./polls-BBdZPx_R.cjs");
7
- const require_messenger = require("./messenger-DM07DC3W.cjs");
7
+ const require_messenger = require("./messenger-DbWgzI01.cjs");
8
8
  let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
9
9
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
10
10
  let openclaw_plugin_sdk_markdown_table_runtime = require("openclaw/plugin-sdk/markdown-table-runtime");
@@ -349,8 +349,7 @@ async function sendMessageMSTeams(params) {
349
349
  const messageId = await sendProactiveActivityRaw({
350
350
  ctx,
351
351
  activity: {
352
- type: "message",
353
- text: messageText || void 0,
352
+ ...require_messenger.buildMSTeamsMessageActivity(messageText || void 0),
354
353
  attachments: [fileCardAttachment]
355
354
  }
356
355
  });
@@ -523,9 +522,11 @@ async function editMessageMSTeams(params) {
523
522
  return updateMSTeamsMessageActivity({
524
523
  ...params,
525
524
  activity: {
526
- type: "message",
527
- id: params.activityId,
528
- text: params.text
525
+ ...require_messenger.buildMSTeamsMessageActivity(require_messenger.formatMSTeamsMarkdown(params.text, (0, openclaw_plugin_sdk_markdown_table_runtime.resolveMarkdownTableMode)({
526
+ cfg: params.cfg,
527
+ channel: "msteams"
528
+ }))),
529
+ id: params.activityId
529
530
  }
530
531
  });
531
532
  }
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_channel_setup = require("./channel.setup-v4zhsO2d.cjs");
2
+ const require_channel_setup = require("./channel.setup-ChwkwdLk.cjs");
3
3
  exports.msteamsSetupPlugin = require_channel_setup.msteamsSetupPlugin;
@@ -2,14 +2,14 @@ const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
2
2
  const require_runtime = require("./runtime-gZTAuFux.cjs");
3
3
  require("./runtime-api.cjs");
4
4
  const require_resolve_allowlist = require("./resolve-allowlist-CoKmLXYe.cjs");
5
- const require_channel = require("./channel-CWwr9KeE.cjs");
5
+ const require_channel = require("./channel-Cdm6-HsU.cjs");
6
6
  const require_thread_session = require("./thread-session-p8FNgohv.cjs");
7
- const require_channel_setup = require("./channel.setup-v4zhsO2d.cjs");
7
+ const require_channel_setup = require("./channel.setup-ChwkwdLk.cjs");
8
8
  const require_polls = require("./polls-BBdZPx_R.cjs");
9
9
  const require_sso_token_store = require("./sso-token-store-DTeYxubp.cjs");
10
- const require_approval_card = require("./approval-card-ClrTjCsL.cjs");
11
- const require_messenger = require("./messenger-DM07DC3W.cjs");
12
- const require_reaction_types = require("./reaction-types-7LiO1RI1.cjs");
10
+ const require_approval_card = require("./approval-card-CPNH_q3P.cjs");
11
+ const require_messenger = require("./messenger-DbWgzI01.cjs");
12
+ const require_reaction_types = require("./reaction-types-zP91Ncwk.cjs");
13
13
  let openclaw_plugin_sdk_channel_outbound = require("openclaw/plugin-sdk/channel-outbound");
14
14
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
15
15
  let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
@@ -22,7 +22,6 @@ let openclaw_plugin_sdk_text_utility_runtime = require("openclaw/plugin-sdk/text
22
22
  let openclaw_plugin_sdk_routing = require("openclaw/plugin-sdk/routing");
23
23
  let node_crypto = require("node:crypto");
24
24
  node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
25
- let openclaw_plugin_sdk_collection_runtime = require("openclaw/plugin-sdk/collection-runtime");
26
25
  let openclaw_plugin_sdk_error_runtime = require("openclaw/plugin-sdk/error-runtime");
27
26
  let openclaw_plugin_sdk_channel_inbound = require("openclaw/plugin-sdk/channel-inbound");
28
27
  let openclaw_plugin_sdk_approval_gateway_runtime = require("openclaw/plugin-sdk/approval-gateway-runtime");
@@ -35,6 +34,7 @@ let openclaw_plugin_sdk_extension_shared = require("openclaw/plugin-sdk/extensio
35
34
  let openclaw_plugin_sdk_plugin_runtime = require("openclaw/plugin-sdk/plugin-runtime");
36
35
  let openclaw_plugin_sdk_dedupe_runtime = require("openclaw/plugin-sdk/dedupe-runtime");
37
36
  let openclaw_plugin_sdk_plugin_state_runtime = require("openclaw/plugin-sdk/plugin-state-runtime");
37
+ let openclaw_plugin_sdk_collection_runtime = require("openclaw/plugin-sdk/collection-runtime");
38
38
  let openclaw_plugin_sdk_gateway_runtime = require("openclaw/plugin-sdk/gateway-runtime");
39
39
  let openclaw_plugin_sdk_channel_pairing = require("openclaw/plugin-sdk/channel-pairing");
40
40
  let openclaw_plugin_sdk_runtime_group_policy = require("openclaw/plugin-sdk/runtime-group-policy");
@@ -188,7 +188,7 @@ async function maybeHandleMSTeamsApprovalCardSubmit(params) {
188
188
  ignored("missing card token");
189
189
  return true;
190
190
  }
191
- const binding = require_approval_card.getMSTeamsApprovalCardBinding(token);
191
+ const binding = require_approval_card.msTeamsApprovalControls.get(token);
192
192
  if (!binding) {
193
193
  ignored("unknown or expired card token");
194
194
  return true;
@@ -220,15 +220,8 @@ async function maybeHandleMSTeamsApprovalCardSubmit(params) {
220
220
  ignored(`unauthorized actor ${senderId || "unknown"}`);
221
221
  return true;
222
222
  }
223
- const claim = require_approval_card.claimMSTeamsApprovalCardBinding(token);
224
- if (claim.kind !== "claimed") {
225
- ignored(claim.kind === "in-flight" ? "card token resolve already in flight" : "card token already consumed");
226
- return true;
227
- }
228
- const consumed = claim.binding;
229
- let result;
230
- try {
231
- result = await (0, openclaw_plugin_sdk_approval_gateway_runtime.resolveApprovalOverGateway)({
223
+ const outcome = await require_approval_card.msTeamsApprovalControls.settle(token, async (consumed) => {
224
+ const result = await (0, openclaw_plugin_sdk_approval_gateway_runtime.resolveApprovalOverGateway)({
232
225
  cfg: deps.cfg,
233
226
  approvalId: consumed.approvalId,
234
227
  approvalKind: consumed.approvalKind,
@@ -245,16 +238,13 @@ async function maybeHandleMSTeamsApprovalCardSubmit(params) {
245
238
  content: require_approval_card.buildMSTeamsCanonicalApprovalTerminalCard(result)
246
239
  }]
247
240
  });
248
- } catch (error) {
249
- if ((0, openclaw_plugin_sdk_error_runtime.isApprovalNotFoundError)(error)) {
250
- require_approval_card.completeMSTeamsApprovalCardBinding(token);
251
- ignored(`approval expired or no longer exists id=${consumed.approvalId}`);
252
- return true;
253
- }
254
- require_approval_card.releaseMSTeamsApprovalCardBinding(token);
255
- throw error;
241
+ return result;
242
+ });
243
+ if (outcome.kind !== "settled") {
244
+ ignored(outcome.kind === "missing" ? "card token already consumed" : outcome.kind === "in-flight" ? "card token resolve already in flight" : `approval expired or no longer exists id=${outcome.binding.approvalId}`);
245
+ return true;
256
246
  }
257
- require_approval_card.completeMSTeamsApprovalCardBinding(token);
247
+ const { binding: consumed, result } = outcome;
258
248
  deps.log.info("msteams approval resolved", {
259
249
  approvalId: consumed.approvalId,
260
250
  approvalKind: consumed.approvalKind,
@@ -1771,23 +1761,25 @@ function createTeamsReplyStreamController(params) {
1771
1761
  let pendingFinalPayload;
1772
1762
  let emittedText = "";
1773
1763
  let acknowledgedText = "";
1764
+ let acknowledgedLogicalText = "";
1774
1765
  let acknowledgedStreamId;
1775
1766
  let replacementFinalPending = false;
1776
1767
  let replacementEmitFailed = false;
1777
1768
  let replacementSettlementPending = false;
1778
1769
  let replacementTextAwaitingAcknowledgement;
1779
1770
  let deferredReplacementEntries = [];
1780
- let finalMetadataQueued = false;
1771
+ let queuedFinalActivity;
1781
1772
  let failedSegmentFallbackPrepared = false;
1782
1773
  const streamEvents = stream?.events;
1783
1774
  let streamChunkSubscription;
1784
1775
  if (typeof streamEvents?.on === "function" && typeof streamEvents.off === "function") streamChunkSubscription = streamEvents.on("chunk", (activity) => {
1785
1776
  const replacementAcknowledgementPending = replacementTextAwaitingAcknowledgement !== void 0;
1786
- const replacementAcknowledgement = typeof activity.text === "string" && replacementAcknowledgementPending && activity.text === replacementTextAwaitingAcknowledgement;
1787
- if (activity.type !== "typing" || activity.channelData?.streamType !== "streaming" || !activity.id || !activity.text || acknowledgedStreamId !== void 0 && activity.id !== acknowledgedStreamId || (replacementAcknowledgementPending ? !replacementAcknowledgement : !activity.text.startsWith(acknowledgedText)) || !emittedText.startsWith(activity.text)) return;
1777
+ const replacementAcknowledgement = typeof activity.text === "string" && replacementAcknowledgementPending && activity.text === replacementTextAwaitingAcknowledgement?.text;
1778
+ if (activity.type !== "typing" || activity.channelData?.streamType !== "streaming" || !activity.id || !activity.text || acknowledgedStreamId !== void 0 && activity.id !== acknowledgedStreamId || (replacementAcknowledgementPending ? !replacementAcknowledgement : !activity.text.startsWith(acknowledgedText)) || !replacementAcknowledgement && !emittedText.startsWith(activity.text)) return;
1788
1779
  acknowledgedStreamId = activity.id;
1789
1780
  acknowledgedText = activity.text;
1790
- if (activity.text === replacementTextAwaitingAcknowledgement) replacementTextAwaitingAcknowledgement = void 0;
1781
+ acknowledgedLogicalText = replacementAcknowledgement ? replacementTextAwaitingAcknowledgement.logicalText : activity.text;
1782
+ if (replacementAcknowledgement) replacementTextAwaitingAcknowledgement = void 0;
1791
1783
  });
1792
1784
  const wasCanceled = () => canceledLocally || Boolean(stream?.canceled);
1793
1785
  const releaseStreamChunkSubscription = () => {
@@ -1804,8 +1796,8 @@ function createTeamsReplyStreamController(params) {
1804
1796
  };
1805
1797
  };
1806
1798
  const fallbackPayloadAfterAcknowledgedText = (payload) => {
1807
- if (!acknowledgedText || typeof payload.text !== "string" || !payload.text.startsWith(acknowledgedText)) return payload;
1808
- const remainingText = payload.text.slice(acknowledgedText.length);
1799
+ if (!acknowledgedLogicalText || typeof payload.text !== "string" || !payload.text.startsWith(acknowledgedLogicalText)) return payload;
1800
+ const remainingText = payload.text.slice(acknowledgedLogicalText.length);
1809
1801
  const hasMedia = Boolean(payload.mediaUrl || payload.mediaUrls?.length);
1810
1802
  if (!remainingText && !hasMedia) return;
1811
1803
  return {
@@ -1821,15 +1813,7 @@ function createTeamsReplyStreamController(params) {
1821
1813
  } : payload;
1822
1814
  };
1823
1815
  const finalStreamActivity = (text) => ({
1824
- type: "message",
1825
- ...text ? { text } : {},
1826
- entities: [{
1827
- type: "https://schema.org/Message",
1828
- "@type": "Message",
1829
- "@context": "https://schema.org",
1830
- "@id": "",
1831
- additionalType: ["AIGeneratedContent"]
1832
- }],
1816
+ ...require_messenger.buildMSTeamsMessageActivity(text === void 0 ? void 0 : require_messenger.formatMSTeamsMarkdown(text, params.tableMode ?? "code")),
1833
1817
  channelData: params.feedbackLoopEnabled ? { feedbackLoopEnabled: true } : {}
1834
1818
  });
1835
1819
  const deferredReplacementLogicalContent = () => {
@@ -1874,7 +1858,7 @@ function createTeamsReplyStreamController(params) {
1874
1858
  pendingFinalPayload = { text: payload.text };
1875
1859
  return;
1876
1860
  }
1877
- if (streamFinalizationPending) return;
1861
+ if (streamFinalizationPending || nativeDeliveryClaimed) return;
1878
1862
  const fullText = payload.text;
1879
1863
  let prefixLength = 0;
1880
1864
  while (prefixLength < emittedText.length && prefixLength < fullText.length && emittedText[prefixLength] === fullText[prefixLength]) prefixLength += 1;
@@ -1950,9 +1934,13 @@ function createTeamsReplyStreamController(params) {
1950
1934
  });
1951
1935
  pendingFinalPayload = fallbackPayloadForSuppressedFinal(payload);
1952
1936
  try {
1953
- replacementTextAwaitingAcknowledgement = payload.text;
1954
- stream.emit(finalStreamActivity(payload.text));
1955
- finalMetadataQueued = true;
1937
+ const activity = finalStreamActivity(payload.text);
1938
+ replacementTextAwaitingAcknowledgement = {
1939
+ text: activity.text,
1940
+ logicalText: payload.text
1941
+ };
1942
+ stream.emit(activity);
1943
+ queuedFinalActivity = activity;
1956
1944
  emittedText = payload.text;
1957
1945
  tokensEmitted = false;
1958
1946
  return;
@@ -1971,31 +1959,17 @@ function createTeamsReplyStreamController(params) {
1971
1959
  return;
1972
1960
  }
1973
1961
  }
1974
- if (tokensEmitted && !streamFailed) {
1975
- const hasMedia = Boolean(payload.mediaUrl || payload.mediaUrls?.length);
1976
- pendingFinalPayload = fallbackPayloadForSuppressedFinal(payload);
1977
- streamFinalizationPending = true;
1978
- tokensEmitted = false;
1979
- return hasMedia ? {
1980
- ...payload,
1981
- text: void 0
1982
- } : void 0;
1983
- }
1984
1962
  if (streamFailed) {
1985
1963
  const fallback = failedSegmentFallbackPrepared ? payload : fallbackPayloadAfterAcknowledgedText(payload);
1986
1964
  failedSegmentFallbackPrepared = true;
1987
1965
  pendingFinalPayload = void 0;
1988
1966
  return fallback;
1989
1967
  }
1990
- if (streamMode === "progress" && payload.text) try {
1968
+ if (streamMode === "progress" && payload.text && !nativeDispatchStarted) try {
1991
1969
  stream.emit(payload.text);
1970
+ emittedText = payload.text;
1992
1971
  nativeDispatchStarted = true;
1993
- pendingFinalPayload = fallbackPayloadForSuppressedFinal(payload);
1994
- streamFinalizationPending = true;
1995
- return Boolean(payload.mediaUrl || payload.mediaUrls?.length) ? {
1996
- ...payload,
1997
- text: void 0
1998
- } : void 0;
1972
+ tokensEmitted = true;
1999
1973
  } catch (err) {
2000
1974
  if (isStreamCancelledError(err)) {
2001
1975
  canceledLocally = true;
@@ -2003,6 +1977,16 @@ function createTeamsReplyStreamController(params) {
2003
1977
  }
2004
1978
  params.log?.debug?.(`progress-mode finalize failed: ${(0, openclaw_plugin_sdk_error_runtime.coerceErrorMessage)(err)}`);
2005
1979
  }
1980
+ if (tokensEmitted) {
1981
+ const hasMedia = Boolean(payload.mediaUrl || payload.mediaUrls?.length);
1982
+ pendingFinalPayload = fallbackPayloadForSuppressedFinal(payload);
1983
+ streamFinalizationPending = true;
1984
+ tokensEmitted = false;
1985
+ return hasMedia ? {
1986
+ ...payload,
1987
+ text: void 0
1988
+ } : void 0;
1989
+ }
2006
1990
  return payload;
2007
1991
  },
2008
1992
  claimNativeDelivery() {
@@ -2031,15 +2015,34 @@ function createTeamsReplyStreamController(params) {
2031
2015
  kind: "replacement",
2032
2016
  payload: pendingFinalPayload
2033
2017
  });
2034
- replacementTextAwaitingAcknowledgement = pendingFinalPayload.text;
2018
+ const activity = finalStreamActivity(pendingFinalPayload.text);
2019
+ replacementTextAwaitingAcknowledgement = {
2020
+ text: activity.text,
2021
+ logicalText: pendingFinalPayload.text
2022
+ };
2035
2023
  logicalContent = deferredReplacementLogicalContent();
2036
- stream.emit(finalStreamActivity(pendingFinalPayload.text));
2037
- finalMetadataQueued = true;
2024
+ stream.emit(activity);
2025
+ queuedFinalActivity = activity;
2038
2026
  emittedText = pendingFinalPayload.text;
2039
2027
  }
2040
2028
  logicalContent ??= replacementSettlementPending ? deferredReplacementLogicalContent() : void 0;
2041
- const content = pendingFinalPayload?.text ?? (emittedText || void 0);
2042
- if (!finalMetadataQueued) stream.emit(finalStreamActivity());
2029
+ const logicalText = pendingFinalPayload?.text ?? (emittedText || void 0);
2030
+ const finalActivity = queuedFinalActivity ?? finalStreamActivity(logicalText);
2031
+ const content = finalActivity.text;
2032
+ logicalContent ??= content !== logicalText ? logicalText : void 0;
2033
+ if (!queuedFinalActivity) {
2034
+ if (content !== void 0 && content !== emittedText) {
2035
+ stream.clearText();
2036
+ replacementTextAwaitingAcknowledgement = {
2037
+ text: content,
2038
+ logicalText
2039
+ };
2040
+ stream.emit(finalActivity);
2041
+ } else stream.emit({
2042
+ ...finalActivity,
2043
+ text: void 0
2044
+ });
2045
+ }
2043
2046
  const result = await stream.close();
2044
2047
  streamFinalizationPending = false;
2045
2048
  if (!result) {
@@ -2092,7 +2095,8 @@ function createTeamsReplyStreamController(params) {
2092
2095
  ...postNativePayloads.length > 0 ? { postNativePayloads } : {}
2093
2096
  };
2094
2097
  } finally {
2095
- finalMetadataQueued = false;
2098
+ failedSegmentFallbackPrepared = true;
2099
+ queuedFinalActivity = void 0;
2096
2100
  replacementEmitFailed = false;
2097
2101
  replacementFinalPending = false;
2098
2102
  replacementSettlementPending = false;
@@ -2185,6 +2189,7 @@ function createMSTeamsReplyDispatcher(params) {
2185
2189
  const feedbackLoopEnabled = params.cfg.channels?.msteams?.feedbackEnabled !== false;
2186
2190
  const hookRunner = (0, openclaw_plugin_sdk_plugin_runtime.getGlobalHookRunner)();
2187
2191
  const streamController = createTeamsReplyStreamController({
2192
+ tableMode,
2188
2193
  allowProviderPreview: !((hookRunner?.hasHooks("reply_payload_sending") ?? false) || (hookRunner?.hasHooks("message_sending") ?? false)),
2189
2194
  conversationType,
2190
2195
  context: params.context,
@@ -2200,6 +2205,8 @@ function createMSTeamsReplyDispatcher(params) {
2200
2205
  const blockStreamingEnabled = blockStreamingResolved ?? false;
2201
2206
  const typingIndicatorEnabled = typeof msteamsCfg?.typingIndicator === "boolean" ? msteamsCfg.typingIndicator : true;
2202
2207
  const pendingDeliveries = [];
2208
+ let pendingSettlement;
2209
+ const findPendingNativeDelivery = () => pendingDeliveries.find((candidate) => candidate.native && !candidate.nativeSettled);
2203
2210
  const joinAcceptedContents = (contents) => contents.filter((content) => Boolean(content)).join("\n");
2204
2211
  const sendMessages = async (messages) => {
2205
2212
  return require_messenger.sendMSTeamsMessages({
@@ -2342,9 +2349,11 @@ function createMSTeamsReplyDispatcher(params) {
2342
2349
  const delivery = {
2343
2350
  observeMessageSent: true,
2344
2351
  deliver: async (payload) => {
2352
+ if (pendingSettlement) await pendingSettlement;
2345
2353
  const preparedPayload = streamController.preparePayload(payload);
2346
2354
  const native = streamController.claimNativeDelivery();
2347
- const messages = preparedPayload ? renderReplyPayload(preparedPayload) : [];
2355
+ if (preparedPayload && !native && findPendingNativeDelivery()) await settleDelivery();
2356
+ const messages = preparedPayload && !streamController.wasCanceled() ? renderReplyPayload(preparedPayload) : [];
2348
2357
  if (!native && messages.length === 0) return {
2349
2358
  visibleReplySent: false,
2350
2359
  suppression: { reason: "no_visible_result" }
@@ -2370,9 +2379,9 @@ function createMSTeamsReplyDispatcher(params) {
2370
2379
  });
2371
2380
  }
2372
2381
  };
2373
- const settleDelivery = async () => {
2382
+ const settleDelivery = () => pendingSettlement ??= Promise.resolve().then(async () => {
2374
2383
  await flushPendingMessages();
2375
- const nativeDelivery = pendingDeliveries.find((candidate) => candidate.native && !candidate.nativeSettled);
2384
+ const nativeDelivery = findPendingNativeDelivery();
2376
2385
  if (!nativeDelivery) {
2377
2386
  await streamController.finalize();
2378
2387
  return;
@@ -2406,7 +2415,9 @@ function createMSTeamsReplyDispatcher(params) {
2406
2415
  } catch (error) {
2407
2416
  params.log.warn?.("failed to record sent Teams message id", { error: require_channel_setup.formatUnknownError(error) });
2408
2417
  }
2409
- };
2418
+ }).finally(() => {
2419
+ pendingSettlement = void 0;
2420
+ });
2410
2421
  const shouldSuppressDefaultToolProgressMessages = streamController.hasStream() && teamsStreamMode === "progress";
2411
2422
  const progressCallbacks = streamController.hasStream() ? {
2412
2423
  onReasoningStream: async (payload) => {
@@ -2592,7 +2603,6 @@ async function dispatchMSTeamsInboundTurn(params) {
2592
2603
  allowNameMatching
2593
2604
  }).allowed
2594
2605
  }) : true;
2595
- const bodyForAgent = threadContext ? `[Thread history]\n${threadContext}\n[/Thread history]\n\n${agentBody}` : agentBody;
2596
2606
  const nativeChannelId = isChannel && teamAadGroupId ? `${teamAadGroupId}/${graphChannelId}` : void 0;
2597
2607
  const boundIngress = await resolveMSTeamsSenderAccess({
2598
2608
  cfg,
@@ -2611,13 +2621,25 @@ async function dispatchMSTeamsInboundTurn(params) {
2611
2621
  channelIngress: boundIngress.channelIngress,
2612
2622
  channel: "msteams",
2613
2623
  contextVisibility: contextVisibilityMode,
2614
- supplemental: { quote: quoteInfo ? {
2615
- id: quoteInfo.id ?? activity.replyToId ?? void 0,
2616
- body: quoteBodyFull ?? quoteInfo.body,
2617
- sender: quoteInfo.sender,
2618
- senderAllowed: quoteSenderAllowed,
2619
- isQuote: true
2620
- } : void 0 },
2624
+ supplemental: {
2625
+ channelStructuredContext: threadContext.length ? [{
2626
+ label: "Thread history",
2627
+ source: "msteams",
2628
+ type: "chat_window",
2629
+ sessionTranscriptMode: "preserve",
2630
+ payload: {
2631
+ order: "chronological",
2632
+ messages: threadContext
2633
+ }
2634
+ }] : void 0,
2635
+ quote: quoteInfo ? {
2636
+ id: quoteInfo.id ?? activity.replyToId ?? void 0,
2637
+ body: quoteBodyFull ?? quoteInfo.body,
2638
+ sender: quoteInfo.sender,
2639
+ senderAllowed: quoteSenderAllowed,
2640
+ isQuote: true
2641
+ } : void 0
2642
+ },
2621
2643
  media: await (0, openclaw_plugin_sdk_channel_inbound.toInboundMediaFactsWithMetadata)(inboundMedia),
2622
2644
  messageId: activity.id,
2623
2645
  timestamp: timestamp?.getTime() ?? Date.now(),
@@ -2648,7 +2670,7 @@ async function dispatchMSTeamsInboundTurn(params) {
2648
2670
  },
2649
2671
  message: {
2650
2672
  body: combinedBody,
2651
- bodyForAgent,
2673
+ bodyForAgent: agentBody,
2652
2674
  inboundHistory,
2653
2675
  rawBody,
2654
2676
  commandBody
@@ -2709,7 +2731,7 @@ async function dispatchMSTeamsInboundTurn(params) {
2709
2731
  id: activity.id ?? `${teamsFrom}:${Date.now()}`,
2710
2732
  timestamp: timestamp?.getTime(),
2711
2733
  rawText: rawBody,
2712
- textForAgent: bodyForAgent,
2734
+ textForAgent: agentBody,
2713
2735
  textForCommands: commandBody,
2714
2736
  raw: activity
2715
2737
  }),
@@ -3052,7 +3074,7 @@ async function resolveMSTeamsThreadContext(params) {
3052
3074
  } catch (err) {
3053
3075
  params.log.debug?.("failed to fetch full quoted message text", { error: require_channel_setup.formatUnknownError(err) });
3054
3076
  }
3055
- let threadContext;
3077
+ let threadContext = [];
3056
3078
  const threadParentId = params.routing.threadRootId;
3057
3079
  if (threadParentId && threadParentId !== activity.id && params.isChannel && teamAadGroupId) try {
3058
3080
  const graphToken = await require_resolve_allowlist.withMSTeamsRequestDeadline({
@@ -3103,7 +3125,7 @@ async function resolveMSTeamsThreadContext(params) {
3103
3125
  kind: "thread",
3104
3126
  isSenderAllowed: isThreadSenderAllowed
3105
3127
  });
3106
- threadContext = require_reaction_types.formatThreadContext(threadMessages, activity.id) || void 0;
3128
+ threadContext = require_reaction_types.buildThreadContext(threadMessages, activity.id);
3107
3129
  } catch (err) {
3108
3130
  params.log.debug?.("failed to fetch thread history", { error: require_channel_setup.formatUnknownError(err) });
3109
3131
  }
@@ -2,7 +2,23 @@
2
2
  "id": "msteams",
3
3
  "doctorContract": {
4
4
  "configRepair": true,
5
- "stateMigrations": true
5
+ "stateMigrations": [
6
+ {
7
+ "id": "msteams-conversations-json-to-plugin-state"
8
+ },
9
+ {
10
+ "id": "msteams-polls-json-to-plugin-state"
11
+ },
12
+ {
13
+ "id": "msteams-sso-tokens-json-to-plugin-state"
14
+ },
15
+ {
16
+ "id": "msteams-delegated-token-json-to-plugin-state"
17
+ },
18
+ {
19
+ "id": "msteams-feedback-learnings-json-to-plugin-state"
20
+ }
21
+ ]
6
22
  },
7
23
  "name": "Microsoft Teams",
8
24
  "description": "OpenClaw Microsoft Teams channel plugin for bot conversations.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/msteams",
3
- "version": "2026.9.2",
3
+ "version": "2026.9.3",
4
4
  "description": "OpenClaw Microsoft Teams channel plugin for bot conversations.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,7 +22,7 @@
22
22
  "openclaw": "workspace:*"
23
23
  },
24
24
  "peerDependencies": {
25
- "openclaw": ">=2026.9.2"
25
+ "openclaw": ">=2026.9.3"
26
26
  },
27
27
  "peerDependenciesMeta": {
28
28
  "openclaw": {
@@ -72,10 +72,10 @@
72
72
  "minHostVersion": ">=2026.4.10"
73
73
  },
74
74
  "compat": {
75
- "pluginApi": ">=2026.9.2"
75
+ "pluginApi": ">=2026.9.3"
76
76
  },
77
77
  "build": {
78
- "openclawVersion": "2026.9.2",
78
+ "openclawVersion": "2026.9.3",
79
79
  "runtimeFormat": "cjs"
80
80
  },
81
81
  "release": {