@openclaw/msteams 2026.8.1-beta.3 → 2026.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/{adapter.runtime-8g-MOPfb.cjs → adapter.runtime-DDuc7sKJ.cjs} +1 -1
  2. package/dist/api.cjs +2 -2
  3. package/dist/approval-card-BEblaIto.cjs +303 -0
  4. package/dist/approval-handler.runtime-C0AWNWXk.cjs +107 -0
  5. package/dist/{channel-KpWENcgK.cjs → channel-DLn4b6Qi.cjs} +168 -53
  6. package/dist/channel-config-api.cjs +1 -1
  7. package/dist/channel-plugin-api.cjs +1 -1
  8. package/dist/{channel.runtime-Bkzj8jZ0.cjs → channel.runtime-1mH99ZAm.cjs} +49 -608
  9. package/dist/{channel.setup-mOFtsfYz.cjs → channel.setup-BRbhaigB.cjs} +29 -18
  10. package/dist/{config-schema-C4CszdDi.cjs → config-schema-MyN2lamA.cjs} +12 -11
  11. package/dist/configured-state.cjs +20 -0
  12. package/dist/directory-contract-api.cjs +1 -1
  13. package/dist/doctor-contract-api.cjs +79 -42
  14. package/dist/{messenger-IISPSOvj.cjs → messenger-flkiAkkB.cjs} +29 -171
  15. package/dist/{oauth-C9So7uld.cjs → oauth-Bctn-WPI.cjs} +4 -3
  16. package/dist/{polls-DvuJvaDG.cjs → polls-P86qBpeV.cjs} +5 -4
  17. package/dist/qa-runner-api.cjs +1 -1
  18. package/dist/reaction-types-CL79V_5D.cjs +150 -0
  19. package/dist/{resolve-allowlist-CIfWUwuc.cjs → resolve-allowlist-BrdWKHE-.cjs} +26 -51
  20. package/dist/{rolldown-runtime-D6vf50IK.cjs → rolldown-runtime-VH7oDXx4.cjs} +1 -1
  21. package/dist/runtime-api.cjs +2 -1
  22. package/dist/secret-input-C65t6kjM.cjs +1 -0
  23. package/dist/send-C4BjzS1F.cjs +631 -0
  24. package/dist/setup-plugin-api.cjs +1 -1
  25. package/dist/{src-C127-DcQ.cjs → src-RSjkJRjI.cjs} +237 -99
  26. package/dist/{sso-token-store-ChhO1f4i.cjs → sso-token-store-CNbRhz-A.cjs} +1 -1
  27. package/dist/thread-session-DxOri69r.cjs +63 -0
  28. package/package.json +13 -11
@@ -1,12 +1,13 @@
1
- const require_resolve_allowlist = require("./resolve-allowlist-CIfWUwuc.cjs");
2
- const require_config_schema = require("./config-schema-C4CszdDi.cjs");
1
+ const require_resolve_allowlist = require("./resolve-allowlist-BrdWKHE-.cjs");
2
+ require("./secret-input-C65t6kjM.cjs");
3
+ const require_config_schema = require("./config-schema-MyN2lamA.cjs");
3
4
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
4
5
  let openclaw_plugin_sdk_allow_from = require("openclaw/plugin-sdk/allow-from");
5
6
  let openclaw_plugin_sdk_account_id = require("openclaw/plugin-sdk/account-id");
7
+ let openclaw_plugin_sdk_account_helpers = require("openclaw/plugin-sdk/account-helpers");
6
8
  let openclaw_plugin_sdk_number_runtime = require("openclaw/plugin-sdk/number-runtime");
7
9
  let openclaw_plugin_sdk_channel_config_helpers = require("openclaw/plugin-sdk/channel-config-helpers");
8
10
  let openclaw_plugin_sdk_secret_file_runtime = require("openclaw/plugin-sdk/secret-file-runtime");
