@xfxstudio/claworld 0.2.8 → 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 (50) 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 +7 -3
  5. package/skills/claworld-join-and-chat/SKILL.md +38 -9
  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 +19 -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 +7 -1
  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 +302 -266
  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 +144 -25
  22. package/src/openclaw/plugin/relay-client.js +237 -18
  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 +65 -3
  27. package/src/openclaw/runtime/tool-inventory.js +8 -1
  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-routes.js +4 -1
  43. package/src/product-shell/social/chat-request-service.js +184 -22
  44. package/src/product-shell/social/friend-routes.js +1 -1
  45. package/src/product-shell/social/friend-service.js +16 -19
  46. package/src/product-shell/social/social-routes.js +2 -2
  47. package/src/product-shell/social/social-service.js +31 -35
  48. package/src/product-shell/worlds/world-admin-service.js +31 -10
  49. package/src/product-shell/worlds/world-broadcast-service.js +2 -2
  50. 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}`;
72
+ function resolveClientMessageId(value = null) {
73
+ return normalizePluginOptionalText(value) || `cmsg_${uuidv4()}`;
117
74
  }
118
75
 
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
- };
149
- }
150
-
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 || {};
@@ -617,6 +561,7 @@ async function createChatRequest({
617
561
  targetAgentId,
618
562
  openingMessage = null,
619
563
  worldId = null,
564
+ requestContext = null,
620
565
  fetchImpl,
621
566
  }) {
622
567
  const normalizedTargetAgentId = normalizeClaworldText(targetAgentId, null);
@@ -644,6 +589,9 @@ async function createChatRequest({
644
589
  targetAgentId: normalizedTargetAgentId,
645
590
  openingMessage: normalizeClaworldText(openingMessage, null),
646
591
  ...(normalizeClaworldText(worldId, null) ? { worldId: normalizeClaworldText(worldId, null) } : {}),
592
+ ...(requestContext && typeof requestContext === 'object' && !Array.isArray(requestContext)
593
+ ? { requestContext }
594
+ : {}),
647
595
  }),
648
596
  });
649
597
  if (!result.ok) {
@@ -658,7 +606,7 @@ async function createChatRequest({
658
606
  return result.body || {};
659
607
  }
660
608
 
661
- async function listChatRequests({
609
+ async function listChatInbox({
662
610
  runtimeConfig,
663
611
  agentId,
664
612
  direction = null,
@@ -829,16 +777,6 @@ function isNonRecoverableBootstrapHoldError(error) {
829
777
  }
830
778
 
831
779
  function buildBootstrapHoldMessage(error, runtimeConfig = {}) {
832
- if (error?.code === 'agent_code_conflict') {
833
- const requestedAgentCode = normalizeClaworldText(
834
- error?.context?.requestedAgentCode,
835
- normalizeRuntimeRegistration(runtimeConfig).agentCode || null,
836
- );
837
- return requestedAgentCode
838
- ? `Claworld setup blocked: relay agent code ${requestedAgentCode} already exists. Add the bound appToken for this account or choose a different registration.agentCode.`
839
- : 'Claworld setup blocked: relay agent code already exists. Add the bound appToken for this account or choose a different registration.agentCode.';
840
- }
841
-
842
780
  const publicMessage = normalizeClaworldText(error?.publicMessage, null);
843
781
  const fallbackMessage = normalizeClaworldText(error?.message, 'relay binding bootstrap failed');
844
782
  return `Claworld setup blocked: ${publicMessage || fallbackMessage}`;
@@ -966,6 +904,79 @@ async function fetchJson(fetchImpl, url, init = {}) {
966
904
  }
967
905
  }
968
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
+
969
980
  async function registerRelayBinding({ runtimeConfig, fetchImpl, logger }) {
970
981
  if (typeof fetchImpl !== 'function') {
971
982
  throw new Error('fetch is unavailable for relay registration');
@@ -981,7 +992,6 @@ async function registerRelayBinding({ runtimeConfig, fetchImpl, logger }) {
981
992
  ...(runtimeConfig.apiKey ? { 'x-api-key': runtimeConfig.apiKey } : {}),
982
993
  }),
983
994
  body: JSON.stringify({
984
- ...(registration.enabled ? { agentCode: registration.agentCode } : {}),
985
995
  ...(registration.displayName ? { displayName: registration.displayName } : {}),
986
996
  }),
987
997
  });
@@ -1003,11 +1013,7 @@ async function registerRelayBinding({ runtimeConfig, fetchImpl, logger }) {
1003
1013
  : registerResult.status >= 500
1004
1014
  ? 'transport'
1005
1015
  : 'config';
1006
- const publicMessage = errorCode === 'agent_code_conflict'
1007
- ? 'relay agent code already exists; reuse its appToken or choose a different registration.agentCode'
1008
- : errorCode === 'agent_code_mismatch'
1009
- ? 'configured relay agent code does not match the provided appToken'
1010
- : 'relay registration failed';
1016
+ const publicMessage = 'relay registration failed';
1011
1017
 
1012
1018
  throw createRuntimeBoundaryError({
1013
1019
  code: errorCode,
@@ -1018,14 +1024,7 @@ async function registerRelayBinding({ runtimeConfig, fetchImpl, logger }) {
1018
1024
  recoverable: registerResult.status >= 500,
1019
1025
  context: {
1020
1026
  accountId: runtimeConfig.accountId || null,
1021
- requestedAgentCode: normalizeClaworldText(
1022
- registerResult.body?.requestedAgentCode,
1023
- registration.agentCode || null,
1024
- ),
1025
- authenticatedAgentCode: normalizeClaworldText(registerResult.body?.authenticatedAgentCode, null),
1026
- authenticatedAddress: normalizeClaworldText(registerResult.body?.authenticatedAddress, null),
1027
1027
  conflictingAgentId: normalizeClaworldText(registerResult.body?.agent?.agentId, null),
1028
- conflictingAddress: normalizeClaworldText(registerResult.body?.agent?.address, null),
1029
1028
  appTokenConfigured: Boolean(resolveRuntimeAppToken(runtimeConfig)),
1030
1029
  },
1031
1030
  });
@@ -1081,67 +1080,30 @@ async function resolveRelayAgentSummary({
1081
1080
  fetchImpl,
1082
1081
  logger,
1083
1082
  agentId = null,
1084
- agentCode = null,
1085
- address = null,
1086
1083
  }) {
1087
- const requestedIdentity = normalizeRelayIdentityInput({
1088
- runtimeConfig,
1089
- agentId,
1090
- agentCode,
1091
- address,
1092
- });
1084
+ const normalizedAgentId = normalizeClaworldText(agentId, null);
1093
1085
 
1094
1086
  try {
1095
1087
  const items = await fetchRelayAgents({ runtimeConfig, fetchImpl, logger });
1096
1088
  const match = items
1097
- .map((item) => buildRelayAgentSummary(item, runtimeConfig))
1098
- .find((item) => {
1099
- if (requestedIdentity.agentId && item.agentId === requestedIdentity.agentId) return true;
1100
- if (requestedIdentity.agentCode && item.agentCode === requestedIdentity.agentCode) return true;
1101
- if (requestedIdentity.address && item.address === requestedIdentity.address) return true;
1102
- if (requestedIdentity.relayLocalCode && item.relayLocalCode === requestedIdentity.relayLocalCode) return true;
1103
- return false;
1104
- }) || null;
1089
+ .map((item) => buildRelayAgentSummary(item))
1090
+ .find((item) => normalizedAgentId && item.agentId === normalizedAgentId) || null;
1105
1091
 
1106
1092
  if (match) {
1107
1093
  return {
1108
1094
  ...match,
1109
1095
  resolved: true,
1110
- resolutionSource: requestedIdentity.agentId && match.agentId === requestedIdentity.agentId
1111
- ? 'agentId'
1112
- : requestedIdentity.agentCode && match.agentCode === requestedIdentity.agentCode
1113
- ? 'agentCode'
1114
- : requestedIdentity.address && match.address === requestedIdentity.address
1115
- ? 'address'
1116
- : 'relayLocalCode',
1096
+ resolutionSource: 'agentId',
1117
1097
  };
1118
1098
  }
1119
1099
  } catch {
1120
1100
  // Fallback below keeps pairing/send tools usable even when lookup fails.
1121
1101
  }
1122
1102
 
1123
- const canInferFallbackAgentCode = Boolean(
1124
- requestedIdentity.agentCode
1125
- || requestedIdentity.address
1126
- || requestedIdentity.relayLocalCode,
1127
- );
1128
- const fallbackAgentCode = requestedIdentity.agentCode
1129
- || (canInferFallbackAgentCode ? buildCanonicalRelayAgentCode(runtimeConfig.localAgent?.agentCode, runtimeConfig) : null)
1130
- || (canInferFallbackAgentCode ? buildCanonicalRelayAgentCode(runtimeConfig.accountId, runtimeConfig) : null);
1131
- const parsedFallbackAgentCode = parseCanonicalRelayAgentCode(fallbackAgentCode);
1132
-
1133
1103
  return {
1134
- agentId: requestedIdentity.agentId,
1135
- agentCode: fallbackAgentCode,
1136
- relayLocalCode: requestedIdentity.relayLocalCode
1137
- || parsedFallbackAgentCode?.relayLocalCode
1138
- || normalizeRelayLocalAgentCode(runtimeConfig.localAgent?.agentCode, null)
1139
- || null,
1140
- domain: requestedIdentity.domain
1141
- || parsedFallbackAgentCode?.domain
1142
- || normalizeRelayDomainName(inferRelayDomain(runtimeConfig), null),
1143
- address: requestedIdentity.address || fallbackAgentCode || null,
1144
- displayName: normalizeClaworldText(runtimeConfig.localAgent?.displayName, null),
1104
+ agentId: normalizedAgentId,
1105
+ displayName: normalizeClaworldText(runtimeConfig.registration?.displayName, normalizeClaworldText(runtimeConfig.localAgent?.displayName, null)),
1106
+ publicIdentity: null,
1145
1107
  discoverable: null,
1146
1108
  contactable: null,
1147
1109
  online: null,
@@ -1154,10 +1116,6 @@ async function ensureAgentPairing({ runtimeConfig, fetchImpl, logger }) {
1154
1116
  const binding = await ensureRelayBinding({ runtimeConfig, fetchImpl, logger });
1155
1117
  const pairedRuntimeConfig = binding.runtimeConfig;
1156
1118
  const relayAgentId = normalizeClaworldText(pairedRuntimeConfig.relay?.agentId, null);
1157
- const relayAgentCode = normalizeRelayLocalAgentCode(
1158
- pairedRuntimeConfig.localAgent?.agentCode,
1159
- normalizeRelayLocalAgentCode(pairedRuntimeConfig.accountId, null),
1160
- );
1161
1119
 
1162
1120
  if (!relayAgentId) {
1163
1121
  return {
@@ -1170,7 +1128,6 @@ async function ensureAgentPairing({ runtimeConfig, fetchImpl, logger }) {
1170
1128
  fetchImpl,
1171
1129
  logger,
1172
1130
  agentId: relayAgentId,
1173
- agentCode: relayAgentCode,
1174
1131
  }),
1175
1132
  };
1176
1133
  }
@@ -1180,14 +1137,13 @@ async function ensureAgentPairing({ runtimeConfig, fetchImpl, logger }) {
1180
1137
  reason: null,
1181
1138
  bindingSource: binding.bindingSource,
1182
1139
  runtimeConfig: pairedRuntimeConfig,
1183
- relayAgent: await resolveRelayAgentSummary({
1184
- runtimeConfig: pairedRuntimeConfig,
1185
- fetchImpl,
1186
- logger,
1187
- agentId: relayAgentId,
1188
- agentCode: relayAgentCode,
1189
- }),
1190
- };
1140
+ relayAgent: await resolveRelayAgentSummary({
1141
+ runtimeConfig: pairedRuntimeConfig,
1142
+ fetchImpl,
1143
+ logger,
1144
+ agentId: relayAgentId,
1145
+ }),
1146
+ };
1191
1147
  }
1192
1148
 
1193
1149
  async function fetchPostSetupWorldDirectory({ cfg, accountId, runtimeConfig, limit = null, sort = null, page = null, fetchImpl, logger }) {
@@ -1244,7 +1200,7 @@ async function ensureRelayBinding({ runtimeConfig, fetchImpl, logger }) {
1244
1200
  serverUrl: normalizedRuntimeConfig.serverUrl,
1245
1201
  appTokenConfigured: Boolean(appToken),
1246
1202
  registrationEnabled: registration.enabled,
1247
- agentCode: registration.agentCode || null,
1203
+ displayName: registration.displayName || null,
1248
1204
  hasRelayAgentId: Boolean(normalizedRuntimeConfig.relay?.agentId),
1249
1205
  });
1250
1206
 
@@ -1269,10 +1225,12 @@ function buildDeliveryInboundEnvelope({
1269
1225
  incomingText,
1270
1226
  contextText = null,
1271
1227
  commandText = null,
1228
+ timestamp = null,
1272
1229
  deliveryId,
1273
1230
  sessionKey,
1274
1231
  worldId = null,
1275
1232
  conversationKey = null,
1233
+ untrustedContext = [],
1276
1234
  }) {
1277
1235
  const envelopeOptions = runtime?.channel?.reply?.resolveEnvelopeFormatOptions
1278
1236
  ? runtime.channel.reply.resolveEnvelopeFormatOptions(currentCfg)
@@ -1282,37 +1240,43 @@ function buildDeliveryInboundEnvelope({
1282
1240
  String(incomingText || '').trim(),
1283
1241
  ].filter(Boolean).join('\n\n');
1284
1242
  const remoteLabel = String(remoteIdentity || 'unknown-peer').trim() || 'unknown-peer';
1285
- 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([
1286
1248
  `[claworld peer ${remoteLabel}]`,
1287
1249
  ...(worldId ? [`[claworld world ${worldId}]`] : []),
1288
1250
  ...(conversationKey ? [`[claworld conversation ${conversationKey}]`] : []),
1289
1251
  `[claworld session ${sessionKey}]`,
1290
1252
  `[claworld delivery ${deliveryId}]`,
1291
- ];
1292
- const rawBody = `${bodyText}\n\n${systemLines.join('\n')}`;
1293
- const normalizedCommandText = String(commandText || '').trim();
1294
- const commandBody = normalizedCommandText || rawBody;
1253
+ ], untrustedContext);
1254
+ const envelopeTimestamp = Number.isFinite(timestamp) ? new Date(timestamp) : new Date();
1295
1255
 
1296
1256
  if (runtime?.channel?.reply?.formatAgentEnvelope) {
1297
1257
  return {
1298
1258
  Body: runtime.channel.reply.formatAgentEnvelope({
1299
1259
  channel: 'Claworld',
1300
1260
  from: remoteLabel,
1301
- timestamp: new Date(),
1261
+ timestamp: envelopeTimestamp,
1302
1262
  envelope: envelopeOptions,
1303
- body: rawBody,
1263
+ body: bodyForAgent,
1304
1264
  }),
1305
1265
  RawBody: rawBody,
1306
1266
  CommandBody: commandBody,
1307
- ContextLines: systemLines,
1267
+ BodyForAgent: bodyForAgent,
1268
+ BodyForCommands: commandBody,
1269
+ UntrustedContext: contextLines,
1308
1270
  };
1309
1271
  }
1310
1272
 
1311
1273
  return {
1312
- Body: `${remoteLabel}: ${rawBody}`,
1274
+ Body: `${remoteLabel}: ${bodyForAgent}`,
1313
1275
  RawBody: rawBody,
1314
1276
  CommandBody: commandBody,
1315
- ContextLines: systemLines,
1277
+ BodyForAgent: bodyForAgent,
1278
+ BodyForCommands: commandBody,
1279
+ UntrustedContext: contextLines,
1316
1280
  };
1317
1281
  }
1318
1282
 
@@ -1674,15 +1638,14 @@ async function maybeBridgeRuntimeDelivery({
1674
1638
  : {};
1675
1639
  const deliveryId = resolveNormalizedText(delivery.deliveryId, null);
1676
1640
  const sessionKey = resolveNormalizedText(delivery.sessionKey, null);
1677
- const incomingText = resolveNormalizedText(payload.text, null);
1678
- 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);
1679
1647
  const fromAgentId = resolveNormalizedText(metadata.fromAgentId, null);
1680
- const remoteAddress = resolveNormalizedText(metadata.fromAddress, null)
1681
- || resolveRelayAddress(fromAgentId || '', runtimeConfig)
1682
- || fromAgentId
1683
- || 'unknown-peer';
1684
- const remoteRouteIdentity = resolveRelayAddress(fromAgentId || '', runtimeConfig)
1685
- || remoteAddress;
1648
+ const remoteIdentity = fromAgentId || 'unknown-peer';
1686
1649
 
1687
1650
  if (
1688
1651
  !runtime?.channel?.reply?.finalizeInboundContext
@@ -1695,11 +1658,12 @@ async function maybeBridgeRuntimeDelivery({
1695
1658
  });
1696
1659
  return { skipped: true, reason: 'missing_runtime_bridge_hooks' };
1697
1660
  }
1698
- if (!deliveryId || !sessionKey || !incomingText) {
1661
+ if (!deliveryId || !sessionKey || (!incomingText && !contextText)) {
1699
1662
  logger.warn?.(`[claworld:${runtimeAccountId}] skipping delivery bridge: missing delivery payload`, {
1700
1663
  deliveryId,
1701
1664
  sessionKey,
1702
1665
  hasIncomingText: Boolean(incomingText),
1666
+ hasContextText: Boolean(contextText),
1703
1667
  });
1704
1668
  return { skipped: true, reason: 'missing_delivery_payload' };
1705
1669
  }
@@ -1712,48 +1676,51 @@ async function maybeBridgeRuntimeDelivery({
1712
1676
  const worldId = resolveDeliveryWorldId(delivery);
1713
1677
  const commandAuthorized = shouldAuthorizeBridgedCommand({
1714
1678
  runtimeConfig,
1715
- incomingText,
1679
+ incomingText: commandText || incomingText,
1716
1680
  });
1717
- const { Body, RawBody, CommandBody, ContextLines } = buildDeliveryInboundEnvelope({
1681
+ const inboundTimestamp = resolveBridgeDeliveryTimestampMs({ delivery, metadata });
1682
+ const { Body, RawBody, CommandBody, BodyForAgent, BodyForCommands, UntrustedContext } = buildDeliveryInboundEnvelope({
1718
1683
  runtime,
1719
1684
  currentCfg,
1720
- remoteIdentity: remoteAddress,
1685
+ remoteIdentity,
1721
1686
  incomingText,
1722
1687
  contextText,
1723
- commandText: incomingText,
1688
+ commandText,
1689
+ timestamp: inboundTimestamp,
1724
1690
  deliveryId,
1725
1691
  sessionKey,
1726
1692
  worldId,
1727
1693
  conversationKey: metadata.conversationKey || null,
1694
+ untrustedContext: payload.untrustedContext,
1728
1695
  });
1729
- const localAddress = resolveRelayAddress(runtimeConfig.accountId || '', runtimeConfig);
1696
+ const localIdentity = normalizeClaworldText(runtimeConfig.relay?.agentId, runtimeConfig.accountId);
1730
1697
  const inboundCtx = runtime.channel.reply.finalizeInboundContext({
1731
1698
  Body,
1732
1699
  RawBody,
1733
1700
  CommandBody,
1734
- BodyForAgent: RawBody,
1735
- BodyForCommands: CommandBody,
1736
- From: `claworld:${remoteRouteIdentity}`,
1737
- To: localAddress ? `claworld:${localAddress}` : `claworld:${runtimeConfig.accountId}`,
1701
+ BodyForAgent,
1702
+ BodyForCommands,
1703
+ From: `claworld:${remoteIdentity}`,
1704
+ To: `claworld:${localIdentity}`,
1738
1705
  SessionKey: sessionKey,
1739
1706
  AccountId: runtimeConfig.accountId,
1740
1707
  OriginatingChannel: 'claworld',
1741
- OriginatingFrom: remoteRouteIdentity,
1742
- OriginatingTo: remoteRouteIdentity,
1708
+ OriginatingFrom: remoteIdentity,
1709
+ OriginatingTo: remoteIdentity,
1743
1710
  ChatType: 'direct',
1744
- SenderName: remoteAddress,
1745
- SenderId: remoteRouteIdentity,
1711
+ SenderName: remoteIdentity,
1712
+ SenderId: remoteIdentity,
1746
1713
  MessageId: deliveryId,
1747
1714
  Provider: 'claworld',
1748
1715
  Surface: 'claworld',
1749
- ConversationLabel: remoteAddress,
1750
- Timestamp: Date.now(),
1716
+ ConversationLabel: remoteIdentity,
1717
+ Timestamp: inboundTimestamp,
1751
1718
  MessageSid: deliveryId,
1752
1719
  WasMentioned: false,
1753
1720
  CommandAuthorized: commandAuthorized,
1754
1721
  RelayDeliveryId: deliveryId,
1755
1722
  RelayFromAgentId: fromAgentId,
1756
- UntrustedContext: ContextLines,
1723
+ UntrustedContext,
1757
1724
  });
1758
1725
  const localAgentId = resolveBoundLocalAgentId({
1759
1726
  cfg: currentCfg,
@@ -1784,7 +1751,7 @@ async function maybeBridgeRuntimeDelivery({
1784
1751
  deliveryId,
1785
1752
  sessionKey,
1786
1753
  localAgentId,
1787
- remoteIdentity: remoteAddress,
1754
+ remoteIdentity,
1788
1755
  routeStatus: routed?.status || null,
1789
1756
  bodyPreview: String(Body || '').slice(0, 240),
1790
1757
  rawBodyPreview: String(RawBody || '').slice(0, 240),
@@ -1792,6 +1759,23 @@ async function maybeBridgeRuntimeDelivery({
1792
1759
  commandAuthorized,
1793
1760
  });
1794
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
+
1795
1779
  let {
1796
1780
  dispatchResult,
1797
1781
  replied,
@@ -1855,15 +1839,15 @@ async function maybeBridgeRuntimeDelivery({
1855
1839
  }));
1856
1840
  }
1857
1841
 
1858
- logger.info?.(`[claworld:${runtimeAccountId}] delivery bridge completed`, {
1859
- deliveryId,
1860
- sessionKey,
1861
- queuedFinal: Boolean(dispatchResult?.queuedFinal),
1862
- replied,
1863
- keptSilent,
1864
- routeStatus: routed?.status || null,
1865
- runtimeOutputSummary,
1866
- });
1842
+ logger.info?.(`[claworld:${runtimeAccountId}] delivery bridge completed`, {
1843
+ deliveryId,
1844
+ sessionKey,
1845
+ queuedFinal: Boolean(dispatchResult?.queuedFinal),
1846
+ replied,
1847
+ keptSilent,
1848
+ routeStatus: routed?.status || null,
1849
+ runtimeOutputSummary,
1850
+ });
1867
1851
 
1868
1852
  return {
1869
1853
  skipped: false,
@@ -2345,9 +2329,9 @@ export function createClaworldChannelPlugin({
2345
2329
  },
2346
2330
  agentPrompt: {
2347
2331
  messageToolHints: () => [
2348
- '- Claworld targets are canonical identity handles like `alice@robin`.',
2332
+ '- Claworld message targets are canonical `agentId` values such as `agt_xxx`.',
2349
2333
  '- Omit `target` to keep replying inside the current A2A session when the runtime already inferred the peer.',
2350
- '- 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.',
2351
2335
  ],
2352
2336
  },
2353
2337
  reload: { configPrefixes: ['channels.claworld'] },
@@ -2379,19 +2363,18 @@ export function createClaworldChannelPlugin({
2379
2363
  looksLikeId: (raw, normalized) => {
2380
2364
  const value = String(normalized || raw || '').trim();
2381
2365
  if (!value) return false;
2382
- if (value.includes('@')) return RELAY_CANONICAL_AGENT_CODE_PATTERN.test(value);
2383
- return /^[a-z0-9._:+~-]+$/i.test(value) || /^agt_[a-z0-9_-]+$/i.test(value);
2366
+ return /^agt_[a-z0-9_-]+$/i.test(value);
2384
2367
  },
2385
- hint: '<agent@relay-domain|agentId>',
2368
+ hint: '<agentId>',
2386
2369
  },
2387
2370
  },
2388
2371
  directory: {
2389
2372
  self: async ({ cfg, accountId } = {}) => {
2390
2373
  const account = inspectClaworldChannelAccount(cfg || {}, accountId || null);
2391
- const address = resolveRelayAddress(account?.accountId || '', account || {});
2392
- if (!account?.configured || !address) return null;
2374
+ const agentId = normalizeClaworldText(account?.relay?.agentId, null);
2375
+ if (!account?.configured || !agentId) return null;
2393
2376
  return {
2394
- id: address,
2377
+ id: agentId,
2395
2378
  name: account.accountId,
2396
2379
  handle: account.accountId,
2397
2380
  };
@@ -2474,8 +2457,6 @@ export function createClaworldChannelPlugin({
2474
2457
  fetchImpl,
2475
2458
  logger,
2476
2459
  agentId: context.agentId || context.targetAgentId || null,
2477
- agentCode: context.agentCode || context.targetAgentCode || null,
2478
- address: context.address || null,
2479
2460
  }),
2480
2461
  },
2481
2462
  social: {
@@ -2484,7 +2465,7 @@ export function createClaworldChannelPlugin({
2484
2465
  return createFriendRequest({
2485
2466
  runtimeConfig: resolvedContext.runtimeConfig,
2486
2467
  fromAgentId: resolvedContext.agentId || null,
2487
- toAddress: context.toAddress || null,
2468
+ targetAgentId: context.targetAgentId || null,
2488
2469
  message: context.message || null,
2489
2470
  metadata: context.metadata || {},
2490
2471
  fetchImpl,
@@ -2520,18 +2501,35 @@ export function createClaworldChannelPlugin({
2520
2501
  },
2521
2502
  requestChat: async (context = {}) => {
2522
2503
  const resolvedContext = await resolveBoundRuntimeContext(context);
2504
+ const requestContext = resolvedContext.requesterSessionKey
2505
+ ? {
2506
+ followUp: {
2507
+ sessionKey: resolvedContext.requesterSessionKey,
2508
+ },
2509
+ }
2510
+ : null;
2523
2511
  return createChatRequest({
2524
2512
  runtimeConfig: resolvedContext.runtimeConfig,
2525
2513
  fromAgentId: resolvedContext.agentId || null,
2526
2514
  targetAgentId: context.targetAgentId || null,
2527
2515
  openingMessage: context.openingMessage || context.message || context.text || null,
2528
2516
  worldId: context.worldId || null,
2517
+ requestContext,
2518
+ fetchImpl,
2519
+ });
2520
+ },
2521
+ listChatInbox: async (context = {}) => {
2522
+ const resolvedContext = await resolveBoundRuntimeContext(context);
2523
+ return listChatInbox({
2524
+ runtimeConfig: resolvedContext.runtimeConfig,
2525
+ agentId: resolvedContext.agentId || null,
2526
+ direction: context.direction || null,
2529
2527
  fetchImpl,
2530
2528
  });
2531
2529
  },
2532
2530
  listChatRequests: async (context = {}) => {
2533
2531
  const resolvedContext = await resolveBoundRuntimeContext(context);
2534
- return listChatRequests({
2532
+ return listChatInbox({
2535
2533
  runtimeConfig: resolvedContext.runtimeConfig,
2536
2534
  agentId: resolvedContext.agentId || null,
2537
2535
  direction: context.direction || null,
@@ -2557,6 +2555,25 @@ export function createClaworldChannelPlugin({
2557
2555
  });
2558
2556
  },
2559
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
+ },
2560
2577
  postSetup: {
2561
2578
  fetchWorldDirectory: async (context = {}) => {
2562
2579
  const resolvedContext = await resolveBoundRuntimeContext(context);
@@ -2665,7 +2682,7 @@ export function createClaworldChannelPlugin({
2665
2682
  agentId: resolvedContext.agentId || null,
2666
2683
  displayName: context.displayName || null,
2667
2684
  worldContextText: context.worldContextText || null,
2668
- enabled: context.enabled === true,
2685
+ enabled: typeof context.enabled === 'boolean' ? context.enabled : true,
2669
2686
  fetchImpl,
2670
2687
  logger,
2671
2688
  });
@@ -2707,6 +2724,25 @@ export function createClaworldChannelPlugin({
2707
2724
  results,
2708
2725
  demo,
2709
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
+ },
2710
2746
  fetchWorldDirectory: async (context = {}) => {
2711
2747
  const resolvedContext = await resolveBoundRuntimeContext(context);
2712
2748
  return fetchPostSetupWorldDirectory({
@@ -2847,14 +2883,14 @@ export function createClaworldChannelPlugin({
2847
2883
  moderation: {
2848
2884
  createWorld: async (context = {}) => {
2849
2885
  const resolvedContext = await resolveBoundRuntimeContext(context);
2850
- return createModeratedWorld({
2886
+ return createModeratedWorld({
2851
2887
  cfg: resolvedContext.cfg || {},
2852
2888
  accountId: resolvedContext.accountId || null,
2853
2889
  runtimeConfig: resolvedContext.runtimeConfig || null,
2854
2890
  agentId: resolvedContext.agentId || null,
2855
2891
  displayName: context.displayName || null,
2856
2892
  worldContextText: context.worldContextText || null,
2857
- enabled: context.enabled === true,
2893
+ enabled: typeof context.enabled === 'boolean' ? context.enabled : true,
2858
2894
  fetchImpl,
2859
2895
  logger,
2860
2896
  });