@openclaw/msteams 2026.2.6 → 2026.2.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026.2.6-3
4
+
5
+ ### Changes
6
+
7
+ - Version alignment with core OpenClaw release numbers.
8
+
9
+ ## 2026.2.6-2
10
+
11
+ ### Changes
12
+
13
+ - Version alignment with core OpenClaw release numbers.
14
+
3
15
  ## 2026.2.6
4
16
 
5
17
  ### Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/msteams",
3
- "version": "2026.2.6",
3
+ "version": "2026.2.9",
4
4
  "description": "OpenClaw Microsoft Teams channel plugin",
5
5
  "type": "module",
6
6
  "dependencies": {
@@ -8,7 +8,6 @@
8
8
  "@microsoft/agents-hosting-express": "^1.2.3",
9
9
  "@microsoft/agents-hosting-extensions-teams": "^1.2.3",
10
10
  "express": "^5.2.1",
11
- "openclaw": "workspace:*",
12
11
  "proper-lockfile": "^4.1.2"
13
12
  },
14
13
  "devDependencies": {
package/src/channel.ts CHANGED
@@ -42,6 +42,7 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
42
42
  id: "msteams",
43
43
  meta: {
44
44
  ...meta,
45
+ aliases: [...meta.aliases],
45
46
  },
46
47
  onboarding: msteamsOnboardingAdapter,
47
48
  pairing: {
@@ -384,7 +385,8 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
384
385
  if (!to) {
385
386
  return {
386
387
  isError: true,
387
- content: [{ type: "text", text: "Card send requires a target (to)." }],
388
+ content: [{ type: "text" as const, text: "Card send requires a target (to)." }],
389
+ details: { error: "Card send requires a target (to)." },
388
390
  };
389
391
  }
390
392
  const result = await sendAdaptiveCardMSTeams({
@@ -395,7 +397,7 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
395
397
  return {
396
398
  content: [
397
399
  {
398
- type: "text",
400
+ type: "text" as const,
399
401
  text: JSON.stringify({
400
402
  ok: true,
401
403
  channel: "msteams",
@@ -404,6 +406,7 @@ export const msteamsPlugin: ChannelPlugin<ResolvedMSTeamsAccount> = {
404
406
  }),
405
407
  },
406
408
  ],
409
+ details: { ok: true, channel: "msteams", messageId: result.messageId },
407
410
  };
408
411
  }
409
412
  // Return null to fall through to default handler
@@ -1,4 +1,4 @@
1
- import type { ChannelDirectoryEntry } from "openclaw/plugin-sdk";
1
+ import type { ChannelDirectoryEntry, MSTeamsConfig } from "openclaw/plugin-sdk";
2
2
  import { GRAPH_ROOT } from "./attachments/shared.js";
3
3
  import { loadMSTeamsSdkWithAuth } from "./sdk.js";
4
4
  import { resolveMSTeamsCredentials } from "./token.js";
@@ -62,7 +62,7 @@ async function fetchGraphJson<T>(params: {
62
62
 
63
63
  async function resolveGraphToken(cfg: unknown): Promise<string> {
64
64
  const creds = resolveMSTeamsCredentials(
65
- (cfg as { channels?: { msteams?: unknown } })?.channels?.msteams,
65
+ (cfg as { channels?: { msteams?: unknown } })?.channels?.msteams as MSTeamsConfig | undefined,
66
66
  );
67
67
  if (!creds) {
68
68
  throw new Error("MS Teams credentials missing");
package/src/messenger.ts CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  type MSTeamsReplyStyle,
7
7
  type ReplyPayload,
8
8
  SILENT_REPLY_TOKEN,
9
+ sleep,
9
10
  } from "openclaw/plugin-sdk";
10
11
  import type { MSTeamsAccessTokenProvider } from "./attachments/types.js";
11
12
  import type { StoredConversationReference } from "./conversation-store.js";
@@ -166,16 +167,6 @@ function clampMs(value: number, maxMs: number): number {
166
167
  return Math.min(value, maxMs);
167
168
  }
168
169
 
169
- async function sleep(ms: number): Promise<void> {
170
- const delay = Math.max(0, ms);
171
- if (delay === 0) {
172
- return;
173
- }
174
- await new Promise<void>((resolve) => {
175
- setTimeout(resolve, delay);
176
- });
177
- }
178
-
179
170
  function resolveRetryOptions(
180
171
  retry: false | MSTeamsSendRetryOptions | undefined,
181
172
  ): Required<MSTeamsSendRetryOptions> & { enabled: boolean } {
@@ -10,7 +10,7 @@ import {
10
10
  } from "../attachments.js";
11
11
 
12
12
  type MSTeamsLogger = {
13
- debug: (message: string, meta?: Record<string, unknown>) => void;
13
+ debug?: (message: string, meta?: Record<string, unknown>) => void;
14
14
  };
15
15
 
16
16
  export async function resolveMSTeamsInboundMedia(params: {
@@ -66,7 +66,7 @@ export async function resolveMSTeamsInboundMedia(params: {
66
66
  channelData: activity.channelData,
67
67
  });
68
68
  if (messageUrls.length === 0) {
69
- log.debug("graph message url unavailable", {
69
+ log.debug?.("graph message url unavailable", {
70
70
  conversationType,
71
71
  hasChannelData: Boolean(activity.channelData),
72
72
  messageId: activity.id ?? undefined,
@@ -107,16 +107,16 @@ export async function resolveMSTeamsInboundMedia(params: {
107
107
  }
108
108
  }
109
109
  if (mediaList.length === 0) {
110
- log.debug("graph media fetch empty", { attempts });
110
+ log.debug?.("graph media fetch empty", { attempts });
111
111
  }
112
112
  }
113
113
  }
114
114
  }
115
115
 
116
116
  if (mediaList.length > 0) {
117
- log.debug("downloaded attachments", { count: mediaList.length });
117
+ log.debug?.("downloaded attachments", { count: mediaList.length });
118
118
  } else if (htmlSummary?.imgTags) {
119
- log.debug("inline images detected but none downloaded", {
119
+ log.debug?.("inline images detected but none downloaded", {
120
120
  imgTags: htmlSummary.imgTags,
121
121
  srcHosts: htmlSummary.srcHosts,
122
122
  dataImages: htmlSummary.dataImages,
@@ -54,7 +54,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
54
54
  const core = getMSTeamsRuntime();
55
55
  const logVerboseMessage = (message: string) => {
56
56
  if (core.logging.shouldLogVerbose()) {
57
- log.debug(message);
57
+ log.debug?.(message);
58
58
  }
59
59
  };
60
60
  const msteamsCfg = cfg.channels?.msteams;
@@ -105,11 +105,11 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
105
105
  conversation: conversation?.id,
106
106
  });
107
107
  if (htmlSummary) {
108
- log.debug("html attachment summary", htmlSummary);
108
+ log.debug?.("html attachment summary", htmlSummary);
109
109
  }
110
110
 
111
111
  if (!from?.id) {
112
- log.debug("skipping message without from.id");
112
+ log.debug?.("skipping message without from.id");
113
113
  return;
114
114
  }
115
115
 
@@ -137,7 +137,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
137
137
  const allowFrom = dmAllowFrom;
138
138
 
139
139
  if (dmPolicy === "disabled") {
140
- log.debug("dropping dm (dms disabled)");
140
+ log.debug?.("dropping dm (dms disabled)");
141
141
  return;
142
142
  }
143
143
 
@@ -163,7 +163,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
163
163
  });
164
164
  }
165
165
  }
166
- log.debug("dropping dm (not allowlisted)", {
166
+ log.debug?.("dropping dm (not allowlisted)", {
167
167
  sender: senderId,
168
168
  label: senderName,
169
169
  allowlistMatch: formatAllowlistMatchMeta(allowMatch),
@@ -200,7 +200,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
200
200
 
201
201
  if (!isDirectMessage && msteamsCfg) {
202
202
  if (groupPolicy === "disabled") {
203
- log.debug("dropping group message (groupPolicy: disabled)", {
203
+ log.debug?.("dropping group message (groupPolicy: disabled)", {
204
204
  conversationId,
205
205
  });
206
206
  return;
@@ -208,7 +208,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
208
208
 
209
209
  if (groupPolicy === "allowlist") {
210
210
  if (channelGate.allowlistConfigured && !channelGate.allowed) {
211
- log.debug("dropping group message (not in team/channel allowlist)", {
211
+ log.debug?.("dropping group message (not in team/channel allowlist)", {
212
212
  conversationId,
213
213
  teamKey: channelGate.teamKey ?? "none",
214
214
  channelKey: channelGate.channelKey ?? "none",
@@ -218,20 +218,19 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
218
218
  return;
219
219
  }
220
220
  if (effectiveGroupAllowFrom.length === 0 && !channelGate.allowlistConfigured) {
221
- log.debug("dropping group message (groupPolicy: allowlist, no allowlist)", {
221
+ log.debug?.("dropping group message (groupPolicy: allowlist, no allowlist)", {
222
222
  conversationId,
223
223
  });
224
224
  return;
225
225
  }
226
226
  if (effectiveGroupAllowFrom.length > 0) {
227
227
  const allowMatch = resolveMSTeamsAllowlistMatch({
228
- groupPolicy,
229
228
  allowFrom: effectiveGroupAllowFrom,
230
229
  senderId,
231
230
  senderName,
232
231
  });
233
232
  if (!allowMatch.allowed) {
234
- log.debug("dropping group message (not in groupAllowFrom)", {
233
+ log.debug?.("dropping group message (not in groupAllowFrom)", {
235
234
  sender: senderId,
236
235
  label: senderName,
237
236
  allowlistMatch: formatAllowlistMatchMeta(allowMatch),
@@ -293,7 +292,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
293
292
  locale: activity.locale,
294
293
  };
295
294
  conversationStore.upsert(conversationId, conversationRef).catch((err) => {
296
- log.debug("failed to save conversation reference", {
295
+ log.debug?.("failed to save conversation reference", {
297
296
  error: formatUnknownError(err),
298
297
  });
299
298
  });
@@ -307,7 +306,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
307
306
  selections: pollVote.selections,
308
307
  });
309
308
  if (!poll) {
310
- log.debug("poll vote ignored (poll not found)", {
309
+ log.debug?.("poll vote ignored (poll not found)", {
311
310
  pollId: pollVote.pollId,
312
311
  });
313
312
  } else {
@@ -327,7 +326,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
327
326
  }
328
327
 
329
328
  if (!rawBody) {
330
- log.debug("skipping empty message after stripping mentions");
329
+ log.debug?.("skipping empty message after stripping mentions");
331
330
  return;
332
331
  }
333
332
 
@@ -342,7 +341,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
342
341
  cfg,
343
342
  channel: "msteams",
344
343
  peer: {
345
- kind: isDirectMessage ? "dm" : isChannel ? "channel" : "group",
344
+ kind: isDirectMessage ? "direct" : isChannel ? "channel" : "group",
346
345
  id: isDirectMessage ? senderId : conversationId,
347
346
  },
348
347
  });
@@ -377,7 +376,7 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
377
376
  });
378
377
  const mentioned = mentionGate.effectiveWasMentioned;
379
378
  if (requireMention && mentionGate.shouldSkip) {
380
- log.debug("skipping message (mention required)", {
379
+ log.debug?.("skipping message (mention required)", {
381
380
  teamId,
382
381
  channelId,
383
382
  requireMention,
@@ -413,7 +412,8 @@ export function createMSTeamsMessageHandler(deps: MSTeamsMessageHandlerDeps) {
413
412
  channelData: activity.channelData,
414
413
  },
415
414
  log,
416
- preserveFilenames: cfg.media?.preserveFilenames,
415
+ preserveFilenames: (cfg as { media?: { preserveFilenames?: boolean } }).media
416
+ ?.preserveFilenames,
417
417
  });
418
418
 
419
419
  const mediaPayload = buildMSTeamsMediaPayload(mediaList);
@@ -49,7 +49,7 @@ async function handleFileConsentInvoke(
49
49
 
50
50
  const consentResponse = parseFileConsentInvoke(activity);
51
51
  if (!consentResponse) {
52
- log.debug("invalid file consent invoke", { value: activity.value });
52
+ log.debug?.("invalid file consent invoke", { value: activity.value });
53
53
  return false;
54
54
  }
55
55
 
@@ -61,7 +61,7 @@ async function handleFileConsentInvoke(
61
61
  if (consentResponse.action === "accept" && consentResponse.uploadInfo) {
62
62
  const pendingFile = getPendingUpload(uploadId);
63
63
  if (pendingFile) {
64
- log.debug("user accepted file consent, uploading", {
64
+ log.debug?.("user accepted file consent, uploading", {
65
65
  uploadId,
66
66
  filename: pendingFile.filename,
67
67
  size: pendingFile.buffer.length,
@@ -94,20 +94,20 @@ async function handleFileConsentInvoke(
94
94
  uniqueId: consentResponse.uploadInfo.uniqueId,
95
95
  });
96
96
  } catch (err) {
97
- log.debug("file upload failed", { uploadId, error: String(err) });
97
+ log.debug?.("file upload failed", { uploadId, error: String(err) });
98
98
  await context.sendActivity(`File upload failed: ${String(err)}`);
99
99
  } finally {
100
100
  removePendingUpload(uploadId);
101
101
  }
102
102
  } else {
103
- log.debug("pending file not found for consent", { uploadId });
103
+ log.debug?.("pending file not found for consent", { uploadId });
104
104
  await context.sendActivity(
105
105
  "The file upload request has expired. Please try sending the file again.",
106
106
  );
107
107
  }
108
108
  } else {
109
109
  // User declined
110
- log.debug("user declined file consent", { uploadId });
110
+ log.debug?.("user declined file consent", { uploadId });
111
111
  removePendingUpload(uploadId);
112
112
  }
113
113
 
@@ -151,7 +151,7 @@ export function registerMSTeamsHandlers<T extends MSTeamsActivityHandler>(
151
151
  const membersAdded = (context as MSTeamsTurnContext).activity?.membersAdded ?? [];
152
152
  for (const member of membersAdded) {
153
153
  if (member.id !== (context as MSTeamsTurnContext).activity?.recipient?.id) {
154
- deps.log.debug("member added", { member: member.id });
154
+ deps.log.debug?.("member added", { member: member.id });
155
155
  // Don't send welcome message - let the user initiate conversation.
156
156
  }
157
157
  }
@@ -1,5 +1,5 @@
1
1
  export type MSTeamsMonitorLogger = {
2
- debug: (message: string, meta?: Record<string, unknown>) => void;
2
+ debug?: (message: string, meta?: Record<string, unknown>) => void;
3
3
  info: (message: string, meta?: Record<string, unknown>) => void;
4
4
  error: (message: string, meta?: Record<string, unknown>) => void;
5
5
  };
package/src/monitor.ts CHANGED
@@ -9,7 +9,7 @@ import type { MSTeamsConversationStore } from "./conversation-store.js";
9
9
  import type { MSTeamsAdapter } from "./messenger.js";
10
10
  import { createMSTeamsConversationStoreFs } from "./conversation-store-fs.js";
11
11
  import { formatUnknownError } from "./errors.js";
12
- import { registerMSTeamsHandlers } from "./monitor-handler.js";
12
+ import { registerMSTeamsHandlers, type MSTeamsActivityHandler } from "./monitor-handler.js";
13
13
  import { createMSTeamsPollStoreFs, type MSTeamsPollStore } from "./polls.js";
14
14
  import {
15
15
  resolveMSTeamsChannelAllowlist,
@@ -40,7 +40,7 @@ export async function monitorMSTeamsProvider(
40
40
  let cfg = opts.cfg;
41
41
  let msteamsCfg = cfg.channels?.msteams;
42
42
  if (!msteamsCfg?.enabled) {
43
- log.debug("msteams provider disabled");
43
+ log.debug?.("msteams provider disabled");
44
44
  return { app: null, shutdown: async () => {} };
45
45
  }
46
46
 
@@ -224,7 +224,7 @@ export async function monitorMSTeamsProvider(
224
224
  const tokenProvider = new MsalTokenProvider(authConfig);
225
225
  const adapter = createMSTeamsAdapter(authConfig, sdk);
226
226
 
227
- const handler = registerMSTeamsHandlers(new ActivityHandler(), {
227
+ const handler = registerMSTeamsHandlers(new ActivityHandler() as MSTeamsActivityHandler, {
228
228
  cfg,
229
229
  runtime,
230
230
  appId,
@@ -246,7 +246,7 @@ export async function monitorMSTeamsProvider(
246
246
  const configuredPath = msteamsCfg.webhook?.path ?? "/api/messages";
247
247
  const messageHandler = (req: Request, res: Response) => {
248
248
  void adapter
249
- .process(req, res, (context: unknown) => handler.run(context))
249
+ .process(req, res, (context: unknown) => handler.run!(context))
250
250
  .catch((err: unknown) => {
251
251
  log.error("msteams webhook failed", { error: formatUnknownError(err) });
252
252
  });
@@ -258,7 +258,7 @@ export async function monitorMSTeamsProvider(
258
258
  expressApp.post("/api/messages", messageHandler);
259
259
  }
260
260
 
261
- log.debug("listening on paths", {
261
+ log.debug?.("listening on paths", {
262
262
  primary: configuredPath,
263
263
  fallback: "/api/messages",
264
264
  });
@@ -277,7 +277,7 @@ export async function monitorMSTeamsProvider(
277
277
  return new Promise<void>((resolve) => {
278
278
  httpServer.close((err) => {
279
279
  if (err) {
280
- log.debug("msteams server close error", { error: String(err) });
280
+ log.debug?.("msteams server close error", { error: String(err) });
281
281
  }
282
282
  resolve();
283
283
  });
package/src/onboarding.ts CHANGED
@@ -4,6 +4,7 @@ import type {
4
4
  OpenClawConfig,
5
5
  DmPolicy,
6
6
  WizardPrompter,
7
+ MSTeamsTeamConfig,
7
8
  } from "openclaw/plugin-sdk";
8
9
  import {
9
10
  addWildcardAllowFrom,
@@ -184,7 +185,7 @@ function setMSTeamsTeamsAllowlist(
184
185
  msteams: {
185
186
  ...cfg.channels?.msteams,
186
187
  enabled: true,
187
- teams,
188
+ teams: teams as Record<string, MSTeamsTeamConfig>,
188
189
  },
189
190
  },
190
191
  };
@@ -49,7 +49,7 @@ export function createMSTeamsReplyDispatcher(params: {
49
49
  start: sendTypingIndicator,
50
50
  onStartError: (err) => {
51
51
  logTypingFailure({
52
- log: (message) => params.log.debug(message),
52
+ log: (message) => params.log.debug?.(message),
53
53
  channel: "msteams",
54
54
  action: "start",
55
55
  error: err,
@@ -94,7 +94,7 @@ export function createMSTeamsReplyDispatcher(params: {
94
94
  // Enable default retry/backoff for throttling/transient failures.
95
95
  retry: {},
96
96
  onRetry: (event) => {
97
- params.log.debug("retrying send", {
97
+ params.log.debug?.("retrying send", {
98
98
  replyStyle: params.replyStyle,
99
99
  ...event,
100
100
  });
@@ -1,3 +1,4 @@
1
+ import type { MSTeamsConfig } from "openclaw/plugin-sdk";
1
2
  import { GRAPH_ROOT } from "./attachments/shared.js";
2
3
  import { loadMSTeamsSdkWithAuth } from "./sdk.js";
3
4
  import { resolveMSTeamsCredentials } from "./token.js";
@@ -155,7 +156,7 @@ async function fetchGraphJson<T>(params: {
155
156
 
156
157
  async function resolveGraphToken(cfg: unknown): Promise<string> {
157
158
  const creds = resolveMSTeamsCredentials(
158
- (cfg as { channels?: { msteams?: unknown } })?.channels?.msteams,
159
+ (cfg as { channels?: { msteams?: unknown } })?.channels?.msteams as MSTeamsConfig | undefined,
159
160
  );
160
161
  if (!creds) {
161
162
  throw new Error("MS Teams credentials missing");
package/src/send.ts CHANGED
@@ -111,7 +111,7 @@ export async function sendMessageMSTeams(
111
111
  sharePointSiteId,
112
112
  } = ctx;
113
113
 
114
- log.debug("sending proactive message", {
114
+ log.debug?.("sending proactive message", {
115
115
  conversationId,
116
116
  conversationType,
117
117
  textLength: messageText.length,
@@ -131,7 +131,7 @@ export async function sendMessageMSTeams(
131
131
  const fallbackFileName = await extractFilename(mediaUrl);
132
132
  const fileName = media.fileName ?? fallbackFileName;
133
133
 
134
- log.debug("processing media", {
134
+ log.debug?.("processing media", {
135
135
  fileName,
136
136
  contentType: media.contentType,
137
137
  size: media.buffer.length,
@@ -155,7 +155,7 @@ export async function sendMessageMSTeams(
155
155
  description: messageText || undefined,
156
156
  });
157
157
 
158
- log.debug("sending file consent card", { uploadId, fileName, size: media.buffer.length });
158
+ log.debug?.("sending file consent card", { uploadId, fileName, size: media.buffer.length });
159
159
 
160
160
  const baseRef = buildConversationReference(ref);
161
161
  const proactiveRef = { ...baseRef, activityId: undefined };
@@ -205,7 +205,7 @@ export async function sendMessageMSTeams(
205
205
  try {
206
206
  if (sharePointSiteId) {
207
207
  // Use SharePoint upload + Graph API for native file card
208
- log.debug("uploading to SharePoint for native file card", {
208
+ log.debug?.("uploading to SharePoint for native file card", {
209
209
  fileName,
210
210
  conversationType,
211
211
  siteId: sharePointSiteId,
@@ -221,7 +221,7 @@ export async function sendMessageMSTeams(
221
221
  usePerUserSharing: conversationType === "groupChat",
222
222
  });
223
223
 
224
- log.debug("SharePoint upload complete", {
224
+ log.debug?.("SharePoint upload complete", {
225
225
  itemId: uploaded.itemId,
226
226
  shareUrl: uploaded.shareUrl,
227
227
  });
@@ -233,7 +233,7 @@ export async function sendMessageMSTeams(
233
233
  tokenProvider,
234
234
  });
235
235
 
236
- log.debug("driveItem properties retrieved", {
236
+ log.debug?.("driveItem properties retrieved", {
237
237
  eTag: driveItem.eTag,
238
238
  webDavUrl: driveItem.webDavUrl,
239
239
  });
@@ -265,7 +265,7 @@ export async function sendMessageMSTeams(
265
265
  }
266
266
 
267
267
  // Fallback: no SharePoint site configured, use OneDrive with markdown link
268
- log.debug("uploading to OneDrive (no SharePoint site configured)", {
268
+ log.debug?.("uploading to OneDrive (no SharePoint site configured)", {
269
269
  fileName,
270
270
  conversationType,
271
271
  });
@@ -277,7 +277,7 @@ export async function sendMessageMSTeams(
277
277
  tokenProvider,
278
278
  });
279
279
 
280
- log.debug("OneDrive upload complete", {
280
+ log.debug?.("OneDrive upload complete", {
281
281
  itemId: uploaded.itemId,
282
282
  shareUrl: uploaded.shareUrl,
283
283
  });
@@ -349,7 +349,7 @@ async function sendTextWithMedia(
349
349
  messages: [{ text: text || undefined, mediaUrl }],
350
350
  retry: {},
351
351
  onRetry: (event) => {
352
- log.debug("retrying send", { conversationId, ...event });
352
+ log.debug?.("retrying send", { conversationId, ...event });
353
353
  },
354
354
  tokenProvider,
355
355
  sharePointSiteId,
@@ -392,7 +392,7 @@ export async function sendPollMSTeams(
392
392
  maxSelections,
393
393
  });
394
394
 
395
- log.debug("sending poll", {
395
+ log.debug?.("sending poll", {
396
396
  conversationId,
397
397
  pollId: pollCard.pollId,
398
398
  optionCount: pollCard.options.length,
@@ -452,7 +452,7 @@ export async function sendAdaptiveCardMSTeams(
452
452
  to,
453
453
  });
454
454
 
455
- log.debug("sending adaptive card", {
455
+ log.debug?.("sending adaptive card", {
456
456
  conversationId,
457
457
  cardType: card.type,
458
458
  cardVersion: card.version,
package/src/store-fs.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import crypto from "node:crypto";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
+ import { safeParseJson } from "openclaw/plugin-sdk";
4
5
  import lockfile from "proper-lockfile";
5
6
 
6
7
  const STORE_LOCK_OPTIONS = {
@@ -14,14 +15,6 @@ const STORE_LOCK_OPTIONS = {
14
15
  stale: 30_000,
15
16
  } as const;
16
17
 
17
- function safeParseJson<T>(raw: string): T | null {
18
- try {
19
- return JSON.parse(raw) as T;
20
- } catch {
21
- return null;
22
- }
23
- }
24
-
25
18
  export async function readJsonFile<T>(
26
19
  filePath: string,
27
20
  fallback: T,