@xfxstudio/claworld 0.2.9 → 0.2.10-beta.0

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 (49) hide show
  1. package/README.md +1 -1
  2. package/openclaw.plugin.json +7 -63
  3. package/package.json +6 -2
  4. package/skills/claworld-help/SKILL.md +5 -1
  5. package/skills/claworld-join-and-chat/SKILL.md +21 -1
  6. package/skills/claworld-manage-worlds/SKILL.md +81 -10
  7. package/src/lib/agent-profile.js +8 -3
  8. package/src/lib/chat-request.js +0 -1
  9. package/src/lib/policy.js +2 -6
  10. package/src/lib/public-identity.js +175 -0
  11. package/src/lib/relay/kickoff-text.js +1 -0
  12. package/src/openclaw/installer/cli.js +46 -1
  13. package/src/openclaw/installer/constants.js +1 -0
  14. package/src/openclaw/installer/core.js +234 -3
  15. package/src/openclaw/installer/doctor.js +2 -2
  16. package/src/openclaw/plugin/account-identity.js +1 -2
  17. package/src/openclaw/plugin/claworld-channel-plugin.js +270 -255
  18. package/src/openclaw/plugin/config-schema.js +9 -23
  19. package/src/openclaw/plugin/managed-config.js +284 -79
  20. package/src/openclaw/plugin/onboarding.js +22 -42
  21. package/src/openclaw/plugin/register.js +109 -10
  22. package/src/openclaw/plugin/relay-client.js +233 -17
  23. package/src/openclaw/runtime/backend-error-context.js +91 -0
  24. package/src/openclaw/runtime/feedback-helper.js +1 -2
  25. package/src/openclaw/runtime/product-shell-helper.js +43 -9
  26. package/src/openclaw/runtime/tool-contracts.js +26 -3
  27. package/src/openclaw/runtime/tool-inventory.js +7 -0
  28. package/src/openclaw/runtime/world-moderation-helper.js +3 -19
  29. package/src/product-shell/contracts/candidate-feed.js +7 -0
  30. package/src/product-shell/contracts/world-manifest.js +0 -1
  31. package/src/product-shell/contracts/world-orchestration.js +10 -1
  32. package/src/product-shell/conversation-feedback/conversation-feedback-service.js +261 -0
  33. package/src/product-shell/feedback/feedback-routes.js +0 -1
  34. package/src/product-shell/feedback/feedback-service.js +4 -9
  35. package/src/product-shell/index.js +40 -7
  36. package/src/product-shell/matching/matchmaking-service.js +22 -1
  37. package/src/product-shell/membership/membership-service.js +5 -1
  38. package/src/product-shell/onboarding/onboarding-service.js +10 -21
  39. package/src/product-shell/profile/public-identity-routes.js +60 -0
  40. package/src/product-shell/profile/public-identity-service.js +190 -0
  41. package/src/product-shell/search/search-service.js +9 -2
  42. package/src/product-shell/social/chat-request-service.js +22 -7
  43. package/src/product-shell/social/friend-routes.js +1 -1
  44. package/src/product-shell/social/friend-service.js +16 -19
  45. package/src/product-shell/social/social-routes.js +2 -2
  46. package/src/product-shell/social/social-service.js +31 -35
  47. package/src/product-shell/worlds/world-admin-service.js +31 -10
  48. package/src/product-shell/worlds/world-broadcast-service.js +2 -2
  49. package/src/lib/agent-address.js +0 -46
@@ -45,12 +45,14 @@ import {
45
45
  resolveWorldSelection,
46
46
  resolveWorldSelectionFlow,
47
47
  } from '../runtime/product-shell-helper.js';
48
+ import { extractBackendErrorContext } from '../runtime/backend-error-context.js';
48
49
  import { getClaworldRuntime } from './runtime.js';
49
50
  import {
50
51
  createRuntimeBoundaryError,
51
52
  normalizeRuntimeBoundaryError,
52
53
  serializeRuntimeBoundaryError,
53
54
  } from '../../lib/runtime-errors.js';
55
+ import { v4 as uuidv4 } from 'uuid';
54
56
 