9
- let openclaw_plugin_sdk_account_helpers = require("openclaw/plugin-sdk/account-helpers");
10
11
  let openclaw_plugin_sdk_channel_setup = require("openclaw/plugin-sdk/channel-setup");
11
12
  let openclaw_plugin_sdk_setup = require("openclaw/plugin-sdk/setup");
12
13
  let openclaw_plugin_sdk_setup_tools = require("openclaw/plugin-sdk/setup-tools");
@@ -106,16 +107,17 @@ function createMSTeamsSetupWizardBase() {
106
107
  let appPassword = null;
107
108
  let tenantId = null;
108
109
  if (!resolved && !hasConfigCreds) await noteMSTeamsCredentialHelp(prompter);
109
- if (canUseEnv || hasConfigCreds) if (await prompter.confirm({
110
- message: t$1(canUseEnv ? "wizard.msteams.envPrompt" : "wizard.msteams.credentialsKeep"),
111
- initialValue: true
112
- })) next = msteamsSetupAdapter.applyAccountConfig({
113
- cfg: next,
114
- accountId: openclaw_plugin_sdk_setup.DEFAULT_ACCOUNT_ID,
115
- input: {}
116
- });
117
- else ({appId, appPassword, tenantId} = await promptMSTeamsCredentials(prompter));
118
- else ({appId, appPassword, tenantId} = await promptMSTeamsCredentials(prompter));
110
+ if (canUseEnv || hasConfigCreds) {
111
+ if (await prompter.confirm({
112
+ message: t$1(canUseEnv ? "wizard.msteams.envPrompt" : "wizard.msteams.credentialsKeep"),
113
+ initialValue: true
114
+ })) next = msteamsSetupAdapter.applyAccountConfig({
115
+ cfg: next,
116
+ accountId: openclaw_plugin_sdk_setup.DEFAULT_ACCOUNT_ID,
117
+ input: {}
118
+ });
119
+ else ({appId, appPassword, tenantId} = await promptMSTeamsCredentials(prompter));
120
+ } else ({appId, appPassword, tenantId} = await promptMSTeamsCredentials(prompter));
119
121
  if (appId && appPassword && tenantId) next = {
120
122
  ...next,
121
123
  channels: {
@@ -189,7 +191,8 @@ function extractErrorCode(err) {
189
191
  }
190
192
  function extractRetryAfterMs(err) {
191
193
  if (!(0, openclaw_plugin_sdk_string_coerce_runtime.isRecord)(err)) return null;
192
- const directMs = (0, openclaw_plugin_sdk_number_runtime.asFiniteNumberInRange)(err.retryAfterMs ?? err.retry_after_ms, {
194
+ const direct = err.retryAfterMs ?? err.retry_after_ms;
195
+ const directMs = (0, openclaw_plugin_sdk_number_runtime.asFiniteNumberInRange)(direct, {
193
196
  min: 0,
194
197
  max: Number.MAX_SAFE_INTEGER
195
198
  });
@@ -336,12 +339,13 @@ async function promptMSTeamsAllowFrom(params) {
336
339
  "- 00000000-0000-0000-0000-000000000000"
337
340
  ].join("\n"), t("wizard.msteams.allowlistTitle"));
338
341
  while (true) {
339
- const parts = (0, openclaw_plugin_sdk_setup.splitSetupEntries)(await params.prompter.text({
342
+ const entry = await params.prompter.text({
340
343
  message: t("wizard.msteams.allowFromPrompt"),
341
344
  placeholder: "alex@example.com, Alex Johnson",
342
345
  initialValue: existing[0] ? existing[0] : void 0,
343
346
  validate: (value) => value.trim() ? void 0 : t("common.required")
344
- }));
347
+ });
348
+ const parts = (0, openclaw_plugin_sdk_setup.splitSetupEntries)(entry);
345
349
  if (parts.length === 0) {
346
350
  await params.prompter.note(t("wizard.msteams.enterAtLeastOneUser"), t("wizard.msteams.allowlistTitle"));
347
351
  continue;
@@ -364,7 +368,8 @@ async function promptMSTeamsAllowFrom(params) {
364
368
  await params.prompter.note(t("wizard.msteams.couldNotResolve", { entries: unresolved.map((item) => item.input).join(", ") }), t("wizard.msteams.allowlistTitle"));
365
369
  continue;
366
370
  }
367
- const unique = (0, openclaw_plugin_sdk_setup.mergeAllowFromEntries)(existing, resolved.map((item) => item.id));
371
+ const ids = resolved.map((item) => item.id);
372
+ const unique = (0, openclaw_plugin_sdk_setup.mergeAllowFromEntries)(existing, ids);
368
373
  return setMSTeamsAllowFrom(params.cfg, unique);
369
374
  }
370
375
  }
@@ -478,7 +483,7 @@ const msteamsSetupWizard = {
478
483
  };
479
484
  let oauthModule;
480
485
  try {
481
- oauthModule = await Promise.resolve().then(() => require("./oauth-C9So7uld.cjs"));
486
+ oauthModule = await Promise.resolve().then(() => require("./oauth-Bctn-WPI.cjs"));
482
487
  } catch (err) {
483
488
  await noteDelegatedAuthFailure(err);
484
489
  return {
@@ -602,6 +607,12 @@ Object.defineProperty(exports, "isRevokedProxyError", {
602
607
  return isRevokedProxyError;
603
608
  }
604
609
  });
610
+ Object.defineProperty(exports, "msteamsConfigAdapter", {
611
+ enumerable: true,
612
+ get: function() {
613
+ return msteamsConfigAdapter;
614
+ }
615
+ });
605
616
  Object.defineProperty(exports, "msteamsSetupAdapter", {
606
617
  enumerable: true,
607
618
  get: function() {
@@ -80,18 +80,19 @@ function isAzureChinaBotFrameworkServiceUrl(value) {
80
80
  return false;
81
81
  }
82
82
  }
83
+ const { accountShape, rootPolicyShape } = (0, openclaw_plugin_sdk_channel_config_schema.buildChannelAccountSchemaParts)({
84
+ omit: [
85
+ "name",
86
+ "mentionPatterns",
87
+ "replyToMode"
88
+ ],
89
+ allowFrom: zod.z.array(zod.z.string()).optional(),
90
+ groupAllowFrom: zod.z.array(zod.z.string()).optional(),
91
+ streaming: openclaw_plugin_sdk_channel_config_schema.ChannelPreviewStreamingConfigSchema.optional()
92
+ });
83
93
  const MSTeamsConfigSchema = zod.z.object({
84
- ...(0, openclaw_plugin_sdk_channel_config_schema.buildCommonChannelAccountShape)({
85
- useDefaults: true,
86
- omit: [
87
- "name",
88
- "mentionPatterns",
89
- "replyToMode"
90
- ],
91
- allowFrom: zod.z.array(zod.z.string()).optional(),
92
- groupAllowFrom: zod.z.array(zod.z.string()).optional(),
93
- streaming: openclaw_plugin_sdk_channel_config_schema.ChannelPreviewStreamingConfigSchema.optional()
94
- }),
94
+ ...accountShape,
95
+ ...rootPolicyShape,
95
96
  dangerouslyAllowNameMatching: openclaw_plugin_sdk_channel_config_schema.ChannelDangerouslyAllowNameMatchingSchema,
96
97
  appId: zod.z.string().optional(),
97
98
  appPassword: (0, openclaw_plugin_sdk_secret_input.registerSensitiveConfigSchema)(SecretInputSchema.optional()),
@@ -0,0 +1,20 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ require("./secret-input-C65t6kjM.cjs");
3
+ let openclaw_plugin_sdk_secret_input = require("openclaw/plugin-sdk/secret-input");
4
+ //#region extensions/msteams/configured-state.ts
5
+ /** Mirror Teams auth-mode requirements without loading the Azure SDK or full channel. */
6
+ function hasConfiguredMSTeamsChannelState(params) {
7
+ const config = params.cfg.channels?.msteams;
8
+ if (config?.enabled === false) return false;
9
+ const env = params.env ?? process.env;
10
+ const appId = (0, openclaw_plugin_sdk_secret_input.normalizeSecretInputString)(config && Object.hasOwn(config, "appId") ? config.appId : env.MSTEAMS_APP_ID);
11
+ const tenantId = (0, openclaw_plugin_sdk_secret_input.normalizeSecretInputString)(config && Object.hasOwn(config, "tenantId") ? config.tenantId : env.MSTEAMS_TENANT_ID);
12
+ if (!appId || !tenantId) return false;
13
+ if ((config?.authType ?? env.MSTEAMS_AUTH_TYPE ?? "secret") === "federated") {
14
+ const certificatePath = (0, openclaw_plugin_sdk_secret_input.normalizeSecretInputString)(config && Object.hasOwn(config, "certificatePath") ? config.certificatePath : env.MSTEAMS_CERTIFICATE_PATH);
15
+ return Boolean(certificatePath || (config?.useManagedIdentity ?? env.MSTEAMS_USE_MANAGED_IDENTITY === "true"));
16
+ }
17
+ return config && Object.hasOwn(config, "appPassword") ? (0, openclaw_plugin_sdk_secret_input.hasConfiguredSecretInput)(config.appPassword) : Boolean((0, openclaw_plugin_sdk_secret_input.normalizeSecretInputString)(env.MSTEAMS_APP_PASSWORD));
18
+ }
19
+ //#endregion
20
+ exports.hasConfiguredMSTeamsChannelState = hasConfiguredMSTeamsChannelState;
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_resolve_allowlist = require("./resolve-allowlist-CIfWUwuc.cjs");
2
+ const require_resolve_allowlist = require("./resolve-allowlist-BrdWKHE-.cjs");
3
3
  let openclaw_plugin_sdk_directory_runtime = require("openclaw/plugin-sdk/directory-runtime");
4
4
  const msteamsDirectoryContractPlugin = {
5
5
  id: "msteams",
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_rolldown_runtime = require("./rolldown-runtime-D6vf50IK.cjs");
2
+ const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
3
3
  const require_delegated_state = require("./delegated-state-DiEz9JIa.cjs");
4
- const require_polls = require("./polls-DvuJvaDG.cjs");
5
- const require_sso_token_store = require("./sso-token-store-ChhO1f4i.cjs");
4
+ const require_polls = require("./polls-P86qBpeV.cjs");
5
+ const require_sso_token_store = require("./sso-token-store-CNbRhz-A.cjs");
6
6
  let openclaw_plugin_sdk_string_coerce_runtime = require("openclaw/plugin-sdk/string-coerce-runtime");
7
7
  let node_crypto = require("node:crypto");
8
8
  node_crypto = require_rolldown_runtime.__toESM(node_crypto, 1);
@@ -11,7 +11,7 @@ node_fs_promises = require_rolldown_runtime.__toESM(node_fs_promises, 1);
11
11
  let node_path = require("node:path");
12
12
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
13
13
  let openclaw_plugin_sdk_runtime_doctor_migrations = require("openclaw/plugin-sdk/runtime-doctor-migrations");
14
- //#region extensions/msteams/doctor-contract-api.ts
14
+ //#region extensions/msteams/config-doctor-api.ts
15
15
  const streamingAliasMigration = (0, openclaw_plugin_sdk_runtime_doctor_migrations.defineChannelAliasMigration)({
16
16
  channelId: "msteams",
17
17
  streaming: { defaultMode: "partial" }
@@ -20,6 +20,8 @@ const legacyConfigRules = streamingAliasMigration.legacyConfigRules;
20
20
  function normalizeCompatibilityConfig({ cfg }) {
21
21
  return streamingAliasMigration.normalizeChannelConfig({ cfg });
22
22
  }
23
+ //#endregion
24
+ //#region extensions/msteams/doctor-contract-api.ts
23
25
  const LEARNINGS_NAMESPACE = "feedback-learnings";
24
26
  const MAX_LEARNING_ENTRIES = 1e4;
25
27
  const MSTEAMS_PLUGIN_ID = "Microsoft Teams";
@@ -165,6 +167,33 @@ function mergeLearnings(legacy, existing) {
165
167
  }
166
168
  return merged.slice(-10);
167
169
  }
170
+ async function completeLegacyKeyedImport(params) {
171
+ const { filePath, label, imported, warnings } = params;
172
+ const changes = [];
173
+ let missing = 0;
174
+ for (const { store, requiredKeys } of params.stores) {
175
+ const retainedKeys = new Set((await store.entries()).map((entry) => entry.key));
176
+ missing += [...requiredKeys].filter((key) => !retainedKeys.has(key)).length;
177
+ }
178
+ if (missing > 0) {
179
+ warnings.push(`Incomplete ${label} migration: plugin state failed to retain every required entry (${missing} missing); left legacy source in place`);
180
+ return {
181
+ changes,
182
+ warnings
183
+ };
184
+ }
185
+ changes.push(`Migrated ${imported} ${label} ${imported === 1 ? "entry" : "entries"} -> plugin state`);
186
+ await (0, openclaw_plugin_sdk_runtime_doctor_migrations.archiveLegacyStateSource)({
187
+ filePath,
188
+ label: params.archiveLabel ?? label,
189
+ changes,
190
+ warnings
191
+ });
192
+ return {
193
+ changes,
194
+ warnings
195
+ };
196
+ }
168
197
  const stateMigrations = [
169
198
  {
170
199
  id: "msteams-conversations-json-to-plugin-state",
@@ -175,35 +204,36 @@ const stateMigrations = [
175
204
  return { preview: [`- ${MSTEAMS_PLUGIN_ID} conversations: ${Object.keys(state.conversations).length} entries -> plugin state (${require_polls.MSTEAMS_CONVERSATIONS_NAMESPACE})`] };
176
205
  },
177
206
  async migrateLegacyState(params) {
178
- const changes = [];
179
- const warnings = [];
180
207
  const filePath = resolveStateFilePath(params.stateDir, require_polls.MSTEAMS_CONVERSATIONS_LEGACY_FILENAME);
181
208
  const state = await readLegacyJsonFile(filePath, parseLegacyConversationStore);
182
209
  if (!state) return {
183
- changes,
184
- warnings
210
+ changes: [],
211
+ warnings: []
185
212
  };
186
213
  const store = params.context.openPluginStateKeyedStore({
187
214
  namespace: require_polls.MSTEAMS_CONVERSATIONS_NAMESPACE,
188
215
  maxEntries: require_polls.MSTEAMS_SQLITE_MAX_CONVERSATION_ROWS
189
216
  });
217
+ const requiredKeys = new Set((await store.entries()).map((entry) => entry.key));
190
218
  let imported = 0;
191
219
  for (const [rawConversationId, reference] of require_polls.selectRetainedMSTeamsConversations(state.conversations)) {
192
220
  const conversationId = require_polls.normalizeStoredConversationId(rawConversationId);
193
221
  if (!conversationId) continue;
194
- if (await store.registerIfAbsent(require_polls.buildMSTeamsConversationStateKey(conversationId), require_polls.prepareMSTeamsConversationReferenceForStorage(conversationId, reference))) imported++;
222
+ const key = require_polls.buildMSTeamsConversationStateKey(conversationId);
223
+ requiredKeys.add(key);
224
+ const storedReference = require_polls.prepareMSTeamsConversationReferenceForStorage(conversationId, reference);
225
+ if (await store.registerIfAbsent(key, storedReference)) imported++;
195
226
  }
196
- changes.push(`Migrated ${imported} ${MSTEAMS_PLUGIN_ID} conversation ${imported === 1 ? "entry" : "entries"} -> plugin state`);
197
- await (0, openclaw_plugin_sdk_runtime_doctor_migrations.archiveLegacyStateSource)({
227
+ return completeLegacyKeyedImport({
198
228
  filePath,
199
229
  label: `${MSTEAMS_PLUGIN_ID} conversation`,
200
- changes,
201
- warnings
230
+ imported,
231
+ warnings: [],
232
+ stores: [{
233
+ store,
234
+ requiredKeys
235
+ }]
202
236
  });
203
- return {
204
- changes,
205
- warnings
206
- };
207
237
  }
208
238
  },
209
239
  {
@@ -215,13 +245,11 @@ const stateMigrations = [
215
245
  return { preview: [`- ${MSTEAMS_PLUGIN_ID} polls: ${Object.keys(state.polls).length} entries -> plugin state (${require_polls.MSTEAMS_POLLS_NAMESPACE})`] };
216
246
  },
217
247
  async migrateLegacyState(params) {
218
- const changes = [];
219
- const warnings = [];
220
248
  const filePath = resolveStateFilePath(params.stateDir, require_polls.MSTEAMS_POLLS_LEGACY_FILENAME);
221
249
  const state = await readLegacyJsonFile(filePath, parseLegacyPollStore);
222
250
  if (!state) return {
223
- changes,
224
- warnings
251
+ changes: [],
252
+ warnings: []
225
253
  };
226
254
  const pollStore = params.context.openPluginStateKeyedStore({
227
255
  namespace: require_polls.MSTEAMS_POLLS_NAMESPACE,
@@ -231,10 +259,14 @@ const stateMigrations = [
231
259
  namespace: require_polls.MSTEAMS_POLL_VOTE_BUCKETS_NAMESPACE,
232
260
  maxEntries: require_polls.MSTEAMS_MAX_POLL_VOTE_BUCKET_ROWS
233
261
  });
262
+ const requiredPollKeys = new Set((await pollStore.entries()).map((entry) => entry.key));
263
+ const requiredVoteKeys = new Set((await voteBucketStore.entries()).map((entry) => entry.key));
234
264
  let imported = 0;
235
265
  for (const [pollId, poll] of require_polls.selectRetainedMSTeamsPolls(state.polls)) {
236
266
  const { metadata, votes } = require_polls.splitMSTeamsPoll(poll);
237
- const didImportPoll = await pollStore.registerIfAbsent(require_polls.buildMSTeamsPollStateKey(pollId), metadata);
267
+ const pollKey = require_polls.buildMSTeamsPollStateKey(pollId);
268
+ requiredPollKeys.add(pollKey);
269
+ const didImportPoll = await pollStore.registerIfAbsent(pollKey, metadata);
238
270
  const buckets = /* @__PURE__ */ new Map();
239
271
  for (const [voterId, selections] of Object.entries(votes)) {
240
272
  const bucket = require_polls.selectMSTeamsPollVoteBucket(pollId, voterId);
@@ -245,6 +277,7 @@ const stateMigrations = [
245
277
  let importedVoteBucket = false;
246
278
  for (const [bucket, bucketVotes] of buckets) {
247
279
  const key = require_polls.buildMSTeamsPollVoteBucketKey(pollId, bucket);
280
+ requiredVoteKeys.add(key);
248
281
  const existing = await voteBucketStore.lookup(key);
249
282
  await voteBucketStore.register(key, {
250
283
  pollId,
@@ -259,17 +292,19 @@ const stateMigrations = [
259
292
  }
260
293
  if (didImportPoll || importedVoteBucket) imported++;
261
294
  }
262
- changes.push(`Migrated ${imported} ${MSTEAMS_PLUGIN_ID} poll ${imported === 1 ? "entry" : "entries"} -> plugin state`);
263
- await (0, openclaw_plugin_sdk_runtime_doctor_migrations.archiveLegacyStateSource)({
295
+ return completeLegacyKeyedImport({
264
296
  filePath,
265
297
  label: `${MSTEAMS_PLUGIN_ID} poll`,
266
- changes,
267
- warnings
298
+ imported,
299
+ warnings: [],
300
+ stores: [{
301
+ store: pollStore,
302
+ requiredKeys: requiredPollKeys
303
+ }, {
304
+ store: voteBucketStore,
305
+ requiredKeys: requiredVoteKeys
306
+ }]
268
307
  });
269
- return {
270
- changes,
271
- warnings
272
- };
273
308
  }
274
309
  },
275
310
  {
@@ -281,18 +316,18 @@ const stateMigrations = [
281
316
  return { preview: [`- ${MSTEAMS_PLUGIN_ID} SSO tokens: ${Object.keys(state.tokens).length} entries -> plugin state (${require_sso_token_store.MSTEAMS_SSO_TOKENS_NAMESPACE})`] };
282
317
  },
283
318
  async migrateLegacyState(params) {
284
- const changes = [];
285
319
  const warnings = [];
286
320
  const filePath = resolveStateFilePath(params.stateDir, require_sso_token_store.MSTEAMS_SSO_TOKENS_LEGACY_FILENAME);
287
321
  const state = await readLegacyJsonFile(filePath, (value) => require_sso_token_store.isMSTeamsSsoStoreData(value) ? value : null);
288
322
  if (!state) return {
289
- changes,
323
+ changes: [],
290
324
  warnings
291
325
  };
292
326
  const store = params.context.openPluginStateKeyedStore({
293
327
  namespace: require_sso_token_store.MSTEAMS_SSO_TOKENS_NAMESPACE,
294
328
  maxEntries: require_sso_token_store.MSTEAMS_MAX_SSO_TOKENS
295
329
  });
330
+ const requiredKeys = new Set((await store.entries()).map((entry) => entry.key));
296
331
  let imported = 0;
297
332
  let skipped = 0;
298
333
  for (const token of Object.values(state.tokens)) {
@@ -301,20 +336,22 @@ const stateMigrations = [
301
336
  skipped++;
302
337
  continue;
303
338
  }
304
- if (await store.registerIfAbsent(require_sso_token_store.makeMSTeamsSsoTokenStoreKey(normalized.connectionName, normalized.userId), normalized)) imported++;
339
+ const key = require_sso_token_store.makeMSTeamsSsoTokenStoreKey(normalized.connectionName, normalized.userId);
340
+ requiredKeys.add(key);
341
+ if (await store.registerIfAbsent(key, normalized)) imported++;
305
342
  }
306
- changes.push(`Migrated ${imported} ${MSTEAMS_PLUGIN_ID} SSO token ${imported === 1 ? "entry" : "entries"} -> plugin state`);
307
343
  if (skipped > 0) warnings.push(`Skipped ${skipped} malformed ${MSTEAMS_PLUGIN_ID} SSO token ${skipped === 1 ? "entry" : "entries"} during migration`);
308
- await (0, openclaw_plugin_sdk_runtime_doctor_migrations.archiveLegacyStateSource)({
344
+ return completeLegacyKeyedImport({
309
345
  filePath,
310
- label: `${MSTEAMS_PLUGIN_ID} SSO-token`,
311
- changes,
312
- warnings
346
+ label: `${MSTEAMS_PLUGIN_ID} SSO token`,
347
+ archiveLabel: `${MSTEAMS_PLUGIN_ID} SSO-token`,
348
+ imported,
349
+ warnings,
350
+ stores: [{
351
+ store,
352
+ requiredKeys
353
+ }]
313
354
  });
314
- return {
315
- changes,
316
- warnings
317
- };
318
355
  }
319
356
  },
320
357
  {