@openclaw/msteams 2026.6.1 → 2026.6.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.
@@ -1,223 +1,8 @@
1
- import { S as normalizeSecretInputString, _ as hasConfiguredMSTeamsCredentials, a as searchGraphUsers, b as saveDelegatedTokens, d as listTeamsByName, f as normalizeQuery, g as resolveGraphToken, r as formatUnknownError, u as listChannelsForTeam, y as resolveMSTeamsCredentials } from "./errors-Dpn8B05h.js";
2
- import { mapAllowlistResolutionInputs } from "openclaw/plugin-sdk/allow-from";
3
- import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
1
+ import { C as resolveMSTeamsCredentials, T as normalizeSecretInputString, c as resolveMSTeamsUserAllowlist, o as parseMSTeamsTeamEntry, s as resolveMSTeamsChannelAllowlist, w as saveDelegatedTokens, x as hasConfiguredMSTeamsCredentials } from "./resolve-allowlist-BzIUWmMm.js";
2
+ import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
3
+ import { asFiniteNumberInRange, parseStrictFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
4
4
  import { DEFAULT_ACCOUNT_ID, createSetupTranslator, createStandardChannelSetupStatus, createTopLevelChannelAllowFromSetter, createTopLevelChannelDmPolicy, createTopLevelChannelGroupPolicySetter, mergeAllowFromEntries, splitSetupEntries } from "openclaw/plugin-sdk/setup";
5
5
  import { formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
6
- //#region extensions/msteams/src/resolve-allowlist.ts
7
- function stripProviderPrefix(raw) {
8
- return raw.replace(/^(msteams|teams):/i, "");
9
- }
10
- function normalizeMSTeamsMessagingTarget(raw) {
11
- let trimmed = raw.trim();
12
- if (!trimmed) return;
13
- trimmed = stripProviderPrefix(trimmed).trim();
14
- if (/^conversation:/i.test(trimmed)) {
15
- const id = trimmed.slice(13).trim();
16
- return id ? `conversation:${id}` : void 0;
17
- }
18
- if (/^user:/i.test(trimmed)) {
19
- const id = trimmed.slice(5).trim();
20
- return id ? `user:${id}` : void 0;
21
- }
22
- return trimmed || void 0;
23
- }
24
- function normalizeMSTeamsUserInput(raw) {
25
- return stripProviderPrefix(raw).replace(/^(user|conversation):/i, "").trim();
26
- }
27
- function parseMSTeamsConversationId(raw) {
28
- const trimmed = stripProviderPrefix(raw).trim();
29
- if (!/^conversation:/i.test(trimmed)) return null;
30
- return trimmed.slice(13).trim();
31
- }
32
- /**
33
- * Detect whether a raw target string looks like a Microsoft Teams conversation
34
- * or user id that cron announce delivery and other explicit-target paths can
35
- * forward verbatim to the channel adapter.
36
- *
37
- * Accepts both prefixed and bare formats:
38
- * - `conversation:<id>` — explicit conversation prefix
39
- * - `user:<aad-guid>` — user id (16+ hex chars, UUID-like)
40
- * - `19:abc@thread.tacv2` / `19:abc@thread.skype` — channel / legacy group
41
- * - `19:{userId}_{appId}@unq.gbl.spaces` — Graph 1:1 chat thread format
42
- * - `a:1xxx` — Bot Framework personal (1:1) chat id
43
- * - `8:orgid:xxx` — Bot Framework org-scoped personal chat id
44
- * - `29:xxx` — Bot Framework user id
45
- *
46
- * Display-name user targets such as `user:John Smith` intentionally return
47
- * false so that the Graph API directory lookup still runs for them.
48
- */
49
- function looksLikeMSTeamsTargetId(raw) {
50
- const trimmed = raw.trim();
51
- if (!trimmed) return false;
52
- if (/^conversation:/i.test(trimmed)) return true;
53
- if (/^user:/i.test(trimmed)) {
54
- const id = trimmed.slice(5).trim();
55
- return /^[0-9a-fA-F-]{16,}$/.test(id);
56
- }
57
- if (/^19:.+@thread\.(tacv2|skype)$/i.test(trimmed)) return true;
58
- if (/^19:.+@unq\.gbl\.spaces$/i.test(trimmed)) return true;
59
- if (/^a:1[A-Za-z0-9_-]+$/i.test(trimmed)) return true;
60
- if (/^8:orgid:[A-Za-z0-9-]+$/i.test(trimmed)) return true;
61
- if (/^29:[A-Za-z0-9_-]+$/i.test(trimmed)) return true;
62
- return /@thread\b/i.test(trimmed);
63
- }
64
- function normalizeMSTeamsTeamKey(raw) {
65
- return stripProviderPrefix(raw).replace(/^team:/i, "").trim() || void 0;
66
- }
67
- function normalizeMSTeamsChannelKey(raw) {
68
- return (raw?.trim().replace(/^#/, "").trim() ?? "") || void 0;
69
- }
70
- function normalizeMSTeamsConversationTargetId(raw) {
71
- const trimmed = stripProviderPrefix(raw).trim();
72
- return parseMSTeamsConversationId(trimmed) ?? trimmed;
73
- }
74
- function looksLikeMSTeamsThreadConversationId(raw) {
75
- const normalized = normalizeMSTeamsConversationTargetId(raw);
76
- return /^19:.+@thread\./i.test(normalized);
77
- }
78
- function parseMSTeamsTeamChannelInput(raw) {
79
- const trimmed = stripProviderPrefix(raw).trim();
80
- if (!trimmed) return {};
81
- const parts = trimmed.split("/");
82
- const team = normalizeMSTeamsTeamKey(parts[0] ?? "");
83
- const channel = parts.length > 1 ? normalizeMSTeamsChannelKey(parts.slice(1).join("/")) : void 0;
84
- return {
85
- ...team ? { team } : {},
86
- ...channel ? { channel } : {}
87
- };
88
- }
89
- function parseMSTeamsTeamEntry(raw) {
90
- const { team, channel } = parseMSTeamsTeamChannelInput(raw);
91
- if (!team) return null;
92
- return {
93
- teamKey: team,
94
- ...channel ? { channelKey: channel } : {}
95
- };
96
- }
97
- async function resolveMSTeamsChannelAllowlist(params) {
98
- let tokenPromise;
99
- const getToken = () => {
100
- tokenPromise ??= resolveGraphToken(params.cfg);
101
- return tokenPromise;
102
- };
103
- return await mapAllowlistResolutionInputs({
104
- inputs: params.entries,
105
- mapInput: async (input) => {
106
- const { team, channel } = parseMSTeamsTeamChannelInput(input);
107
- if (!team) return {
108
- input,
109
- resolved: false
110
- };
111
- if (looksLikeMSTeamsThreadConversationId(team)) {
112
- const teamId = normalizeMSTeamsConversationTargetId(team);
113
- if (!channel) return {
114
- input,
115
- resolved: true,
116
- teamId,
117
- teamName: teamId
118
- };
119
- if (!looksLikeMSTeamsThreadConversationId(channel)) return {
120
- input,
121
- resolved: false,
122
- teamId,
123
- teamName: teamId,
124
- note: "channel id required for conversation-id team"
125
- };
126
- const channelId = normalizeMSTeamsConversationTargetId(channel);
127
- return {
128
- input,
129
- resolved: true,
130
- teamId,
131
- teamName: teamId,
132
- channelId,
133
- channelName: channelId
134
- };
135
- }
136
- const token = await getToken();
137
- const teams = /^[0-9a-fA-F-]{16,}$/.test(team) ? [{
138
- id: team,
139
- displayName: team
140
- }] : await listTeamsByName(token, team);
141
- if (teams.length === 0) return {
142
- input,
143
- resolved: false,
144
- note: "team not found"
145
- };
146
- const teamMatch = teams[0];
147
- const graphTeamId = teamMatch.id?.trim();
148
- const teamName = teamMatch.displayName?.trim() || team;
149
- if (!graphTeamId) return {
150
- input,
151
- resolved: false,
152
- note: "team id missing"
153
- };
154
- let teamChannels = [];
155
- try {
156
- teamChannels = await listChannelsForTeam(token, graphTeamId);
157
- } catch {}
158
- const teamId = teamChannels.find((ch) => normalizeOptionalLowercaseString(ch.displayName) === "general")?.id?.trim() || graphTeamId;
159
- if (!channel) return {
160
- input,
161
- resolved: true,
162
- teamId,
163
- teamName,
164
- note: teams.length > 1 ? "multiple teams; chose first" : void 0
165
- };
166
- const normalizedChannel = normalizeOptionalLowercaseString(channel);
167
- const channelMatch = teamChannels.find((item) => item.id === channel) ?? teamChannels.find((item) => normalizeOptionalLowercaseString(item.displayName) === normalizedChannel) ?? teamChannels.find((item) => normalizeLowercaseStringOrEmpty(item.displayName ?? "").includes(normalizedChannel ?? ""));
168
- if (!channelMatch?.id) return {
169
- input,
170
- resolved: false,
171
- note: "channel not found"
172
- };
173
- return {
174
- input,
175
- resolved: true,
176
- teamId,
177
- teamName,
178
- channelId: channelMatch.id,
179
- channelName: channelMatch.displayName ?? channel,
180
- note: teamChannels.length > 1 ? "multiple channels; chose first" : void 0
181
- };
182
- }
183
- });
184
- }
185
- async function resolveMSTeamsUserAllowlist(params) {
186
- const token = await resolveGraphToken(params.cfg);
187
- return await mapAllowlistResolutionInputs({
188
- inputs: params.entries,
189
- mapInput: async (input) => {
190
- const query = normalizeQuery(normalizeMSTeamsUserInput(input));
191
- if (!query) return {
192
- input,
193
- resolved: false
194
- };
195
- if (/^[0-9a-fA-F-]{16,}$/.test(query)) return {
196
- input,
197
- resolved: true,
198
- id: query
199
- };
200
- const users = await searchGraphUsers({
201
- token,
202
- query,
203
- top: 10
204
- });
205
- const match = users[0];
206
- if (!match?.id) return {
207
- input,
208
- resolved: false
209
- };
210
- return {
211
- input,
212
- resolved: true,
213
- id: match.id,
214
- name: match.displayName ?? void 0,
215
- note: users.length > 1 ? "multiple matches; chose first" : void 0
216
- };
217
- }
218
- });
219
- }
220
- //#endregion
221
6
  //#region extensions/msteams/src/setup-core.ts
222
7
  const t$1 = createSetupTranslator();
223
8
  const msteamsSetupAdapter = {
@@ -321,6 +106,181 @@ function createMSTeamsSetupWizardBase() {
321
106
  };
322
107
  }
323
108
  //#endregion
109
+ //#region extensions/msteams/src/errors.ts
110
+ const MAX_SAFE_RETRY_AFTER_SECONDS = Number.MAX_SAFE_INTEGER / 1e3;
111
+ function formatUnknownError(err) {
112
+ if (err instanceof Error) return err.message;
113
+ if (typeof err === "string") return err;
114
+ if (err === null) return "null";
115
+ if (err === void 0) return "undefined";
116
+ if (typeof err === "number" || typeof err === "boolean" || typeof err === "bigint") return String(err);
117
+ if (typeof err === "symbol") return err.description ?? err.toString();
118
+ if (typeof err === "function") return err.name ? `[function ${err.name}]` : "[function]";
119
+ try {
120
+ return JSON.stringify(err) ?? "unknown error";
121
+ } catch {
122
+ return "unknown error";
123
+ }
124
+ }
125
+ function extractStatusCode(err) {
126
+ if (!isRecord(err)) return null;
127
+ const parseStatusCode = (value) => {
128
+ if (typeof value === "number") return Number.isInteger(value) && value >= 100 && value <= 599 ? value : null;
129
+ if (typeof value === "string") {
130
+ const trimmed = value.trim();
131
+ if (!/^\d{3}$/.test(trimmed)) return null;
132
+ const parsed = Number(trimmed);
133
+ return parsed >= 100 && parsed <= 599 ? parsed : null;
134
+ }
135
+ return null;
136
+ };
137
+ const directStatus = parseStatusCode(err.statusCode ?? err.status);
138
+ if (directStatus !== null) return directStatus;
139
+ const response = err.response;
140
+ if (isRecord(response)) {
141
+ const responseStatus = parseStatusCode(response.status);
142
+ if (responseStatus !== null) return responseStatus;
143
+ }
144
+ return null;
145
+ }
146
+ function extractErrorCode(err) {
147
+ if (!isRecord(err)) return null;
148
+ const direct = err.code;
149
+ if (typeof direct === "string" && direct.trim()) return direct;
150
+ const response = err.response;
151
+ if (!isRecord(response)) return null;
152
+ const body = response.body;
153
+ if (isRecord(body)) {
154
+ const error = body.error;
155
+ if (isRecord(error) && typeof error.code === "string" && error.code.trim()) return error.code;
156
+ }
157
+ return null;
158
+ }
159
+ function extractRetryAfterMs(err) {
160
+ if (!isRecord(err)) return null;
161
+ const directMs = asFiniteNumberInRange(err.retryAfterMs ?? err.retry_after_ms, {
162
+ min: 0,
163
+ max: Number.MAX_SAFE_INTEGER
164
+ });
165
+ if (directMs !== void 0) return directMs;
166
+ const retryAfter = err.retryAfter ?? err.retry_after;
167
+ const retryAfterSeconds = asFiniteNumberInRange(retryAfter, {
168
+ min: 0,
169
+ max: MAX_SAFE_RETRY_AFTER_SECONDS
170
+ });
171
+ if (retryAfterSeconds !== void 0) return retryAfterSeconds * 1e3;
172
+ if (typeof retryAfter === "string") {
173
+ const parsed = parseNonNegativeRetryAfterSeconds(retryAfter);
174
+ if (parsed !== void 0) return parsed * 1e3;
175
+ }
176
+ const response = err.response;
177
+ if (!isRecord(response)) return null;
178
+ const headers = response.headers;
179
+ if (!headers) return null;
180
+ if (isRecord(headers)) {
181
+ const raw = headers["retry-after"] ?? headers["Retry-After"];
182
+ if (typeof raw === "string") {
183
+ const parsed = parseNonNegativeRetryAfterSeconds(raw);
184
+ if (parsed !== void 0) return parsed * 1e3;
185
+ }
186
+ }
187
+ if (typeof headers === "object" && headers !== null && "get" in headers && typeof headers.get === "function") {
188
+ const raw = headers.get("retry-after");
189
+ if (raw) {
190
+ const parsed = parseNonNegativeRetryAfterSeconds(raw);
191
+ if (parsed !== void 0) return parsed * 1e3;
192
+ }
193
+ }
194
+ return null;
195
+ }
196
+ function parseNonNegativeRetryAfterSeconds(raw) {
197
+ const trimmed = raw.trim();
198
+ if (!/^\d+(?:\.\d+)?$/.test(trimmed)) return;
199
+ return asFiniteNumberInRange(parseStrictFiniteNumber(trimmed), {
200
+ min: 0,
201
+ max: MAX_SAFE_RETRY_AFTER_SECONDS
202
+ });
203
+ }
204
+ /**
205
+ * Classify outbound send errors for safe retries and actionable logs.
206
+ *
207
+ * Important: We only mark errors as retryable when we have an explicit HTTP
208
+ * status code that indicates the message was not accepted (e.g. 429, 5xx).
209
+ * For transport-level errors where delivery is ambiguous, we prefer to avoid
210
+ * retries to reduce the chance of duplicate posts.
211
+ */
212
+ function classifyMSTeamsSendError(err) {
213
+ const statusCode = extractStatusCode(err);
214
+ const retryAfterMs = extractRetryAfterMs(err);
215
+ const errorCode = extractErrorCode(err) ?? void 0;
216
+ if (statusCode === 401) return {
217
+ kind: "auth",
218
+ statusCode,
219
+ errorCode
220
+ };
221
+ if (statusCode === 403) {
222
+ if (errorCode === "ContentStreamNotAllowed") return {
223
+ kind: "permanent",
224
+ statusCode,
225
+ errorCode
226
+ };
227
+ return {
228
+ kind: "auth",
229
+ statusCode,
230
+ errorCode
231
+ };
232
+ }
233
+ if (statusCode === 429) return {
234
+ kind: "throttled",
235
+ statusCode,
236
+ retryAfterMs: retryAfterMs ?? void 0,
237
+ errorCode
238
+ };
239
+ if (statusCode === 408 || statusCode != null && statusCode >= 500) return {
240
+ kind: "transient",
241
+ statusCode,
242
+ retryAfterMs: retryAfterMs ?? void 0,
243
+ errorCode
244
+ };
245
+ if (statusCode != null && statusCode >= 400) return {
246
+ kind: "permanent",
247
+ statusCode,
248
+ errorCode
249
+ };
250
+ if (statusCode == null) {
251
+ const networkCode = isRecord(err) && typeof err.code === "string" ? err.code : null;
252
+ if (networkCode === "ECONNREFUSED" || networkCode === "ENOTFOUND" || networkCode === "EHOSTUNREACH" || networkCode === "ETIMEDOUT" || networkCode === "ECONNRESET") return {
253
+ kind: "network",
254
+ errorCode: networkCode
255
+ };
256
+ }
257
+ return {
258
+ kind: "unknown",
259
+ statusCode: statusCode ?? void 0,
260
+ retryAfterMs: retryAfterMs ?? void 0,
261
+ errorCode
262
+ };
263
+ }
264
+ /**
265
+ * Detect whether an error is caused by a revoked Proxy.
266
+ *
267
+ * The Bot Framework SDK wraps TurnContext in a Proxy that is revoked once the
268
+ * turn handler returns. Any later access (e.g. from a debounced callback)
269
+ * throws a TypeError whose message contains the distinctive "proxy that has
270
+ * been revoked" string.
271
+ */
272
+ function isRevokedProxyError(err) {
273
+ if (!(err instanceof TypeError)) return false;
274
+ return /proxy that has been revoked/i.test(err.message);
275
+ }
276
+ function formatMSTeamsSendErrorHint(classification) {
277
+ if (classification.kind === "auth") return "check msteams appId/appPassword/tenantId (or env vars MSTEAMS_APP_ID/MSTEAMS_APP_PASSWORD/MSTEAMS_TENANT_ID)";
278
+ if (classification.errorCode === "ContentStreamNotAllowed") return "Teams expired the content stream; stop streaming earlier and fall back to normal message delivery";
279
+ if (classification.kind === "throttled") return "Teams throttled the bot; backing off may help";
280
+ if (classification.kind === "transient") return "transient Teams/Bot Framework error; retry may succeed";
281
+ if (classification.kind === "network") return "transport-level failure sending reply to Teams Bot Connector (smba.trafficmanager.net) — check egress firewall rules allow outbound HTTPS to smba.trafficmanager.net";
282
+ }
283
+ //#endregion
324
284
  //#region extensions/msteams/src/setup-surface.ts
325
285
  const t = createSetupTranslator();
326
286
  const channel = "msteams";
@@ -489,7 +449,7 @@ const msteamsSetupWizard = {
489
449
  }
490
450
  };
491
451
  try {
492
- const { loginMSTeamsDelegated } = await import("./oauth-ei63gdyS.js");
452
+ const { loginMSTeamsDelegated } = await import("./oauth-CDUB5xPY.js");
493
453
  const progress = params.prompter.progress(t("wizard.msteams.delegatedOAuthProgress"));
494
454
  saveDelegatedTokens(await loginMSTeamsDelegated({
495
455
  isRemote: true,
@@ -530,4 +490,4 @@ const msteamsSetupWizard = {
530
490
  })
531
491
  };
532
492
  //#endregion
533
- export { looksLikeMSTeamsTargetId as a, parseMSTeamsConversationId as c, resolveMSTeamsUserAllowlist as d, msteamsSetupAdapter as i, parseMSTeamsTeamChannelInput as l, openDelegatedOAuthUrl as n, normalizeMSTeamsMessagingTarget as o, createMSTeamsSetupWizardBase as r, normalizeMSTeamsUserInput as s, msteamsSetupWizard as t, resolveMSTeamsChannelAllowlist as u };
493
+ export { formatUnknownError as a, msteamsSetupAdapter as c, formatMSTeamsSendErrorHint as i, openDelegatedOAuthUrl as n, isRevokedProxyError as o, classifyMSTeamsSendError as r, createMSTeamsSetupWizardBase as s, msteamsSetupWizard as t };
@@ -1,8 +1,11 @@
1
- import { A as summarizeMapping, D as resolveDefaultGroupPolicy, E as resolveChannelMediaMaxBytes, M as getMSTeamsRuntime, N as getOptionalMSTeamsRuntime, _ as isDangerousNameMatchingEnabled, a as buildMediaPayload, b as logTypingFailure, c as createChannelMessageReplyPipeline, f as dispatchReplyFromConfigWithSettledDispatcher$1, l as createChannelPairingController, n as DEFAULT_WEBHOOK_MAX_BODY_BYTES, t as DEFAULT_ACCOUNT_ID, v as keepHttpServerTaskAlive, x as mergeAllowlist } from "./runtime-api-BlvMnDKz.js";
2
- import { $ as safeFetchWithPolicy, B as estimateBase64DecodedBytes, E as loadMSTeamsSdkWithAuth, F as ATTACHMENT_TAG_RE, G as isLikelyImageAttachment, H as extractInlineImageCandidates, I as GRAPH_ROOT, J as normalizeContentType, K as isRecord$1, L as IMG_SRC_RE, M as tryNormalizeBotFrameworkServiceUrl, N as resolveMSTeamsSdkCloudOptions, O as ensureUserAgentHeader, Q as resolveRequestUrl, R as applyAuthorizationHeaderForUrl, T as createMSTeamsTokenProvider, U as inferPlaceholder, V as extractHtmlFromAttachment, W as isDownloadableAttachment, X as resolveAttachmentFetchPolicy, Y as readNestedString, Z as resolveMediaSsrfPolicy, et as safeHostForUrl, l as fetchGraphJson, n as formatMSTeamsSendErrorHint, q as isUrlAllowed, r as formatUnknownError, t as classifyMSTeamsSendError, tt as tryBuildGraphSharesUrlForSharedLink, w as createMSTeamsExpressAdapter, x as resolveMSTeamsStorePath, y as resolveMSTeamsCredentials, z as encodeGraphShareId } from "./errors-Dpn8B05h.js";
3
- import { d as resolveMSTeamsUserAllowlist, u as resolveMSTeamsChannelAllowlist } from "./setup-surface-DGTz8Mlf.js";
4
- import { a as resolveMSTeamsReplyPolicy, i as resolveMSTeamsAllowlistMatch, o as resolveMSTeamsRouteConfig } from "./channel-Dhw8AvTl.js";
5
- import { C as resolveMSTeamsSqliteStateEnv, E as readJsonFile, S as createMSTeamsConversationStoreState, T as withMSTeamsSqliteMutationLock, _ as buildFileInfoCard, b as createMSTeamsPollStoreState, c as renderReplyPayloadsToMessages, d as withRevokedProxyFallback, f as resolveGraphChatId, g as removePendingUploadFs, h as getPendingUploadFs, l as sendMSTeamsMessages, m as removePendingUpload, p as getPendingUpload, s as buildConversationReference, u as sendMSTeamsActivityWithReference, v as parseFileConsentInvoke, w as toPluginJsonValue, x as extractMSTeamsPollVote, y as uploadToConsentUrl } from "./probe-DYb-0Hmx.js";
1
+ import { n as getOptionalMSTeamsRuntime, t as getMSTeamsRuntime } from "./runtime-BS5AZrKK.js";
2
+ import { DEFAULT_ACCOUNT_ID, DEFAULT_WEBHOOK_MAX_BODY_BYTES, buildMediaPayload, createChannelMessageReplyPipeline, createChannelPairingController, dispatchReplyFromConfigWithSettledDispatcher as dispatchReplyFromConfigWithSettledDispatcher$1, isDangerousNameMatchingEnabled, keepHttpServerTaskAlive, logTypingFailure, mergeAllowlist, resolveChannelMediaMaxBytes, resolveDefaultGroupPolicy, summarizeMapping } from "./runtime-api.js";
3
+ import { $ as inferPlaceholder, C as resolveMSTeamsCredentials, F as loadMSTeamsSdkWithAuth, G as ATTACHMENT_TAG_RE, J as applyAuthorizationHeaderForUrl, K as GRAPH_ROOT, L as ensureUserAgentHeader, N as createMSTeamsExpressAdapter, P as createMSTeamsTokenProvider, Q as extractInlineImageCandidates, U as resolveMSTeamsSdkCloudOptions, V as tryNormalizeBotFrameworkServiceUrl, X as estimateBase64DecodedBytes, Y as encodeGraphShareId, Z as extractHtmlFromAttachment, at as readNestedString, c as resolveMSTeamsUserAllowlist, ct as resolveRequestUrl, dt as tryBuildGraphSharesUrlForSharedLink, et as isDownloadableAttachment, it as normalizeContentType, lt as safeFetchWithPolicy, nt as isRecord$1, ot as resolveAttachmentFetchPolicy, p as fetchGraphJson, q as IMG_SRC_RE, rt as isUrlAllowed, s as resolveMSTeamsChannelAllowlist, st as resolveMediaSsrfPolicy, tt as isLikelyImageAttachment, ut as safeHostForUrl } from "./resolve-allowlist-BzIUWmMm.js";
4
+ import { a as resolveMSTeamsReplyPolicy, i as resolveMSTeamsAllowlistMatch, o as resolveMSTeamsRouteConfig } from "./channel--oT3fyD5.js";
5
+ import { a as formatUnknownError, i as formatMSTeamsSendErrorHint, r as classifyMSTeamsSendError } from "./setup-surface-Dik4VU7f.js";
6
+ import { l as createMSTeamsPollStoreState, u as extractMSTeamsPollVote, v as createMSTeamsConversationStoreState } from "./polls-C1VgSvKE.js";
7
+ import { i as createMSTeamsSsoTokenStoreFs } from "./sso-token-store-BYZaKr82.js";
8
+ import { _ as buildFileInfoCard, c as renderReplyPayloadsToMessages, d as withRevokedProxyFallback, f as resolveGraphChatId, g as removePendingUploadFs, h as getPendingUploadFs, l as sendMSTeamsMessages, m as removePendingUpload, p as getPendingUpload, s as buildConversationReference, u as sendMSTeamsActivityWithReference, v as parseFileConsentInvoke, y as uploadToConsentUrl } from "./probe-C-rWMb-m.js";
6
9
  import { formatAllowlistMatchMeta } from "openclaw/plugin-sdk/allow-from";
7
10
  import { buildChannelProgressDraftLine, buildChannelProgressDraftLineForEntry, createChannelProgressDraftGate, formatChannelProgressDraftText, isChannelProgressDraftWorkToolName, mergeChannelProgressDraftLine, normalizeChannelProgressDraftLineIdentity, resolveChannelPreviewStreamMode, resolveChannelProgressDraftMaxLines, resolveChannelStreamingBlockEnabled, resolveChannelStreamingPreviewToolProgress, resolveChannelStreamingSuppressDefaultToolProgressMessages } from "openclaw/plugin-sdk/channel-outbound";
8
11
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -12,8 +15,7 @@ import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
12
15
  import path from "node:path";
13
16
  import { asDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
14
17
  import { appendRegularFile } from "openclaw/plugin-sdk/security-runtime";
15
- import crypto, { createHash } from "node:crypto";
16
- import fs from "node:fs/promises";
18
+ import crypto from "node:crypto";
17
19
  import { resolveThreadSessionKeys } from "openclaw/plugin-sdk/routing";
18
20
  import { channelIngressRoutes, resolveStableChannelMessageIngress } from "openclaw/plugin-sdk/channel-ingress-runtime";
19
21
  import { filterSupplementalContextItems, resolveChannelContextVisibilityMode } from "openclaw/plugin-sdk/context-visibility-runtime";
@@ -3425,123 +3427,6 @@ async function runMSTeamsFileConsentInvokeHandler(context, log) {
3425
3427
  }
3426
3428
  }
3427
3429
  //#endregion
3428
- //#region extensions/msteams/src/sso-token-store.ts
3429
- /**
3430
- * SQLite-backed store for Bot Framework OAuth SSO tokens.
3431
- *
3432
- * Tokens are keyed by (connectionName, userId). `userId` should be the
3433
- * stable AAD object ID (`activity.from.aadObjectId`) when available,
3434
- * falling back to the Bot Framework `activity.from.id`.
3435
- *
3436
- * The store is intentionally minimal: it persists the exchanged user
3437
- * token plus its expiration so consumers (for example tool handlers
3438
- * that call Microsoft Graph with delegated permissions) can fetch a
3439
- * valid token without reaching back into Bot Framework every turn.
3440
- */
3441
- const STORE_FILENAME = "msteams-sso-tokens.json";
3442
- const SSO_TOKENS_NAMESPACE = "sso-tokens";
3443
- const SSO_TOKEN_MIGRATIONS_NAMESPACE = "sso-token-migrations";
3444
- const SSO_TOKEN_LOCK_FILENAME = "msteams-sso-tokens.sqlite.lock";
3445
- const MAX_SSO_TOKENS = 5e3;
3446
- const STORE_KEY_VERSION_PREFIX = "v2:";
3447
- function makeKey(connectionName, userId) {
3448
- return `${STORE_KEY_VERSION_PREFIX}${createHash("sha256").update(JSON.stringify([connectionName, userId])).digest("hex")}`;
3449
- }
3450
- function buildMigrationKey(filePath) {
3451
- return `legacy-json:${createHash("sha256").update(filePath).digest("hex")}`;
3452
- }
3453
- function buildMigrationContentKey(filePath, value) {
3454
- return `legacy-json-content:${createHash("sha256").update(filePath).update("\0").update(JSON.stringify(value) ?? "undefined").digest("hex")}`;
3455
- }
3456
- function createTokenStore(params) {
3457
- return getMSTeamsRuntime().state.openKeyedStore({
3458
- namespace: SSO_TOKENS_NAMESPACE,
3459
- maxEntries: MAX_SSO_TOKENS,
3460
- env: resolveMSTeamsSqliteStateEnv(params)
3461
- });
3462
- }
3463
- function createMigrationStore(params) {
3464
- return getMSTeamsRuntime().state.openKeyedStore({
3465
- namespace: SSO_TOKEN_MIGRATIONS_NAMESPACE,
3466
- maxEntries: 100,
3467
- env: resolveMSTeamsSqliteStateEnv(params)
3468
- });
3469
- }
3470
- function normalizeStoredToken(value) {
3471
- if (!value || typeof value !== "object") return null;
3472
- const token = value;
3473
- if (typeof token.connectionName !== "string" || !token.connectionName || typeof token.userId !== "string" || !token.userId || typeof token.token !== "string" || !token.token || typeof token.updatedAt !== "string" || !token.updatedAt) return null;
3474
- return {
3475
- connectionName: token.connectionName,
3476
- userId: token.userId,
3477
- token: token.token,
3478
- ...typeof token.expiresAt === "string" ? { expiresAt: token.expiresAt } : {},
3479
- updatedAt: token.updatedAt
3480
- };
3481
- }
3482
- function isSsoStoreData(value) {
3483
- if (!value || typeof value !== "object") return false;
3484
- const obj = value;
3485
- return obj.version === 1 && typeof obj.tokens === "object" && obj.tokens !== null;
3486
- }
3487
- function createMSTeamsSsoTokenStoreFs(params) {
3488
- const legacyFilePath = resolveMSTeamsStorePath({
3489
- filename: STORE_FILENAME,
3490
- env: params?.env,
3491
- homedir: params?.homedir,
3492
- stateDir: params?.stateDir,
3493
- storePath: params?.storePath
3494
- });
3495
- const empty = {
3496
- version: 1,
3497
- tokens: {}
3498
- };
3499
- const tokenStore = createTokenStore(params);
3500
- const migrationStore = createMigrationStore(params);
3501
- const migrationKey = buildMigrationKey(legacyFilePath);
3502
- let legacyImportPromise = null;
3503
- const importLegacyStore = async () => {
3504
- const imported = await migrationStore.lookup(migrationKey) !== void 0;
3505
- const { value, exists } = await readJsonFile(legacyFilePath, empty);
3506
- const contentKey = exists ? buildMigrationContentKey(legacyFilePath, value) : null;
3507
- if (contentKey && await migrationStore.lookup(contentKey)) return;
3508
- if (exists && isSsoStoreData(value)) for (const stored of Object.values(value.tokens)) {
3509
- const normalized = normalizeStoredToken(stored);
3510
- if (!normalized) continue;
3511
- await tokenStore.registerIfAbsent(makeKey(normalized.connectionName, normalized.userId), toPluginJsonValue(normalized));
3512
- }
3513
- if (contentKey) await migrationStore.register(contentKey, { importedAt: (/* @__PURE__ */ new Date()).toISOString() });
3514
- if (!imported) await migrationStore.register(migrationKey, { importedAt: (/* @__PURE__ */ new Date()).toISOString() });
3515
- if (exists) await fs.rm(legacyFilePath, { force: true }).catch(() => {});
3516
- };
3517
- const ensureLegacyImported = async () => {
3518
- if (!legacyImportPromise) legacyImportPromise = withMSTeamsSqliteMutationLock(params, SSO_TOKEN_LOCK_FILENAME, () => importLegacyStore()).finally(() => {
3519
- legacyImportPromise = null;
3520
- });
3521
- await legacyImportPromise;
3522
- };
3523
- return {
3524
- async get({ connectionName, userId }) {
3525
- await ensureLegacyImported();
3526
- return await tokenStore.lookup(makeKey(connectionName, userId)) ?? null;
3527
- },
3528
- async save(token) {
3529
- await withMSTeamsSqliteMutationLock(params, SSO_TOKEN_LOCK_FILENAME, async () => {
3530
- await importLegacyStore();
3531
- await tokenStore.register(makeKey(token.connectionName, token.userId), toPluginJsonValue({ ...token }));
3532
- });
3533
- },
3534
- async remove({ connectionName, userId }) {
3535
- let removed = false;
3536
- await withMSTeamsSqliteMutationLock(params, SSO_TOKEN_LOCK_FILENAME, async () => {
3537
- await importLegacyStore();
3538
- removed = await tokenStore.delete(makeKey(connectionName, userId));
3539
- });
3540
- return removed;
3541
- }
3542
- };
3543
- }
3544
- //#endregion
3545
3430
  //#region extensions/msteams/src/webhook-timeouts.ts
3546
3431
  const MSTEAMS_WEBHOOK_INACTIVITY_TIMEOUT_MS = 3e4;
3547
3432
  const MSTEAMS_WEBHOOK_REQUEST_TIMEOUT_MS = 3e4;
@@ -0,0 +1,70 @@
1
+ import { t as getMSTeamsRuntime } from "./runtime-BS5AZrKK.js";
2
+ import { C as toPluginJsonValue, S as resolveMSTeamsSqliteStateEnv, w as withMSTeamsSqliteMutationLock } from "./polls-C1VgSvKE.js";
3
+ import { createHash } from "node:crypto";
4
+ //#region extensions/msteams/src/sso-token-store.ts
5
+ /**
6
+ * SQLite-backed store for Bot Framework OAuth SSO tokens.
7
+ *
8
+ * Tokens are keyed by (connectionName, userId). `userId` should be the
9
+ * stable AAD object ID (`activity.from.aadObjectId`) when available,
10
+ * falling back to the Bot Framework `activity.from.id`.
11
+ *
12
+ * The store is intentionally minimal: it persists the exchanged user
13
+ * token plus its expiration so consumers (for example tool handlers
14
+ * that call Microsoft Graph with delegated permissions) can fetch a
15
+ * valid token without reaching back into Bot Framework every turn.
16
+ */
17
+ const MSTEAMS_SSO_TOKENS_LEGACY_FILENAME = "msteams-sso-tokens.json";
18
+ const MSTEAMS_SSO_TOKENS_NAMESPACE = "sso-tokens";
19
+ const SSO_TOKEN_LOCK_FILENAME = "msteams-sso-tokens.sqlite.lock";
20
+ const MSTEAMS_MAX_SSO_TOKENS = 5e3;
21
+ const STORE_KEY_VERSION_PREFIX = "v2:";
22
+ function makeMSTeamsSsoTokenStoreKey(connectionName, userId) {
23
+ return `${STORE_KEY_VERSION_PREFIX}${createHash("sha256").update(JSON.stringify([connectionName, userId])).digest("hex")}`;
24
+ }
25
+ function createTokenStore(params) {
26
+ return getMSTeamsRuntime().state.openKeyedStore({
27
+ namespace: MSTEAMS_SSO_TOKENS_NAMESPACE,
28
+ maxEntries: MSTEAMS_MAX_SSO_TOKENS,
29
+ env: resolveMSTeamsSqliteStateEnv(params)
30
+ });
31
+ }
32
+ function normalizeMSTeamsSsoStoredToken(value) {
33
+ if (!value || typeof value !== "object") return null;
34
+ const token = value;
35
+ if (typeof token.connectionName !== "string" || !token.connectionName || typeof token.userId !== "string" || !token.userId || typeof token.token !== "string" || !token.token || typeof token.updatedAt !== "string" || !token.updatedAt) return null;
36
+ return {
37
+ connectionName: token.connectionName,
38
+ userId: token.userId,
39
+ token: token.token,
40
+ ...typeof token.expiresAt === "string" ? { expiresAt: token.expiresAt } : {},
41
+ updatedAt: token.updatedAt
42
+ };
43
+ }
44
+ function isMSTeamsSsoStoreData(value) {
45
+ if (!value || typeof value !== "object") return false;
46
+ const obj = value;
47
+ return obj.version === 1 && typeof obj.tokens === "object" && obj.tokens !== null;
48
+ }
49
+ function createMSTeamsSsoTokenStoreFs(params) {
50
+ const tokenStore = createTokenStore(params);
51
+ return {
52
+ async get({ connectionName, userId }) {
53
+ return await tokenStore.lookup(makeMSTeamsSsoTokenStoreKey(connectionName, userId)) ?? null;
54
+ },
55
+ async save(token) {
56
+ await withMSTeamsSqliteMutationLock(params, SSO_TOKEN_LOCK_FILENAME, async () => {
57
+ await tokenStore.register(makeMSTeamsSsoTokenStoreKey(token.connectionName, token.userId), toPluginJsonValue({ ...token }));
58
+ });
59
+ },
60
+ async remove({ connectionName, userId }) {
61
+ let removed = false;
62
+ await withMSTeamsSqliteMutationLock(params, SSO_TOKEN_LOCK_FILENAME, async () => {
63
+ removed = await tokenStore.delete(makeMSTeamsSsoTokenStoreKey(connectionName, userId));
64
+ });
65
+ return removed;
66
+ }
67
+ };
68
+ }
69
+ //#endregion
70
+ export { isMSTeamsSsoStoreData as a, createMSTeamsSsoTokenStoreFs as i, MSTEAMS_SSO_TOKENS_LEGACY_FILENAME as n, makeMSTeamsSsoTokenStoreKey as o, MSTEAMS_SSO_TOKENS_NAMESPACE as r, normalizeMSTeamsSsoStoredToken as s, MSTEAMS_MAX_SSO_TOKENS as t };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/msteams",
3
- "version": "2026.6.1",
3
+ "version": "2026.6.5-beta.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/msteams",
9
- "version": "2026.6.1",
9
+ "version": "2026.6.5-beta.1",
10
10
  "dependencies": {
11
11
  "@azure/identity": "4.13.1",
12
12
  "@microsoft/teams.api": "2.0.12",
@@ -15,7 +15,7 @@
15
15
  "typebox": "1.1.39"
16
16
  },
17
17
  "peerDependencies": {
18
- "openclaw": ">=2026.6.1"
18
+ "openclaw": ">=2026.6.5-beta.1"
19
19
  },
20
20
  "peerDependenciesMeta": {
21
21
  "openclaw": {