55
57
  function normalizeRelayHttpBaseUrl(serverUrl) {
56
58
  const parsed = new URL(serverUrl);
@@ -67,108 +69,16 @@ function normalizePluginOptionalText(value) {
67
69
  return normalized || null;
68
70
  }
69
71
 
70
- function inferRelayDomain(runtimeConfig = {}) {
71
- const defaultToAddress = String(runtimeConfig.relay?.defaultToAddress || '').trim();
72
- const atIndex = defaultToAddress.indexOf('@');
73
- if (atIndex > 0 && atIndex < defaultToAddress.length - 1) {
74
- return defaultToAddress.slice(atIndex + 1).trim().toLowerCase();
75
- }
76
- return 'relay.local';
77
- }
78
-
79
- const RELAY_LOCAL_AGENT_CODE_PATTERN = /^[A-Za-z0-9._:+~-]+$/;
80
- const RELAY_CANONICAL_AGENT_CODE_PATTERN = /^[A-Za-z0-9._:+~-]+@[A-Za-z0-9._:+~-]+$/;
81
-
82
- function normalizeRelayDomainName(value, fallback = null) {
83
- const normalized = normalizeClaworldText(value, fallback)?.toLowerCase() || null;
84
- if (!normalized) return fallback;
85
- if (!/^[a-z0-9._:+~-]+$/.test(normalized)) return fallback;
86
- return normalized;
87
- }
88
-
89
- function normalizeRelayLocalAgentCode(value, fallback = null) {
90
- const normalized = normalizeClaworldText(value, fallback)?.toLowerCase() || null;
91
- if (!normalized) return fallback;
92
- if (!RELAY_LOCAL_AGENT_CODE_PATTERN.test(normalized)) return fallback;
93
- return normalized;
94
- }
95
-
96
- function parseCanonicalRelayAgentCode(value) {
97
- const normalized = normalizeClaworldText(value, null)?.toLowerCase() || null;
98
- if (!normalized || !RELAY_CANONICAL_AGENT_CODE_PATTERN.test(normalized)) return null;
99
- const atIndex = normalized.indexOf('@');
100
- if (atIndex <= 0 || atIndex >= normalized.length - 1) return null;
101
- const relayLocalCode = normalizeRelayLocalAgentCode(normalized.slice(0, atIndex), null);
102
- const domain = normalizeRelayDomainName(normalized.slice(atIndex + 1), null);
103
- if (!relayLocalCode || !domain) return null;
104
- return {
105
- agentCode: `${relayLocalCode}@${domain}`,
106
- relayLocalCode,
107
- domain,
108
- };
109
- }
110
-
111
- function buildCanonicalRelayAgentCode(relayLocalCode, runtimeConfig = {}, domainOverride = null) {
112
- const normalizedRelayLocalCode = normalizeRelayLocalAgentCode(relayLocalCode, null);
113
- const domain = normalizeRelayDomainName(domainOverride, null)
114
- || normalizeRelayDomainName(inferRelayDomain(runtimeConfig), null);
115
- if (!normalizedRelayLocalCode || !domain) return null;
116
- return `${normalizedRelayLocalCode}@${domain}`;
117
- }
118
-
119
- function normalizeCanonicalRelayAgentCode(value, runtimeConfig = {}) {
120
- const parsed = parseCanonicalRelayAgentCode(value);
121
- if (parsed) return parsed.agentCode;
122
- const relayLocalCode = normalizeRelayLocalAgentCode(value, null);
123
- if (!relayLocalCode) return null;
124
- return buildCanonicalRelayAgentCode(relayLocalCode, runtimeConfig);
125
- }
126
-
127
- function normalizeRelayIdentityInput({ runtimeConfig = {}, agentId = null, agentCode = null, address = null } = {}) {
128
- const normalizedAgentId = normalizeClaworldText(agentId, null);
129
- const normalizedAddress = normalizeClaworldText(resolveRelayAddress(address || agentCode || '', runtimeConfig), null)?.toLowerCase() || null;
130
- const parsedCanonicalAgentCode = parseCanonicalRelayAgentCode(agentCode)
131
- || parseCanonicalRelayAgentCode(normalizedAddress);
132
- const relayLocalCode = normalizeRelayLocalAgentCode(agentCode, null)
133
- || parsedCanonicalAgentCode?.relayLocalCode
134
- || null;
135
- const domain = parsedCanonicalAgentCode?.domain
136
- || normalizeRelayDomainName(normalizedAddress?.split('@')[1], null)
137
- || normalizeRelayDomainName(inferRelayDomain(runtimeConfig), null);
138
- const normalizedAgentCode = parsedCanonicalAgentCode?.agentCode
139
- || (relayLocalCode ? buildCanonicalRelayAgentCode(relayLocalCode, runtimeConfig, domain) : null)
140
- || null;
141
-
142
- return {
143
- agentId: normalizedAgentId,
144
- agentCode: normalizedAgentCode,
145
- relayLocalCode,
146
- address: normalizedAddress || normalizedAgentCode || null,
147
- domain,
148
- };
72
+ function resolveClientMessageId(value = null) {
73
+ return normalizePluginOptionalText(value) || `cmsg_${uuidv4()}`;
149
74
  }
150
75
 
151
- function buildRelayAgentSummary(item = {}, runtimeConfig = {}) {
76
+ function buildRelayAgentSummary(item = {}) {
152
77
  const normalizedAgentId = normalizeClaworldText(item?.agentId, null);
153
- const relayLocalCode = normalizeRelayLocalAgentCode(item?.agentCode, null);
154
- const address = normalizeClaworldText(item?.address, null)?.toLowerCase()
155
- || buildCanonicalRelayAgentCode(relayLocalCode, runtimeConfig, item?.domain)
156
- || null;
157
- const parsedCanonicalAgentCode = parseCanonicalRelayAgentCode(address);
158
- const domain = normalizeRelayDomainName(item?.domain, null)
159
- || parsedCanonicalAgentCode?.domain
160
- || normalizeRelayDomainName(inferRelayDomain(runtimeConfig), null);
161
- const canonicalAgentCode = parsedCanonicalAgentCode?.agentCode
162
- || buildCanonicalRelayAgentCode(relayLocalCode, runtimeConfig, domain)
163
- || null;
164
-
165
78
  return {
166
79
  agentId: normalizedAgentId,
167
- agentCode: canonicalAgentCode,
168
- relayLocalCode: relayLocalCode || parsedCanonicalAgentCode?.relayLocalCode || null,
169
- domain,
170
- address: address || canonicalAgentCode || null,
171
80
  displayName: normalizeClaworldText(item?.displayName, null),
81
+ publicIdentity: item?.publicIdentity && typeof item.publicIdentity === 'object' ? item.publicIdentity : null,
172
82
  discoverable: typeof item?.discoverable === 'boolean' ? item.discoverable : null,
173
83
  contactable: typeof item?.contactable === 'boolean' ? item.contactable : null,
174
84
  online: typeof item?.online === 'boolean' ? item.online : null,
@@ -179,19 +89,10 @@ function normalizeClaworldTarget(raw) {
179
89
  if (typeof raw !== 'string') return undefined;
180
90
  let value = raw.trim();
181
91
  if (!value) return undefined;
182
- value = value.replace(/^claworld:/i, '').replace(/^user:/i, '').replace(/^@/, '').trim();
92
+ value = value.replace(/^claworld:/i, '').replace(/^user:/i, '').trim();
183
93
  return value || undefined;
184
94
  }
185
95
 
186
- function resolveRelayAddress(to, runtimeConfig = {}) {
187
- const normalized = normalizeClaworldTarget(typeof to === 'string' ? to : '');
188
- const fallback = normalizeClaworldTarget(runtimeConfig.relay?.defaultToAddress || '');
189
- const candidate = normalized || fallback;
190
- if (!candidate) return '';
191
- if (candidate.includes('@')) return candidate.toLowerCase();
192
- return `${candidate.toLowerCase()}@${inferRelayDomain(runtimeConfig)}`;
193
- }
194
-
195
96
  function normalizeClaworldText(value, fallback = null) {
196
97
  if (value == null) return fallback;
197
98
  const normalized = String(value).trim();
@@ -215,6 +116,45 @@ function shouldAuthorizeBridgedCommand({ runtimeConfig = {}, incomingText }) {
215
116
  return typeof incomingText === 'string' && incomingText.trim().startsWith('/');
216
117
  }
217
118
 
119
+ function normalizeUntrustedContextLines(value) {
120
+ if (Array.isArray(value)) {
121
+ return value
122
+ .map((entry) => resolveNormalizedText(entry, null))
123
+ .filter(Boolean);
124
+ }
125
+ const singleLine = resolveNormalizedText(value, null);
126
+ return singleLine ? [singleLine] : [];
127
+ }
128
+
129
+ function mergeUntrustedContextLines(...groups) {
130
+ const merged = [];
131
+ const seen = new Set();
132
+ for (const group of groups) {
133
+ for (const line of normalizeUntrustedContextLines(group)) {
134
+ if (seen.has(line)) continue;
135
+ seen.add(line);
136
+ merged.push(line);
137
+ }
138
+ }
139
+ return merged;
140
+ }
141
+
142
+ function parseBridgeTimestampMs(value) {
143
+ if (typeof value === 'number' && Number.isFinite(value)) return value;
144
+ if (typeof value !== 'string') return null;
145
+ const normalized = value.trim();
146
+ if (!normalized) return null;
147
+ const parsed = Date.parse(normalized);
148
+ return Number.isFinite(parsed) ? parsed : null;
149
+ }
150
+
151
+ function resolveBridgeDeliveryTimestampMs({ delivery = {}, metadata = {} } = {}) {
152
+ return parseBridgeTimestampMs(delivery?.createdAt)
153
+ || parseBridgeTimestampMs(delivery?.turnCreatedAt)
154
+ || parseBridgeTimestampMs(metadata?.createdAt)
155
+ || Date.now();
156
+ }
157
+
218
158
  const CLAWORLD_RELAY_OPERATIONAL_NOTICE_PATTERNS = [
219
159
  /^🧭\s*New session:\s+\S+/i,
220
160
  /^🧹\s*Auto-compaction complete(?:\s*\(count \d+\))?\.$/i,
@@ -355,10 +295,11 @@ function buildClaworldDirectoryEntries(config = {}, accountId = null) {
355
295
  const account = inspectClaworldChannelAccount(config, id);
356
296
  if (!account?.enabled || !account?.configured) continue;
357
297
  const normalizedId = String(account.accountId || id || '').trim();
358
- if (!normalizedId) continue;
298
+ const boundAgentId = normalizeClaworldText(account.relay?.agentId, null);
299
+ if (!normalizedId || !boundAgentId) continue;
359
300
  if (currentAccountId && normalizedId === currentAccountId) continue;
360
301
  entries.push({
361
- id: resolveRelayAddress(normalizedId, account),
302
+ id: boundAgentId,
362
303
  name: normalizedId,
363
304
  handle: normalizedId,
364
305
  rank: 100,
@@ -366,11 +307,10 @@ function buildClaworldDirectoryEntries(config = {}, accountId = null) {
366
307
  }
367
308
 
368
309
  const current = inspectClaworldChannelAccount(config, currentAccountId || null);
369
- const defaultTarget = resolveRelayAddress(current?.relay?.defaultToAddress || '', current || {});
370
- if (defaultTarget) {
371
- const localPart = defaultTarget.split('@')[0] || defaultTarget;
372
- if (!entries.some((entry) => entry.id === defaultTarget)) {
373
- entries.push({ id: defaultTarget, name: localPart, handle: localPart, rank: 50 });
310
+ const defaultTargetAgentId = normalizeClaworldText(current?.relay?.defaultTargetAgentId, null);
311
+ if (defaultTargetAgentId) {
312
+ if (!entries.some((entry) => entry.id === defaultTargetAgentId)) {
313
+ entries.push({ id: defaultTargetAgentId, name: defaultTargetAgentId, handle: defaultTargetAgentId, rank: 50 });
374
314
  }
375
315
  }
376
316
 
@@ -381,8 +321,8 @@ async function deliverRelayMessage({ runtimeConfig, to, text, fetchImpl, logger,
381
321
  const fromAgentId = runtimeConfig.relay?.agentId;
382
322
  if (!fromAgentId) throw new Error('claworld relay.agentId is required for outbound send');
383
323
 
384
- const toAddress = resolveRelayAddress(to, runtimeConfig);
385
- if (!toAddress) throw new Error('claworld outbound target is required');
324
+ const targetAgentId = normalizeClaworldText(to, null);
325
+ if (!targetAgentId) throw new Error('claworld outbound targetAgentId is required');
386
326
 
387
327
  const normalizedText = normalizeClaworldText(text, null);
388
328
  const payload = messagePayload && typeof messagePayload === 'object'
@@ -397,6 +337,9 @@ async function deliverRelayMessage({ runtimeConfig, to, text, fetchImpl, logger,
397
337
  }
398
338
  payload.source = normalizeClaworldText(payload.source, 'openclaw-claworld');
399
339
  payload.accountId = normalizeClaworldText(payload.accountId, runtimeConfig.accountId);
340
+ const clientMessageId = resolveClientMessageId(
341
+ outboundContext.clientMessageId || outboundContext.metadata?.clientMessageId || null
342
+ );
400
343
 
401
344
  const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
402
345
  const result = await fetchJson(fetchImpl, `${baseUrl}/v1/messages`, {
@@ -408,7 +351,8 @@ async function deliverRelayMessage({ runtimeConfig, to, text, fetchImpl, logger,
408
351
  },
409
352
  body: JSON.stringify({
410
353
  fromAgentId,
411
- toAddress,
354
+ targetAgentId,
355
+ clientMessageId,
412
356
  payload,
413
357
  conversation: {
414
358
  conversationKey: outboundContext.conversationKey || outboundContext.metadata?.conversationKey || null,
@@ -433,7 +377,7 @@ async function deliverRelayMessage({ runtimeConfig, to, text, fetchImpl, logger,
433
377
  context: {
434
378
  accountId: runtimeConfig.accountId || null,
435
379
  fromAgentId,
436
- toAddress,
380
+ targetAgentId,
437
381
  status: result.status,
438
382
  },
439
383
  });
@@ -442,13 +386,14 @@ async function deliverRelayMessage({ runtimeConfig, to, text, fetchImpl, logger,
442
386
  return {
443
387
  channel: 'claworld',
444
388
  messageId: result.body?.turn?.turnId || `turn_${Date.now()}`,
445
- chatId: toAddress,
389
+ chatId: targetAgentId,
446
390
  timestamp: Date.now(),
447
391
  meta: {
392
+ clientMessageId,
448
393
  sessionKey: result.body?.delivery?.sessionKey || outboundContext.sessionKey || outboundContext.SessionKey || null,
449
394
  turnId: result.body?.turn?.turnId || null,
450
395
  conversationKey: result.body?.conversationKey || null,
451
- toAddress,
396
+ targetAgentId,
452
397
  },
453
398
  };
454
399
  }
@@ -481,8 +426,7 @@ function createRelayRouteError({
481
426
  context: {
482
427
  accountId: runtimeConfig.accountId || null,
483
428
  httpStatus: result?.status || null,
484
- backendCode: result?.body?.error || null,
485
- backendMessage: result?.body?.message || null,
429
+ ...extractBackendErrorContext(result?.body),
486
430
  ...context,
487
431
  },
488
432
  });
@@ -491,7 +435,7 @@ function createRelayRouteError({
491
435
  async function createFriendRequest({
492
436
  runtimeConfig,
493
437
  fromAgentId,
494
- toAddress,
438
+ targetAgentId,
495
439
  message = null,
496
440
  metadata = {},
497
441
  fetchImpl,
@@ -506,7 +450,7 @@ async function createFriendRequest({
506
450
  },
507
451
  body: JSON.stringify({
508
452
  fromAgentId,
509
- toAddress,
453
+ targetAgentId,
510
454
  message: normalizeClaworldText(message, null),
511
455
  metadata: metadata && typeof metadata === 'object' && !Array.isArray(metadata) ? metadata : {},
512
456
  }),
@@ -517,7 +461,7 @@ async function createFriendRequest({
517
461
  runtimeConfig,
518
462
  code: 'friend_request_failed',
519
463
  publicMessage: 'failed to create friend request',
520
- context: { fromAgentId, toAddress },
464
+ context: { fromAgentId, targetAgentId },
521
465
  });
522
466
  }
523
467
  return result.body || {};
@@ -833,16 +777,6 @@ function isNonRecoverableBootstrapHoldError(error) {
833
777
  }
834
778
 
835
779
  function buildBootstrapHoldMessage(error, runtimeConfig = {}) {
836
- if (error?.code === 'agent_code_conflict') {
837
- const requestedAgentCode = normalizeClaworldText(
838
- error?.context?.requestedAgentCode,
839
- normalizeRuntimeRegistration(runtimeConfig).agentCode || null,
840
- );
841
- return requestedAgentCode
842
- ? `Claworld setup blocked: relay agent code ${requestedAgentCode} already exists. Add the bound appToken for this account or choose a different registration.agentCode.`
843
- : 'Claworld setup blocked: relay agent code already exists. Add the bound appToken for this account or choose a different registration.agentCode.';
844
- }
845
-
846
780
  const publicMessage = normalizeClaworldText(error?.publicMessage, null);
847
781
  const fallbackMessage = normalizeClaworldText(error?.message, 'relay binding bootstrap failed');
848
782
  return `Claworld setup blocked: ${publicMessage || fallbackMessage}`;
@@ -970,6 +904,79 @@ async function fetchJson(fetchImpl, url, init = {}) {
970
904
  }
971
905
  }
972
906
 
907
+ async function fetchPublicIdentity({
908
+ runtimeConfig,
909
+ agentId = null,
910
+ fetchImpl,
911
+ }) {
912
+ const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
913
+ const path = buildRelayJsonPath('/v1/profile/public-identity', {
914
+ agentId,
915
+ });
916
+ const result = await fetchJson(fetchImpl, `${baseUrl}${path}`, {
917
+ method: 'GET',
918
+ headers: {
919
+ ...(runtimeConfig.apiKey ? { 'x-api-key': runtimeConfig.apiKey } : {}),
920
+ ...buildRuntimeAuthHeaders(runtimeConfig),
921
+ },
922
+ });
923
+ if (!result.ok) {
924
+ createRelayRouteError({
925
+ result,
926
+ runtimeConfig,
927
+ code: 'public_identity_fetch_failed',
928
+ publicMessage: 'failed to read public identity status',
929
+ context: { agentId: normalizeClaworldText(agentId, null) },
930
+ });
931
+ }
932
+ return result.body || {};
933
+ }
934
+
935
+ async function updatePublicIdentity({
936
+ runtimeConfig,
937
+ agentId = null,
938
+ displayName = null,
939
+ fetchImpl,
940
+ }) {
941
+ const normalizedDisplayName = normalizeClaworldText(displayName, null);
942
+ if (!normalizedDisplayName) {
943
+ throw createRuntimeBoundaryError({
944
+ code: 'tool_input_invalid',
945
+ category: 'input',
946
+ status: 400,
947
+ message: 'claworld public identity update requires displayName',
948
+ publicMessage: 'claworld public identity update requires displayName',
949
+ recoverable: true,
950
+ context: { field: 'displayName' },
951
+ });
952
+ }
953
+ const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
954
+ const result = await fetchJson(fetchImpl, `${baseUrl}/v1/profile/public-identity`, {
955
+ method: 'PUT',
956
+ headers: {
957
+ 'content-type': 'application/json',
958
+ ...(runtimeConfig.apiKey ? { 'x-api-key': runtimeConfig.apiKey } : {}),
959
+ ...buildRuntimeAuthHeaders(runtimeConfig),
960
+ },
961
+ body: JSON.stringify({
962
+ ...(normalizeClaworldText(agentId, null) ? { agentId: normalizeClaworldText(agentId, null) } : {}),
963
+ displayName: normalizedDisplayName,
964
+ }),
965
+ });
966
+ if (!result.ok) {
967
+ createRelayRouteError({
968
+ result,
969
+ runtimeConfig,
970
+ code: 'public_identity_update_failed',
971
+ publicMessage: 'failed to update public identity',
972
+ context: {
973
+ agentId: normalizeClaworldText(agentId, null),
974
+ },
975
+ });
976
+ }
977
+ return result.body || {};
978
+ }
979
+
973
980
  async function registerRelayBinding({ runtimeConfig, fetchImpl, logger }) {
974
981
  if (typeof fetchImpl !== 'function') {
975
982
  throw new Error('fetch is unavailable for relay registration');
@@ -985,7 +992,6 @@ async function registerRelayBinding({ runtimeConfig, fetchImpl, logger }) {
985
992
  ...(runtimeConfig.apiKey ? { 'x-api-key': runtimeConfig.apiKey } : {}),
986
993
  }),
987
994
  body: JSON.stringify({
988
- ...(registration.enabled ? { agentCode: registration.agentCode } : {}),
989
995
  ...(registration.displayName ? { displayName: registration.displayName } : {}),
990
996
  }),
991
997
  });
@@ -1007,11 +1013,7 @@ async function registerRelayBinding({ runtimeConfig, fetchImpl, logger }) {
1007
1013
  : registerResult.status >= 500
1008
1014
  ? 'transport'
1009
1015
  : 'config';
1010
- const publicMessage = errorCode === 'agent_code_conflict'
1011
- ? 'relay agent code already exists; reuse its appToken or choose a different registration.agentCode'
1012
- : errorCode === 'agent_code_mismatch'
1013
- ? 'configured relay agent code does not match the provided appToken'
1014
- : 'relay registration failed';
1016
+ const publicMessage = 'relay registration failed';
1015
1017
 
1016
1018
  throw createRuntimeBoundaryError({
1017
1019
  code: errorCode,
@@ -1022,14 +1024,7 @@ async function registerRelayBinding({ runtimeConfig, fetchImpl, logger }) {
1022
1024
  recoverable: registerResult.status >= 500,
1023
1025
  context: {
1024
1026
  accountId: runtimeConfig.accountId || null,
1025
- requestedAgentCode: normalizeClaworldText(
1026
- registerResult.body?.requestedAgentCode,
1027
- registration.agentCode || null,
1028
- ),
1029
- authenticatedAgentCode: normalizeClaworldText(registerResult.body?.authenticatedAgentCode, null),
1030
- authenticatedAddress: normalizeClaworldText(registerResult.body?.authenticatedAddress, null),
1031
1027
  conflictingAgentId: normalizeClaworldText(registerResult.body?.agent?.agentId, null),
1032
- conflictingAddress: normalizeClaworldText(registerResult.body?.agent?.address, null),
1033
1028
  appTokenConfigured: Boolean(resolveRuntimeAppToken(runtimeConfig)),
1034
1029
  },
1035
1030
  });
@@ -1085,67 +1080,30 @@ async function resolveRelayAgentSummary({
1085
1080
  fetchImpl,
1086
1081
  logger,
1087
1082
  agentId = null,
1088
- agentCode = null,
1089
- address = null,
1090
1083
  }) {
1091
- const requestedIdentity = normalizeRelayIdentityInput({
1092
- runtimeConfig,
1093
- agentId,
1094
- agentCode,
1095
- address,
1096
- });
1084
+ const normalizedAgentId = normalizeClaworldText(agentId, null);
1097
1085
 
1098
1086
  try {
1099
1087
  const items = await fetchRelayAgents({ runtimeConfig, fetchImpl, logger });
1100
1088
  const match = items
1101
- .map((item) => buildRelayAgentSummary(item, runtimeConfig))
1102
- .find((item) => {
1103
- if (requestedIdentity.agentId && item.agentId === requestedIdentity.agentId) return true;
1104
- if (requestedIdentity.agentCode && item.agentCode === requestedIdentity.agentCode) return true;
1105
- if (requestedIdentity.address && item.address === requestedIdentity.address) return true;
1106
- if (requestedIdentity.relayLocalCode && item.relayLocalCode === requestedIdentity.relayLocalCode) return true;
1107
- return false;
1108
- }) || null;
1089
+ .map((item) => buildRelayAgentSummary(item))
1090
+ .find((item) => normalizedAgentId && item.agentId === normalizedAgentId) || null;
1109
1091
 
1110
1092
  if (match) {
1111
1093
  return {
1112
1094
  ...match,
1113
1095
  resolved: true,
1114
- resolutionSource: requestedIdentity.agentId && match.agentId === requestedIdentity.agentId
1115
- ? 'agentId'
1116
- : requestedIdentity.agentCode && match.agentCode === requestedIdentity.agentCode
1117
- ? 'agentCode'
1118
- : requestedIdentity.address && match.address === requestedIdentity.address
1119
- ? 'address'
1120
- : 'relayLocalCode',
1096
+ resolutionSource: 'agentId',
1121
1097
  };
1122
1098
  }
1123
1099
  } catch {
1124
1100
  // Fallback below keeps pairing/send tools usable even when lookup fails.
1125
1101
  }
1126
1102
 
1127
- const canInferFallbackAgentCode = Boolean(
1128
- requestedIdentity.agentCode
1129
- || requestedIdentity.address
1130
- || requestedIdentity.relayLocalCode,
1131
- );
1132
- const fallbackAgentCode = requestedIdentity.agentCode
1133
- || (canInferFallbackAgentCode ? buildCanonicalRelayAgentCode(runtimeConfig.localAgent?.agentCode, runtimeConfig) : null)
1134
- || (canInferFallbackAgentCode ? buildCanonicalRelayAgentCode(runtimeConfig.accountId, runtimeConfig) : null);
1135
- const parsedFallbackAgentCode = parseCanonicalRelayAgentCode(fallbackAgentCode);
1136
-
1137
1103
  return {
1138
- agentId: requestedIdentity.agentId,
1139
- agentCode: fallbackAgentCode,
1140
- relayLocalCode: requestedIdentity.relayLocalCode
1141
- || parsedFallbackAgentCode?.relayLocalCode
1142
- || normalizeRelayLocalAgentCode(runtimeConfig.localAgent?.agentCode, null)
1143
- || null,
1144
- domain: requestedIdentity.domain
1145
- || parsedFallbackAgentCode?.domain
1146
- || normalizeRelayDomainName(inferRelayDomain(runtimeConfig), null),
1147
- address: requestedIdentity.address || fallbackAgentCode || null,
1148
- displayName: normalizeClaworldText(runtimeConfig.localAgent?.displayName, null),
1104
+ agentId: normalizedAgentId,
1105
+ displayName: normalizeClaworldText(runtimeConfig.registration?.displayName, normalizeClaworldText(runtimeConfig.localAgent?.displayName, null)),
1106
+ publicIdentity: null,
1149
1107
  discoverable: null,
1150
1108
  contactable: null,
1151
1109
  online: null,
@@ -1158,10 +1116,6 @@ async function ensureAgentPairing({ runtimeConfig, fetchImpl, logger }) {
1158
1116
  const binding = await ensureRelayBinding({ runtimeConfig, fetchImpl, logger });
1159
1117
  const pairedRuntimeConfig = binding.runtimeConfig;
1160
1118
  const relayAgentId = normalizeClaworldText(pairedRuntimeConfig.relay?.agentId, null);
1161
- const relayAgentCode = normalizeRelayLocalAgentCode(
1162
- pairedRuntimeConfig.localAgent?.agentCode,
1163
- normalizeRelayLocalAgentCode(pairedRuntimeConfig.accountId, null),
1164
- );
1165
1119
 
1166
1120
  if (!relayAgentId) {
1167
1121
  return {
@@ -1174,7 +1128,6 @@ async function ensureAgentPairing({ runtimeConfig, fetchImpl, logger }) {
1174
1128
  fetchImpl,
1175
1129
  logger,
1176
1130
  agentId: relayAgentId,
1177
- agentCode: relayAgentCode,
1178
1131
  }),
1179
1132
  };
1180
1133
  }
@@ -1184,14 +1137,13 @@ async function ensureAgentPairing({ runtimeConfig, fetchImpl, logger }) {
1184
1137
  reason: null,
1185
1138
  bindingSource: binding.bindingSource,
1186
1139
  runtimeConfig: pairedRuntimeConfig,
1187
- relayAgent: await resolveRelayAgentSummary({
1188
- runtimeConfig: pairedRuntimeConfig,
1189
- fetchImpl,
1190
- logger,
1191
- agentId: relayAgentId,
1192
- agentCode: relayAgentCode,
1193
- }),
1194
- };
1140
+ relayAgent: await resolveRelayAgentSummary({
1141
+ runtimeConfig: pairedRuntimeConfig,
1142
+ fetchImpl,
1143
+ logger,
1144
+ agentId: relayAgentId,
1145
+ }),
1146
+ };
1195
1147
  }
1196
1148
 
1197
1149
  async function fetchPostSetupWorldDirectory({ cfg, accountId, runtimeConfig, limit = null, sort = null, page = null, fetchImpl, logger }) {
@@ -1248,7 +1200,7 @@ async function ensureRelayBinding({ runtimeConfig, fetchImpl, logger }) {
1248
1200
  serverUrl: normalizedRuntimeConfig.serverUrl,
1249
1201
  appTokenConfigured: Boolean(appToken),
1250
1202
  registrationEnabled: registration.enabled,
1251
- agentCode: registration.agentCode || null,
1203
+ displayName: registration.displayName || null,
1252
1204
  hasRelayAgentId: Boolean(normalizedRuntimeConfig.relay?.agentId),
1253
1205
  });
1254
1206
 
@@ -1273,10 +1225,12 @@ function buildDeliveryInboundEnvelope({
1273
1225
  incomingText,
1274
1226
  contextText = null,
1275
1227
  commandText = null,
1228
+ timestamp = null,
1276
1229
  deliveryId,
1277
1230
  sessionKey,
1278
1231
  worldId = null,
1279
1232
  conversationKey = null,
1233
+ untrustedContext = [],
1280
1234
  }) {
1281
1235
  const envelopeOptions = runtime?.channel?.reply?.resolveEnvelopeFormatOptions
1282
1236
  ? runtime.channel.reply.resolveEnvelopeFormatOptions(currentCfg)
@@ -1286,37 +1240,43 @@ function buildDeliveryInboundEnvelope({
1286
1240
  String(incomingText || '').trim(),
1287
1241
  ].filter(Boolean).join('\n\n');
1288
1242
  const remoteLabel = String(remoteIdentity || 'unknown-peer').trim() || 'unknown-peer';
1289
- const systemLines = [
1243
+ const rawBody = String(incomingText || '').trim();
1244
+ const normalizedCommandText = String(commandText || '').trim();
1245
+ const commandBody = normalizedCommandText || rawBody;
1246
+ const bodyForAgent = bodyText || rawBody;
1247
+ const contextLines = mergeUntrustedContextLines([
1290
1248
  `[claworld peer ${remoteLabel}]`,
1291
1249
  ...(worldId ? [`[claworld world ${worldId}]`] : []),
1292
1250
  ...(conversationKey ? [`[claworld conversation ${conversationKey}]`] : []),
1293
1251
  `[claworld session ${sessionKey}]`,
1294
1252
  `[claworld delivery ${deliveryId}]`,
1295
- ];
1296
- const rawBody = `${bodyText}\n\n${systemLines.join('\n')}`;
1297
- const normalizedCommandText = String(commandText || '').trim();
1298
- const commandBody = normalizedCommandText || rawBody;
1253
+ ], untrustedContext);
1254
+ const envelopeTimestamp = Number.isFinite(timestamp) ? new Date(timestamp) : new Date();
1299
1255
 
1300
1256
  if (runtime?.channel?.reply?.formatAgentEnvelope) {
1301
1257
  return {
1302
1258
  Body: runtime.channel.reply.formatAgentEnvelope({
1303
1259
  channel: 'Claworld',
1304
1260
  from: remoteLabel,
1305
- timestamp: new Date(),
1261
+ timestamp: envelopeTimestamp,
1306
1262
  envelope: envelopeOptions,
1307
- body: rawBody,
1263
+ body: bodyForAgent,
1308
1264
  }),
1309
1265
  RawBody: rawBody,
1310
1266
  CommandBody: commandBody,
1311
- ContextLines: systemLines,
1267
+ BodyForAgent: bodyForAgent,
1268
+ BodyForCommands: commandBody,
1269
+ UntrustedContext: contextLines,
1312
1270
  };
1313
1271
  }
1314
1272
 
1315
1273
  return {
1316
- Body: `${remoteLabel}: ${rawBody}`,
1274
+ Body: `${remoteLabel}: ${bodyForAgent}`,
1317
1275
  RawBody: rawBody,
1318
1276
  CommandBody: commandBody,
1319
- ContextLines: systemLines,
1277
+ BodyForAgent: bodyForAgent,
1278
+ BodyForCommands: commandBody,
1279
+ UntrustedContext: contextLines,
1320
1280
  };
1321
1281
  }
1322
1282
 
@@ -1678,15 +1638,14 @@ async function maybeBridgeRuntimeDelivery({
1678
1638
  : {};
1679
1639
  const deliveryId = resolveNormalizedText(delivery.deliveryId, null);
1680
1640
  const sessionKey = resolveNormalizedText(delivery.sessionKey, null);
1681
- const incomingText = resolveNormalizedText(payload.text, null);
1682
- const contextText = null;
1641
+ const contextText = resolveNormalizedText(payload.contextText, null);
1642
+ const incomingText = resolveNormalizedText(
1643
+ payload.commandText,
1644
+ contextText ? null : resolveNormalizedText(payload.text, null),
1645
+ );
1646
+ const commandText = resolveNormalizedText(payload.commandText, incomingText);
1683
1647
  const fromAgentId = resolveNormalizedText(metadata.fromAgentId, null);
1684
- const remoteAddress = resolveNormalizedText(metadata.fromAddress, null)
1685
- || resolveRelayAddress(fromAgentId || '', runtimeConfig)
1686
- || fromAgentId
1687
- || 'unknown-peer';
1688
- const remoteRouteIdentity = resolveRelayAddress(fromAgentId || '', runtimeConfig)
1689
- || remoteAddress;
1648
+ const remoteIdentity = fromAgentId || 'unknown-peer';
1690
1649
 
1691
1650
  if (
1692
1651
  !runtime?.channel?.reply?.finalizeInboundContext
@@ -1699,11 +1658,12 @@ async function maybeBridgeRuntimeDelivery({
1699
1658
  });
1700
1659
  return { skipped: true, reason: 'missing_runtime_bridge_hooks' };
1701
1660
  }
1702
- if (!deliveryId || !sessionKey || !incomingText) {
1661
+ if (!deliveryId || !sessionKey || (!incomingText && !contextText)) {
1703
1662
  logger.warn?.(`[claworld:${runtimeAccountId}] skipping delivery bridge: missing delivery payload`, {
1704
1663
  deliveryId,
1705
1664
  sessionKey,
1706
1665
  hasIncomingText: Boolean(incomingText),
1666
+ hasContextText: Boolean(contextText),
1707
1667
  });
1708
1668
  return { skipped: true, reason: 'missing_delivery_payload' };
1709
1669
  }
@@ -1716,48 +1676,51 @@ async function maybeBridgeRuntimeDelivery({
1716
1676
  const worldId = resolveDeliveryWorldId(delivery);
1717
1677
  const commandAuthorized = shouldAuthorizeBridgedCommand({
1718
1678
  runtimeConfig,
1719
- incomingText,
1679
+ incomingText: commandText || incomingText,
1720
1680
  });
1721
- const { Body, RawBody, CommandBody, ContextLines } = buildDeliveryInboundEnvelope({
1681
+ const inboundTimestamp = resolveBridgeDeliveryTimestampMs({ delivery, metadata });
1682
+ const { Body, RawBody, CommandBody, BodyForAgent, BodyForCommands, UntrustedContext } = buildDeliveryInboundEnvelope({
1722
1683
  runtime,
1723
1684
  currentCfg,
1724
- remoteIdentity: remoteAddress,
1685
+ remoteIdentity,
1725
1686
  incomingText,
1726
1687
  contextText,
1727
- commandText: incomingText,
1688
+ commandText,
1689
+ timestamp: inboundTimestamp,
1728
1690
  deliveryId,
1729
1691
  sessionKey,
1730
1692
  worldId,
1731
1693
  conversationKey: metadata.conversationKey || null,
1694
+ untrustedContext: payload.untrustedContext,
1732
1695
  });
1733
- const localAddress = resolveRelayAddress(runtimeConfig.accountId || '', runtimeConfig);
1696
+ const localIdentity = normalizeClaworldText(runtimeConfig.relay?.agentId, runtimeConfig.accountId);
1734
1697
  const inboundCtx = runtime.channel.reply.finalizeInboundContext({
1735
1698
  Body,
1736
1699
  RawBody,
1737
1700
  CommandBody,
1738
- BodyForAgent: RawBody,
1739
- BodyForCommands: CommandBody,
1740
- From: `claworld:${remoteRouteIdentity}`,
1741
- To: localAddress ? `claworld:${localAddress}` : `claworld:${runtimeConfig.accountId}`,
1701
+ BodyForAgent,
1702
+ BodyForCommands,
1703
+ From: `claworld:${remoteIdentity}`,
1704
+ To: `claworld:${localIdentity}`,
1742
1705
  SessionKey: sessionKey,
1743
1706
  AccountId: runtimeConfig.accountId,
1744
1707
  OriginatingChannel: 'claworld',
1745
- OriginatingFrom: remoteRouteIdentity,
1746
- OriginatingTo: remoteRouteIdentity,
1708
+ OriginatingFrom: remoteIdentity,
1709
+ OriginatingTo: remoteIdentity,
1747
1710
  ChatType: 'direct',
1748
- SenderName: remoteAddress,
1749
- SenderId: remoteRouteIdentity,
1711
+ SenderName: remoteIdentity,
1712
+ SenderId: remoteIdentity,
1750
1713
  MessageId: deliveryId,
1751
1714
  Provider: 'claworld',
1752
1715
  Surface: 'claworld',
1753
- ConversationLabel: remoteAddress,
1754
- Timestamp: Date.now(),
1716
+ ConversationLabel: remoteIdentity,
1717
+ Timestamp: inboundTimestamp,
1755
1718
  MessageSid: deliveryId,
1756
1719
  WasMentioned: false,
1757
1720
  CommandAuthorized: commandAuthorized,
1758
1721
  RelayDeliveryId: deliveryId,
1759
1722
  RelayFromAgentId: fromAgentId,
1760
- UntrustedContext: ContextLines,
1723
+ UntrustedContext,
1761
1724
  });
1762
1725
  const localAgentId = resolveBoundLocalAgentId({
1763
1726
  cfg: currentCfg,
@@ -1788,7 +1751,7 @@ async function maybeBridgeRuntimeDelivery({
1788
1751
  deliveryId,
1789
1752
  sessionKey,
1790
1753
  localAgentId,
1791
- remoteIdentity: remoteAddress,
1754
+ remoteIdentity,
1792
1755
  routeStatus: routed?.status || null,
1793
1756
  bodyPreview: String(Body || '').slice(0, 240),
1794
1757
  rawBodyPreview: String(RawBody || '').slice(0, 240),
@@ -1796,6 +1759,23 @@ async function maybeBridgeRuntimeDelivery({
1796
1759
  commandAuthorized,
1797
1760
  });
1798
1761
 
1762
+ try {
1763
+ if (typeof relayClient?.sendAcceptedAndWaitForAck === 'function') {
1764
+ await relayClient.sendAcceptedAndWaitForAck({
1765
+ deliveryId,
1766
+ sessionKey,
1767
+ source: 'runtime_dispatch',
1768
+ });
1769
+ }
1770
+ } catch (error) {
1771
+ logger.warn?.(`[claworld:${runtimeAccountId}] delivery acceptance acknowledgement failed`, {
1772
+ deliveryId,
1773
+ sessionKey,
1774
+ localAgentId,
1775
+ error: error?.message || String(error),
1776
+ });
1777
+ }
1778
+
1799
1779
  let {
1800
1780
  dispatchResult,
1801
1781
  replied,
@@ -2349,9 +2329,9 @@ export function createClaworldChannelPlugin({
2349
2329
  },
2350
2330
  agentPrompt: {
2351
2331
  messageToolHints: () => [
2352
- '- Claworld targets are canonical identity handles like `alice@robin`.',
2332
+ '- Claworld message targets are canonical `agentId` values such as `agt_xxx`.',
2353
2333
  '- Omit `target` to keep replying inside the current A2A session when the runtime already inferred the peer.',
2354
- '- Use explicit targets like `user:alice@robin` when you want to open a new relay session to another remote OpenClaw agent.',
2334
+ '- Resolve public identity like `displayName#code` to `agentId` before opening a new relay session to another agent.',
2355
2335
  ],
2356
2336
  },
2357
2337
  reload: { configPrefixes: ['channels.claworld'] },
@@ -2383,19 +2363,18 @@ export function createClaworldChannelPlugin({
2383
2363
  looksLikeId: (raw, normalized) => {
2384
2364
  const value = String(normalized || raw || '').trim();
2385
2365
  if (!value) return false;
2386
- if (value.includes('@')) return RELAY_CANONICAL_AGENT_CODE_PATTERN.test(value);
2387
- return /^[a-z0-9._:+~-]+$/i.test(value) || /^agt_[a-z0-9_-]+$/i.test(value);
2366
+ return /^agt_[a-z0-9_-]+$/i.test(value);
2388
2367
  },
2389
- hint: '<agent@relay-domain|agentId>',
2368
+ hint: '<agentId>',
2390
2369
  },
2391
2370
  },
2392
2371
  directory: {
2393
2372
  self: async ({ cfg, accountId } = {}) => {
2394
2373
  const account = inspectClaworldChannelAccount(cfg || {}, accountId || null);
2395
- const address = resolveRelayAddress(account?.accountId || '', account || {});
2396
- if (!account?.configured || !address) return null;
2374
+ const agentId = normalizeClaworldText(account?.relay?.agentId, null);
2375
+ if (!account?.configured || !agentId) return null;
2397
2376
  return {
2398
- id: address,
2377
+ id: agentId,
2399
2378
  name: account.accountId,
2400
2379
  handle: account.accountId,
2401
2380
  };
@@ -2478,8 +2457,6 @@ export function createClaworldChannelPlugin({
2478
2457
  fetchImpl,
2479
2458
  logger,
2480
2459
  agentId: context.agentId || context.targetAgentId || null,
2481
- agentCode: context.agentCode || context.targetAgentCode || null,
2482
- address: context.address || null,
2483
2460
  }),
2484
2461
  },
2485
2462
  social: {
@@ -2488,7 +2465,7 @@ export function createClaworldChannelPlugin({
2488
2465
  return createFriendRequest({
2489
2466
  runtimeConfig: resolvedContext.runtimeConfig,
2490
2467
  fromAgentId: resolvedContext.agentId || null,
2491
- toAddress: context.toAddress || null,
2468
+ targetAgentId: context.targetAgentId || null,
2492
2469
  message: context.message || null,
2493
2470
  metadata: context.metadata || {},
2494
2471
  fetchImpl,
@@ -2578,6 +2555,25 @@ export function createClaworldChannelPlugin({
2578
2555
  });
2579
2556
  },
2580
2557
  },
2558
+ profile: {
2559
+ getPublicIdentity: async (context = {}) => {
2560
+ const resolvedContext = await resolveBoundRuntimeContext(context);
2561
+ return fetchPublicIdentity({
2562
+ runtimeConfig: resolvedContext.runtimeConfig,
2563
+ agentId: resolvedContext.agentId || null,
2564
+ fetchImpl,
2565
+ });
2566
+ },
2567
+ updatePublicIdentity: async (context = {}) => {
2568
+ const resolvedContext = await resolveBoundRuntimeContext(context);
2569
+ return updatePublicIdentity({
2570
+ runtimeConfig: resolvedContext.runtimeConfig,
2571
+ agentId: resolvedContext.agentId || null,
2572
+ displayName: context.displayName || null,
2573
+ fetchImpl,
2574
+ });
2575
+ },
2576
+ },
2581
2577
  postSetup: {
2582
2578
  fetchWorldDirectory: async (context = {}) => {
2583
2579
  const resolvedContext = await resolveBoundRuntimeContext(context);
@@ -2686,7 +2682,7 @@ export function createClaworldChannelPlugin({
2686
2682
  agentId: resolvedContext.agentId || null,
2687
2683
  displayName: context.displayName || null,
2688
2684
  worldContextText: context.worldContextText || null,
2689
- enabled: context.enabled === true,
2685
+ enabled: typeof context.enabled === 'boolean' ? context.enabled : true,
2690
2686
  fetchImpl,
2691
2687
  logger,
2692
2688
  });
@@ -2728,6 +2724,25 @@ export function createClaworldChannelPlugin({
2728
2724
  results,
2729
2725
  demo,
2730
2726
  productShell: {
2727
+ profile: {
2728
+ getPublicIdentity: async (context = {}) => {
2729
+ const resolvedContext = await resolveBoundRuntimeContext(context);
2730
+ return fetchPublicIdentity({
2731
+ runtimeConfig: resolvedContext.runtimeConfig || null,
2732
+ agentId: resolvedContext.agentId || null,
2733
+ fetchImpl,
2734
+ });
2735
+ },
2736
+ updatePublicIdentity: async (context = {}) => {
2737
+ const resolvedContext = await resolveBoundRuntimeContext(context);
2738
+ return updatePublicIdentity({
2739
+ runtimeConfig: resolvedContext.runtimeConfig || null,
2740
+ agentId: resolvedContext.agentId || null,
2741
+ displayName: context.displayName || null,
2742
+ fetchImpl,
2743
+ });
2744
+ },
2745
+ },
2731
2746
  fetchWorldDirectory: async (context = {}) => {
2732
2747
  const resolvedContext = await resolveBoundRuntimeContext(context);
2733
2748
  return fetchPostSetupWorldDirectory({
@@ -2868,14 +2883,14 @@ export function createClaworldChannelPlugin({
2868
2883
  moderation: {
2869
2884
  createWorld: async (context = {}) => {
2870
2885
  const resolvedContext = await resolveBoundRuntimeContext(context);
2871
- return createModeratedWorld({
2886
+ return createModeratedWorld({
2872
2887
  cfg: resolvedContext.cfg || {},
2873
2888
  accountId: resolvedContext.accountId || null,
2874
2889
  runtimeConfig: resolvedContext.runtimeConfig || null,
2875
2890
  agentId: resolvedContext.agentId || null,
2876
2891
  displayName: context.displayName || null,
2877
2892
  worldContextText: context.worldContextText || null,
2878
- enabled: context.enabled === true,
2893
+ enabled: typeof context.enabled === 'boolean' ? context.enabled : true,
2879
2894
  fetchImpl,
2880
2895
  logger,
2881
2896
  });