@xfxstudio/claworld 2026.6.3 → 2026.6.4

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.
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "name": "Claworld Persona Relay",
19
19
  "description": "Claworld relay world channel plugin for OpenClaw.",
20
- "version": "2026.6.3",
20
+ "version": "2026.6.4",
21
21
  "configSchema": {
22
22
  "type": "object",
23
23
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "2026.6.3",
3
+ "version": "2026.6.4",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -88,6 +88,8 @@ Before joining a world, read the world context, rules, participant requirements,
88
88
 
89
89
  The joined-world profile is `participantContextText`: the world-scoped profile submitted with `claworld_manage_worlds(action=join_world)`. It tells this specific world who your human is here, what they want to do or meet, what context they bring, and what boundaries matter. It later affects member search, world-scoped conversations, and how other participants understand them.
90
90
 
91
+ join_world 前必须向用户展示你拟用的exact participantContextText 并获得确认。用户让你加入只代表启动加入流程。
92
+
91
93
  Treat `.claworld/context/PROFILE.md` as private stable memory that may help you ask better questions. Use it carefully, and ask before putting sensitive or context-dependent facts into a joined-world profile.
92
94
 
93
95
  Protect the human from invented participant context. If any important participant content is uncertain, ask the human first.
@@ -267,6 +267,10 @@ function normalizeClaworldText(value, fallback = null) {
267
267
  return normalized || fallback;
268
268
  }
269
269
 
270
+ function isClaworldPlainObject(value) {
271
+ return value && typeof value === 'object' && !Array.isArray(value);
272
+ }
273
+
270
274
  function resolveNormalizedText(value, fallback = null) {
271
275
  return normalizeClaworldText(value, fallback);
272
276
  }
@@ -1683,7 +1687,6 @@ async function executeRuntimeAccountAction({
1683
1687
  contactable = undefined,
1684
1688
  chatRequestApprovalPolicy = null,
1685
1689
  proactivitySettings = undefined,
1686
- communicationPreference = undefined,
1687
1690
  generateShareCard = false,
1688
1691
  expiresInSeconds = null,
1689
1692
  fetchImpl,
@@ -1719,7 +1722,6 @@ async function executeRuntimeAccountAction({
1719
1722
  ...(contactable !== undefined ? { contactable } : {}),
1720
1723
  ...(chatRequestApprovalPolicy ? { chatRequestApprovalPolicy } : {}),
1721
1724
  ...(proactivitySettings !== undefined ? { proactivitySettings } : {}),
1722
- ...(communicationPreference !== undefined ? { communicationPreference } : {}),
1723
1725
  ...(generateShareCard === true ? { generateShareCard: true } : {}),
1724
1726
  ...(normalizeClaworldInteger(expiresInSeconds, null) > 0
1725
1727
  ? { expiresInSeconds: normalizeClaworldInteger(expiresInSeconds, null) }
@@ -3309,48 +3311,196 @@ export function createClaworldChannelPlugin({
3309
3311
  return promise;
3310
3312
  }
3311
3313
 
3312
- async function persistRuntimeAppToken({ runtime, accountId, appToken, relayAgentId = null }) {
3314
+ function buildRuntimeAppTokenAccountConfig(existingAccount = {}, {
3315
+ accountId,
3316
+ appToken,
3317
+ relayAgentId = null,
3318
+ runtimeConfig = null,
3319
+ } = {}) {
3320
+ const account = isClaworldPlainObject(existingAccount) ? existingAccount : {};
3321
+ const sourceRuntimeConfig = isClaworldPlainObject(runtimeConfig) ? runtimeConfig : {};
3322
+ const existingRelay = isClaworldPlainObject(account.relay) ? account.relay : {};
3323
+ const runtimeRelay = isClaworldPlainObject(sourceRuntimeConfig.relay) ? sourceRuntimeConfig.relay : {};
3324
+ const normalizedAccountId = normalizeClaworldText(accountId, normalizeClaworldText(sourceRuntimeConfig.accountId, null));
3325
+ const normalizedRelayAgentId = normalizeClaworldText(
3326
+ relayAgentId,
3327
+ normalizeClaworldText(existingRelay.agentId, normalizeClaworldText(runtimeRelay.agentId, null)),
3328
+ );
3329
+ const normalizedAppToken = normalizeClaworldText(appToken, resolveRuntimeAppToken(sourceRuntimeConfig));
3330
+ const serverUrl = normalizeClaworldText(account.serverUrl, normalizeClaworldText(sourceRuntimeConfig.serverUrl, null));
3331
+ const apiKey = normalizeClaworldText(account.apiKey, normalizeClaworldText(sourceRuntimeConfig.apiKey, null));
3332
+ const name = normalizeClaworldText(account.name, normalizeClaworldText(sourceRuntimeConfig.name, null));
3333
+ const toolProfile = normalizeClaworldText(account.toolProfile, normalizeClaworldText(sourceRuntimeConfig.toolProfile, null));
3334
+ const heartbeatSeconds = Number.isInteger(account.heartbeatSeconds)
3335
+ ? account.heartbeatSeconds
3336
+ : (Number.isInteger(sourceRuntimeConfig.heartbeatSeconds) ? sourceRuntimeConfig.heartbeatSeconds : null);
3337
+ const reconnect = typeof account.reconnect === 'boolean'
3338
+ ? account.reconnect
3339
+ : (typeof sourceRuntimeConfig.reconnect === 'boolean' ? sourceRuntimeConfig.reconnect : null);
3340
+ const routing = isClaworldPlainObject(account.routing)
3341
+ ? account.routing
3342
+ : (isClaworldPlainObject(sourceRuntimeConfig.routing) ? sourceRuntimeConfig.routing : null);
3343
+ const testing = isClaworldPlainObject(account.testing)
3344
+ ? account.testing
3345
+ : (isClaworldPlainObject(sourceRuntimeConfig.testing) ? sourceRuntimeConfig.testing : null);
3346
+
3347
+ const nextAccount = {
3348
+ ...account,
3349
+ enabled: typeof account.enabled === 'boolean'
3350
+ ? account.enabled
3351
+ : (typeof sourceRuntimeConfig.enabled === 'boolean' ? sourceRuntimeConfig.enabled : true),
3352
+ ...(serverUrl ? { serverUrl } : {}),
3353
+ ...(apiKey ? { apiKey } : {}),
3354
+ ...(normalizedAccountId ? { accountId: normalizedAccountId } : {}),
3355
+ ...(normalizedAppToken ? { appToken: normalizedAppToken } : {}),
3356
+ ...(name ? { name } : {}),
3357
+ ...(toolProfile ? { toolProfile } : {}),
3358
+ ...(heartbeatSeconds ? { heartbeatSeconds } : {}),
3359
+ ...(typeof reconnect === 'boolean' ? { reconnect } : {}),
3360
+ ...(routing ? { routing } : {}),
3361
+ ...(testing ? { testing } : {}),
3362
+ };
3363
+
3364
+ const relayAppToken = normalizeClaworldText(existingRelay.appToken, normalizeClaworldText(runtimeRelay.appToken, null));
3365
+ const relayCredentialToken = normalizeClaworldText(
3366
+ existingRelay.credentialToken,
3367
+ normalizeClaworldText(runtimeRelay.credentialToken, null),
3368
+ );
3369
+ const relayDefaultTargetAgentId = normalizeClaworldText(
3370
+ existingRelay.defaultTargetAgentId,
3371
+ normalizeClaworldText(runtimeRelay.defaultTargetAgentId, null),
3372
+ );
3373
+ const relay = {
3374
+ ...(relayAppToken ? { appToken: relayAppToken } : {}),
3375
+ ...(relayCredentialToken ? { credentialToken: relayCredentialToken } : {}),
3376
+ ...(relayDefaultTargetAgentId ? { defaultTargetAgentId: relayDefaultTargetAgentId } : {}),
3377
+ ...(normalizedRelayAgentId ? { agentId: normalizedRelayAgentId } : {}),
3378
+ };
3379
+ if (Object.keys(relay).length > 0) nextAccount.relay = relay;
3380
+ else delete nextAccount.relay;
3381
+ delete nextAccount.registration;
3382
+
3383
+ const missingRequired = [];
3384
+ if (typeof nextAccount.enabled !== 'boolean') missingRequired.push('enabled');
3385
+ if (!normalizeClaworldText(nextAccount.serverUrl, null)) missingRequired.push('serverUrl');
3386
+ if (!normalizeClaworldText(nextAccount.apiKey, null)) missingRequired.push('apiKey');
3387
+ if (!normalizeClaworldText(nextAccount.accountId, null)) missingRequired.push('accountId');
3388
+
3389
+ return { account: nextAccount, missingRequired };
3390
+ }
3391
+
3392
+ function applyRuntimeAppTokenConfigMutation(configDraft, {
3393
+ accountId,
3394
+ appToken,
3395
+ relayAgentId = null,
3396
+ runtimeConfig = null,
3397
+ } = {}) {
3398
+ if (!isClaworldPlainObject(configDraft)) {
3399
+ return { skipped: true, reason: 'invalid_config_draft' };
3400
+ }
3401
+ const normalizedAccountId = normalizeClaworldText(accountId, normalizeClaworldText(runtimeConfig?.accountId, null));
3402
+ if (!normalizedAccountId || !normalizeClaworldText(appToken, resolveRuntimeAppToken(runtimeConfig))) {
3403
+ return { skipped: true, reason: 'missing_account_or_token' };
3404
+ }
3405
+
3406
+ configDraft.channels = isClaworldPlainObject(configDraft.channels) ? configDraft.channels : {};
3407
+ const claworldRoot = isClaworldPlainObject(configDraft.channels.claworld)
3408
+ ? configDraft.channels.claworld
3409
+ : {};
3410
+ const accounts = isClaworldPlainObject(claworldRoot.accounts) ? claworldRoot.accounts : {};
3411
+ const currentAccount = isClaworldPlainObject(accounts[normalizedAccountId])
3412
+ ? accounts[normalizedAccountId]
3413
+ : {};
3414
+ const built = buildRuntimeAppTokenAccountConfig(currentAccount, {
3415
+ accountId: normalizedAccountId,
3416
+ appToken,
3417
+ relayAgentId,
3418
+ runtimeConfig,
3419
+ });
3420
+ if (built.missingRequired.length > 0) {
3421
+ return {
3422
+ skipped: true,
3423
+ reason: 'missing_required_config_fields',
3424
+ accountId: normalizedAccountId,
3425
+ missingRequired: built.missingRequired,
3426
+ };
3427
+ }
3428
+
3429
+ if (JSON.stringify(currentAccount) === JSON.stringify(built.account)) {
3430
+ return {
3431
+ skipped: true,
3432
+ reason: 'already_persisted',
3433
+ accountId: normalizedAccountId,
3434
+ };
3435
+ }
3436
+
3437
+ accounts[normalizedAccountId] = built.account;
3438
+ claworldRoot.accounts = accounts;
3439
+ if (!normalizeClaworldText(claworldRoot.defaultAccount, null)) {
3440
+ claworldRoot.defaultAccount = normalizedAccountId;
3441
+ }
3442
+ configDraft.channels.claworld = claworldRoot;
3443
+ return {
3444
+ skipped: false,
3445
+ ok: true,
3446
+ accountId: normalizedAccountId,
3447
+ };
3448
+ }
3449
+
3450
+ async function persistRuntimeAppToken({ runtime, accountId, appToken, relayAgentId = null, runtimeConfig = null }) {
3313
3451
  if (!accountId || !appToken) {
3314
3452
  return { skipped: true, reason: 'missing_account_or_token' };
3315
3453
  }
3316
3454
 
3317
3455
  let configPersistResult = { skipped: true, reason: 'missing_runtime_config_io' };
3318
- if (runtime?.config?.loadConfig && runtime?.config?.writeConfigFile) {
3319
- const currentCfg = await runtime.config.loadConfig();
3320
- const nextCfg = JSON.parse(JSON.stringify(currentCfg || {}));
3321
- nextCfg.channels = nextCfg.channels && typeof nextCfg.channels === 'object' && !Array.isArray(nextCfg.channels)
3322
- ? nextCfg.channels
3323
- : {};
3324
- const claworldRoot = nextCfg.channels.claworld && typeof nextCfg.channels.claworld === 'object' && !Array.isArray(nextCfg.channels.claworld)
3325
- ? nextCfg.channels.claworld
3326
- : {};
3327
- const accounts = claworldRoot.accounts && typeof claworldRoot.accounts === 'object' && !Array.isArray(claworldRoot.accounts)
3328
- ? claworldRoot.accounts
3329
- : {};
3330
- const account = accounts[accountId] && typeof accounts[accountId] === 'object' && !Array.isArray(accounts[accountId])
3331
- ? accounts[accountId]
3332
- : {};
3333
-
3334
- const normalizedRelayAgentId = normalizeClaworldText(relayAgentId, normalizeClaworldText(account?.relay?.agentId, null));
3335
- const currentAppToken = normalizeClaworldText(account.appToken, null);
3336
- const currentRelayAgentId = normalizeClaworldText(account?.relay?.agentId, null);
3337
- if (currentAppToken === appToken && currentRelayAgentId === normalizedRelayAgentId) {
3338
- configPersistResult = { skipped: true, reason: 'already_persisted' };
3339
- } else {
3340
- accounts[accountId] = {
3341
- ...account,
3342
- appToken,
3343
- relay: {
3344
- ...(account?.relay && typeof account.relay === 'object' && !Array.isArray(account.relay) ? account.relay : {}),
3345
- ...(normalizedRelayAgentId ? { agentId: normalizedRelayAgentId } : {}),
3456
+ try {
3457
+ if (typeof runtime?.config?.mutateConfigFile === 'function') {
3458
+ let mutationOutcome = null;
3459
+ const mutationParams = {
3460
+ base: 'source',
3461
+ afterWrite: { mode: 'auto' },
3462
+ mutate: (draft) => {
3463
+ mutationOutcome = applyRuntimeAppTokenConfigMutation(draft, {
3464
+ accountId,
3465
+ appToken,
3466
+ relayAgentId,
3467
+ runtimeConfig,
3468
+ });
3469
+ return mutationOutcome;
3346
3470
  },
3347
3471
  };
3348
- delete accounts[accountId].registration;
3349
- claworldRoot.accounts = accounts;
3350
- nextCfg.channels.claworld = claworldRoot;
3351
- await runtime.config.writeConfigFile(nextCfg);
3352
- configPersistResult = { skipped: false, ok: true };
3472
+ const mutationResult = await runtime.config.mutateConfigFile(mutationParams);
3473
+ configPersistResult = {
3474
+ ...(mutationResult?.result || mutationOutcome || { skipped: false, ok: true }),
3475
+ method: 'mutateConfigFile',
3476
+ };
3477
+ } else if (runtime?.config?.loadConfig && runtime?.config?.writeConfigFile) {
3478
+ const currentCfg = await runtime.config.loadConfig();
3479
+ const nextCfg = JSON.parse(JSON.stringify(currentCfg || {}));
3480
+ const mutationOutcome = applyRuntimeAppTokenConfigMutation(nextCfg, {
3481
+ accountId,
3482
+ appToken,
3483
+ relayAgentId,
3484
+ runtimeConfig,
3485
+ });
3486
+ configPersistResult = {
3487
+ ...mutationOutcome,
3488
+ method: 'loadConfig_writeConfigFile',
3489
+ };
3490
+ if (!mutationOutcome.skipped) {
3491
+ await runtime.config.writeConfigFile(nextCfg);
3492
+ }
3353
3493
  }
3494
+ } catch (error) {
3495
+ configPersistResult = {
3496
+ skipped: true,
3497
+ reason: 'config_persist_failed',
3498
+ error: error?.message || String(error),
3499
+ };
3500
+ logger.warn?.(`[claworld:${accountId || 'default'}] failed to persist runtime appToken to OpenClaw config`, {
3501
+ accountId,
3502
+ error: configPersistResult.error,
3503
+ });
3354
3504
  }
3355
3505
 
3356
3506
  let backupPersistResult = { skipped: true, reason: 'missing_runtime_config_loader' };
@@ -3358,6 +3508,9 @@ export function createClaworldChannelPlugin({
3358
3508
  backupPersistResult = await persistClaworldRuntimeBackup({
3359
3509
  runtime,
3360
3510
  accountId,
3511
+ runtimeConfig,
3512
+ appToken,
3513
+ relayAgentId,
3361
3514
  });
3362
3515
  } catch (error) {
3363
3516
  backupPersistResult = {
@@ -3399,8 +3552,10 @@ export function createClaworldChannelPlugin({
3399
3552
  };
3400
3553
  }
3401
3554
 
3555
+ const backupAgentId = normalizeClaworldText(backup?.agentId, null);
3402
3556
  const restoredRuntimeConfig = applyRuntimeIdentity(runtimeConfig, {
3403
3557
  appToken: backupToken,
3558
+ agentId: backupAgentId,
3404
3559
  });
3405
3560
 
3406
3561
  try {
@@ -3408,6 +3563,8 @@ export function createClaworldChannelPlugin({
3408
3563
  runtime,
3409
3564
  accountId,
3410
3565
  appToken: backupToken,
3566
+ relayAgentId: backupAgentId,
3567
+ runtimeConfig: restoredRuntimeConfig,
3411
3568
  });
3412
3569
  } catch (error) {
3413
3570
  logger.warn?.(`[claworld:${accountId || 'default'}] failed to persist restored runtime appToken`, {
@@ -3621,6 +3778,7 @@ export function createClaworldChannelPlugin({
3621
3778
  accountId: runtimeConfig.accountId,
3622
3779
  appToken: resolveRuntimeAppToken(runtimeConfig),
3623
3780
  relayAgentId: runtimeConfig.relay?.agentId || null,
3781
+ runtimeConfig,
3624
3782
  });
3625
3783
  if (!persisted.skipped || persisted.backup?.skipped === false) {
3626
3784
  logger.info?.(`[claworld:${runtimeAccountId}] persisted runtime binding state`, {
@@ -3874,6 +4032,7 @@ async function updateRuntimePublicIdentity(context = {}) {
3874
4032
  accountId: resolvedContext.accountId || boundRuntimeConfig.accountId || null,
3875
4033
  appToken: nextAppToken,
3876
4034
  relayAgentId: nextAgentId,
4035
+ runtimeConfig: boundRuntimeConfig,
3877
4036
  });
3878
4037
  } catch (error) {
3879
4038
  logger.warn?.('[claworld:profile] failed to persist activated runtime binding', {
@@ -4208,7 +4367,6 @@ async function generateRuntimeProfileCard(context = {}) {
4208
4367
  contactable: Object.prototype.hasOwnProperty.call(context, 'contactable') ? context.contactable : undefined,
4209
4368
  chatRequestApprovalPolicy: context.chatRequestApprovalPolicy || null,
4210
4369
  proactivitySettings: Object.prototype.hasOwnProperty.call(context, 'proactivitySettings') ? context.proactivitySettings : undefined,
4211
- communicationPreference: Object.prototype.hasOwnProperty.call(context, 'communicationPreference') ? context.communicationPreference : undefined,
4212
4370
  generateShareCard: context.generateShareCard === true,
4213
4371
  expiresInSeconds: context.expiresInSeconds ?? null,
4214
4372
  fetchImpl,
@@ -4592,7 +4750,6 @@ async function generateRuntimeProfileCard(context = {}) {
4592
4750
  contactable: Object.prototype.hasOwnProperty.call(context, 'contactable') ? context.contactable : undefined,
4593
4751
  chatRequestApprovalPolicy: context.chatRequestApprovalPolicy || null,
4594
4752
  proactivitySettings: Object.prototype.hasOwnProperty.call(context, 'proactivitySettings') ? context.proactivitySettings : undefined,
4595
- communicationPreference: Object.prototype.hasOwnProperty.call(context, 'communicationPreference') ? context.communicationPreference : undefined,
4596
4753
  generateShareCard: context.generateShareCard === true,
4597
4754
  expiresInSeconds: context.expiresInSeconds ?? null,
4598
4755
  fetchImpl,
@@ -490,10 +490,6 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
490
490
  description: 'Account-level proactive-management settings.',
491
491
  additionalProperties: true,
492
492
  }),
493
- communicationPreference: objectParam({
494
- description: 'Optional account-level communication preference settings.',
495
- additionalProperties: true,
496
- }),
497
493
  targetAgentId: stringParam({
498
494
  description: 'Target agent id for subscribe_person or unsubscribe_person.',
499
495
  minLength: 1,
@@ -583,7 +579,6 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
583
579
  contactable: params.contactable,
584
580
  chatRequestApprovalPolicy: params.chatRequestApprovalPolicy || null,
585
581
  proactivitySettings: params.proactivitySettings,
586
- communicationPreference: params.communicationPreference,
587
582
  generateShareCard,
588
583
  expiresInSeconds: params.expiresInSeconds ?? null,
589
584
  });
@@ -2061,14 +2056,16 @@ function buildRegisteredTools(api, plugin) {
2061
2056
  }));
2062
2057
  }
2063
2058
 
2064
- const cfg = await loadCurrentConfig(api);
2065
- const accountId = normalizeText(params.accountId, plugin.config.defaultAccountId(cfg) || null);
2066
- const runtimeConfig = plugin.config.resolveRuntimeConfig(cfg, accountId);
2059
+ const context = await resolveToolContext(api, plugin, params);
2060
+ const cfg = context.cfg || await loadCurrentConfig(api);
2061
+ const accountId = context.accountId;
2062
+ const runtimeConfig = context.runtimeConfig || plugin.config.resolveRuntimeConfig(cfg, accountId);
2067
2063
  const identityPayload = await plugin.runtime.productShell.profile.getPublicIdentity({
2064
+ ...context,
2068
2065
  cfg,
2069
2066
  accountId,
2070
2067
  runtimeConfig,
2071
- agentId: runtimeConfig.relay?.agentId || null,
2068
+ agentId: context.agentId || runtimeConfig.relay?.agentId || null,
2072
2069
  generateShareCard,
2073
2070
  expiresInSeconds: params.expiresInSeconds ?? null,
2074
2071
  });
@@ -8,6 +8,7 @@ import {
8
8
  setClaworldManagedRuntimeBackupState,
9
9
  stripClaworldManagedRuntimeConfig,
10
10
  } from './managed-config.js';
11
+ import { resolveRuntimeAppToken } from './account-identity.js';
11
12
 
12
13
  function normalizeText(value, fallback = null) {
13
14
  if (value == null) return fallback;
@@ -67,18 +68,53 @@ export async function persistClaworldRuntimeBackup({
67
68
  runtime = null,
68
69
  accountId = DEFAULT_CLAWORLD_ACCOUNT_ID,
69
70
  configPath = resolveDefaultOpenClawConfigPath(),
71
+ runtimeConfig = null,
72
+ appToken = null,
73
+ relayAgentId = null,
70
74
  } = {}) {
71
- if (!runtime?.config?.loadConfig) {
75
+ if (!runtime?.config?.loadConfig && !runtimeConfig && !appToken) {
72
76
  return { skipped: true, reason: 'missing_runtime_config_loader' };
73
77
  }
74
78
 
75
- const currentConfig = await runtime.config.loadConfig();
76
- const { backup } = stripClaworldManagedRuntimeConfig(currentConfig, {
79
+ let currentConfig = {};
80
+ if (runtime?.config?.loadConfig) {
81
+ try {
82
+ currentConfig = await runtime.config.loadConfig();
83
+ } catch (error) {
84
+ if (!runtimeConfig && !appToken) throw error;
85
+ currentConfig = {};
86
+ }
87
+ }
88
+ const { backup: configBackup } = stripClaworldManagedRuntimeConfig(currentConfig, {
77
89
  accountId,
78
90
  preserveBackup: true,
79
91
  });
92
+ const explicitRuntimeConfig = runtimeConfig && typeof runtimeConfig === 'object' && !Array.isArray(runtimeConfig)
93
+ ? runtimeConfig
94
+ : {};
95
+ const backup = {
96
+ ...configBackup,
97
+ version: configBackup?.version || 1,
98
+ accountId: normalizeText(configBackup?.accountId, normalizeText(explicitRuntimeConfig.accountId, accountId)),
99
+ agentId: normalizeText(relayAgentId, normalizeText(configBackup?.agentId, normalizeText(explicitRuntimeConfig?.relay?.agentId, null))),
100
+ serverUrl: normalizeText(configBackup?.serverUrl, normalizeText(explicitRuntimeConfig.serverUrl, null)),
101
+ apiKey: normalizeText(configBackup?.apiKey, normalizeText(explicitRuntimeConfig.apiKey, null)),
102
+ appToken: normalizeText(appToken, normalizeText(configBackup?.appToken, resolveRuntimeAppToken(explicitRuntimeConfig))),
103
+ displayName: normalizeText(
104
+ configBackup?.displayName,
105
+ normalizeText(explicitRuntimeConfig.name, normalizeText(explicitRuntimeConfig.registration?.displayName, null)),
106
+ ),
107
+ name: normalizeText(configBackup?.name, normalizeText(explicitRuntimeConfig.name, null)),
108
+ registrationDisplayName: normalizeText(
109
+ configBackup?.registrationDisplayName,
110
+ normalizeText(explicitRuntimeConfig.registration?.displayName, null),
111
+ ),
112
+ sessionDmScope: normalizeText(configBackup?.sessionDmScope, null),
113
+ toolProfile: normalizeText(configBackup?.toolProfile, null),
114
+ preservedAt: new Date().toISOString(),
115
+ };
80
116
 
81
- if (!backup?.appToken) {
117
+ if (!backup.appToken) {
82
118
  return { skipped: true, reason: 'missing_app_token', backup: null };
83
119
  }
84
120
 
@@ -193,6 +193,7 @@ function buildClaworldManagementStartupPrompt(options = {}) {
193
193
  '',
194
194
  '## First Rule',
195
195
  'When you receive a Claworld notification, management wake, lifecycle event, or recurring Claworld management task, read the `claworld-management-session` skill before deciding what to do.',
196
+ 'A pre-compaction memory flush is a maintenance turn only. It does not satisfy, replace, or change any Claworld notification. After the flush finishes, handle the pending or next Claworld notification from scratch: read the Claworld management skill first, then decide accordingly.',
196
197
  '',
197
198
  '## What To Trust',
198
199
  'Use Claworld tools when you need current product facts: account state, public profiles, worlds, memberships, chat requests, conversation status, feedback, and delivery state.',