@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
@@ -0,0 +1,91 @@
1
+ function normalizeText(value, fallback = null) {
2
+ if (value == null) return fallback;
3
+ const normalized = String(value).trim();
4
+ return normalized || fallback;
5
+ }
6
+
7
+ function normalizeFieldError(fieldError = {}) {
8
+ const fieldId = normalizeText(fieldError.fieldId, null);
9
+ const message = normalizeText(fieldError.message, null);
10
+ const code = normalizeText(fieldError.code, null);
11
+ if (!fieldId && !message && !code) return null;
12
+ return {
13
+ ...(fieldId ? { fieldId } : {}),
14
+ ...(message ? { message } : {}),
15
+ ...(code ? { code } : {}),
16
+ };
17
+ }
18
+
19
+ function normalizeMissingField(field = {}) {
20
+ const fieldId = normalizeText(field.fieldId, null);
21
+ const label = normalizeText(field.label, null);
22
+ const description = normalizeText(field.description, null);
23
+ const message = normalizeText(field.message, null);
24
+ const code = normalizeText(field.code, null);
25
+ if (!fieldId && !label && !description && !message && !code) return null;
26
+ return {
27
+ ...(fieldId ? { fieldId } : {}),
28
+ ...(label ? { label } : {}),
29
+ ...(description ? { description } : {}),
30
+ ...(message ? { message } : {}),
31
+ ...(code ? { code } : {}),
32
+ };
33
+ }
34
+
35
+ function normalizePublicIdentity(publicIdentity = {}) {
36
+ const status = normalizeText(publicIdentity.status, null);
37
+ const displayName = normalizeText(publicIdentity.displayName, null);
38
+ const code = normalizeText(publicIdentity.code, null);
39
+ const displayIdentity = normalizeText(publicIdentity.displayIdentity, null);
40
+ const confirmedAt = normalizeText(publicIdentity.confirmedAt, null);
41
+ const updatedAt = normalizeText(publicIdentity.updatedAt, null);
42
+ if (!status && !displayName && !code && !displayIdentity && !confirmedAt && !updatedAt) return null;
43
+ return {
44
+ ...(status ? { status } : {}),
45
+ ...(displayName ? { displayName } : {}),
46
+ ...(code ? { code } : {}),
47
+ ...(displayIdentity ? { displayIdentity } : {}),
48
+ ...(confirmedAt ? { confirmedAt } : {}),
49
+ ...(updatedAt ? { updatedAt } : {}),
50
+ };
51
+ }
52
+
53
+ export function normalizeBackendFieldError(fieldError = {}) {
54
+ return normalizeFieldError(fieldError);
55
+ }
56
+
57
+ export function normalizeBackendMissingField(field = {}) {
58
+ return normalizeMissingField(field);
59
+ }
60
+
61
+ export function normalizeBackendPublicIdentity(publicIdentity = {}) {
62
+ return normalizePublicIdentity(publicIdentity);
63
+ }
64
+
65
+ export function extractBackendErrorContext(payload = {}) {
66
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return {};
67
+
68
+ const backendCode = normalizeText(payload.error, null);
69
+ const backendMessage = normalizeText(payload.message, null);
70
+ const requiredAction = normalizeText(payload.requiredAction, null);
71
+ const nextAction = normalizeText(payload.nextAction, null);
72
+ const nextTool = normalizeText(payload.nextTool, null);
73
+ const fieldErrors = Array.isArray(payload.fieldErrors)
74
+ ? payload.fieldErrors.map((fieldError) => normalizeFieldError(fieldError)).filter(Boolean)
75
+ : [];
76
+ const missingFields = Array.isArray(payload.missingFields)
77
+ ? payload.missingFields.map((field) => normalizeMissingField(field)).filter(Boolean)
78
+ : [];
79
+ const publicIdentity = normalizePublicIdentity(payload.publicIdentity);
80
+
81
+ return {
82
+ ...(backendCode ? { backendCode } : {}),
83
+ ...(backendMessage ? { backendMessage } : {}),
84
+ ...(requiredAction ? { requiredAction } : {}),
85
+ ...(nextAction ? { nextAction } : {}),
86
+ ...(nextTool ? { nextTool } : {}),
87
+ ...(fieldErrors.length > 0 ? { fieldErrors } : {}),
88
+ ...(missingFields.length > 0 ? { missingFields } : {}),
89
+ ...(publicIdentity ? { publicIdentity } : {}),
90
+ };
91
+ }
@@ -114,7 +114,6 @@ export async function submitFeedbackReport({
114
114
  turnId: normalizeText(normalizedContext.turnId, null),
115
115
  deliveryId: normalizeText(normalizedContext.deliveryId, null),
116
116
  targetAgentId: normalizeText(normalizedContext.targetAgentId, null),
117
- targetAgentCode: normalizeText(normalizedContext.targetAgentCode, null),
118
117
  tags: normalizeStringList(normalizedContext.tags),
119
118
  metadata: normalizeObject(normalizedContext.metadata),
120
119
  },
@@ -128,7 +127,7 @@ export async function submitFeedbackReport({
128
127
  accountId: normalizeText(resolvedRuntimeConfig.accountId, normalizeText(accountId, null)),
129
128
  serverUrl: baseUrl,
130
129
  relayAgentId: resolvedAgentId,
131
- defaultToAddress: normalizeText(resolvedRuntimeConfig.relay?.defaultToAddress, null),
130
+ defaultTargetAgentId: normalizeText(resolvedRuntimeConfig.relay?.defaultTargetAgentId, null),
132
131
  },
133
132
  }),
134
133
  });
@@ -1,6 +1,7 @@
1
1
  import { resolveClaworldRuntimeConfig } from '../plugin/config-schema.js';
2
2
  import { buildRuntimeAuthHeaders } from '../plugin/account-identity.js';
3
3
  import { createRuntimeBoundaryError } from '../../lib/runtime-errors.js';
4
+ import { extractBackendErrorContext } from './backend-error-context.js';
4
5
  import {
5
6
  buildCandidateDeliverySummary as buildBackendCandidateDeliverySummary,
6
7
  buildWorldSelectionPrompt as buildBackendWorldSelectionPrompt,
@@ -337,6 +338,15 @@ function normalizeCandidate(candidate = {}, index = 0) {
337
338
  ? candidate.compatibilitySignals.map((signal, signalIndex) => normalizeCompatibilitySignal(signal, signalIndex))
338
339
  : [],
339
340
  deliveryReason: normalizeDeliveryReason(candidate.deliveryReason),
341
+ worldFeedbackSummary: candidate.worldFeedbackSummary && typeof candidate.worldFeedbackSummary === 'object'
342
+ ? {
343
+ likesReceived: normalizeInteger(candidate.worldFeedbackSummary.likesReceived, 0),
344
+ dislikesReceived: normalizeInteger(candidate.worldFeedbackSummary.dislikesReceived, 0),
345
+ }
346
+ : {
347
+ likesReceived: 0,
348
+ dislikesReceived: 0,
349
+ },
340
350
  expiresAt: normalizeText(candidate.expiresAt, null),
341
351
  joinedAt: normalizeText(candidate.joinedAt, null),
342
352
  rank: normalizedRank == null ? null : Math.max(1, Math.trunc(normalizedRank)),
@@ -437,7 +447,6 @@ function normalizeWorldSearchItem(item = {}, index = 0) {
437
447
  worldId: normalizeText(item.worldId, 'unknown-world'),
438
448
  displayName: normalizeText(item.displayName, `Player ${index + 1}`),
439
449
  headline: normalizeText(item.headline, null),
440
- address: normalizeText(item.address, null),
441
450
  online: item.online === true,
442
451
  connectedAt: normalizeText(item.connectedAt, null),
443
452
  lastHeartbeatAt: normalizeText(item.lastHeartbeatAt, null),
@@ -450,6 +459,15 @@ function normalizeWorldSearchItem(item = {}, index = 0) {
450
459
  reasonSummary: normalizeText(item.reasonSummary, ''),
451
460
  joinedAt: normalizeText(item.joinedAt, null),
452
461
  profileSummary: normalizeCandidateProfileSummary(item.profileSummary),
462
+ worldFeedbackSummary: item.worldFeedbackSummary && typeof item.worldFeedbackSummary === 'object'
463
+ ? {
464
+ likesReceived: normalizeInteger(item.worldFeedbackSummary.likesReceived, 0),
465
+ dislikesReceived: normalizeInteger(item.worldFeedbackSummary.dislikesReceived, 0),
466
+ }
467
+ : {
468
+ likesReceived: 0,
469
+ dislikesReceived: 0,
470
+ },
453
471
  };
454
472
  }
455
473
 
@@ -686,8 +704,7 @@ function createProductShellHttpError(action, response, { accountId = null, world
686
704
  accountId,
687
705
  ...(worldId ? { worldId } : {}),
688
706
  httpStatus: response?.status ?? 500,
689
- backendCode,
690
- backendMessage,
707
+ ...extractBackendErrorContext(response?.body),
691
708
  },
692
709
  });
693
710
  }
@@ -776,7 +793,10 @@ export async function fetchWorldDetail({
776
793
  accountId: resolvedRuntimeConfig.accountId || accountId || null,
777
794
  body: detail.body,
778
795
  });
779
- throw new Error(`claworld product-shell world detail fetch failed: ${detail.status}`);
796
+ throw createProductShellHttpError('world_detail', detail, {
797
+ accountId: resolvedRuntimeConfig.accountId || accountId || null,
798
+ worldId: resolvedWorldId,
799
+ });
780
800
  }
781
801
 
782
802
  return normalizeWorldDetail(detail.body);
@@ -830,7 +850,10 @@ export async function joinWorld({
830
850
  accountId: resolvedRuntimeConfig.accountId || accountId || null,
831
851
  body: joinResult.body,
832
852
  });
833
- throw new Error(`claworld product-shell world join failed: ${joinResult.status}`);
853
+ throw createProductShellHttpError('world_join', joinResult, {
854
+ accountId: resolvedRuntimeConfig.accountId || accountId || null,
855
+ worldId: resolvedWorldId,
856
+ });
834
857
  }
835
858
 
836
859
  return normalizeWorldJoinResponse(joinResult.body, {
@@ -890,7 +913,10 @@ export async function fetchWorldSearch({
890
913
  accountId: resolvedRuntimeConfig.accountId || accountId || null,
891
914
  body: searchResult.body,
892
915
  });
893
- throw new Error(`claworld product-shell world search failed: ${searchResult.status}`);
916
+ throw createProductShellHttpError('world_search', searchResult, {
917
+ accountId: resolvedRuntimeConfig.accountId || accountId || null,
918
+ worldId: resolvedWorldId,
919
+ });
894
920
  }
895
921
 
896
922
  return normalizeWorldSearchResponse(searchResult.body, {
@@ -950,7 +976,10 @@ export async function submitWorldSearch({
950
976
  accountId: resolvedRuntimeConfig.accountId || accountId || null,
951
977
  body: searchResult.body,
952
978
  });
953
- throw new Error(`claworld product-shell world search failed: ${searchResult.status}`);
979
+ throw createProductShellHttpError('world_search', searchResult, {
980
+ accountId: resolvedRuntimeConfig.accountId || accountId || null,
981
+ worldId: resolvedWorldId,
982
+ });
954
983
  }
955
984
 
956
985
  if (searchResult.status === 409) {
@@ -1095,7 +1124,10 @@ export async function fetchWorldCandidateFeed({
1095
1124
  accountId: resolvedRuntimeConfig.accountId || accountId || null,
1096
1125
  body: candidateFeed.body,
1097
1126
  });
1098
- throw new Error(`claworld product-shell candidate feed failed: ${candidateFeed.status}`);
1127
+ throw createProductShellHttpError('world_candidate_feed', candidateFeed, {
1128
+ accountId: resolvedRuntimeConfig.accountId || accountId || null,
1129
+ worldId: resolvedWorldId,
1130
+ });
1099
1131
  }
1100
1132
 
1101
1133
  return normalizeCandidateFeedResponse(candidateFeed.body, {
@@ -1136,7 +1168,9 @@ export async function resolveWorldSelectionFlow({
1136
1168
  accountId: resolvedRuntimeConfig.accountId || accountId || null,
1137
1169
  body: worlds.body,
1138
1170
  });
1139
- throw new Error(`claworld product-shell world fetch failed: ${worlds.status}`);
1171
+ throw createProductShellHttpError('world_directory', worlds, {
1172
+ accountId: resolvedRuntimeConfig.accountId || accountId || null,
1173
+ });
1140
1174
  }
1141
1175
 
1142
1176
  return buildPostSetupWorldDirectory(worlds.body, {
@@ -85,6 +85,26 @@ function projectWorldStats(stats = null) {
85
85
  totalParticipants: normalizeOptionalInteger(stats.totalParticipants, null),
86
86
  activeParticipants: normalizeOptionalInteger(stats.activeParticipants, null),
87
87
  totalConversationCount: normalizeOptionalInteger(stats.totalConversationCount, null),
88
+ totalLikes: normalizeOptionalInteger(stats.totalLikes, null),
89
+ totalDislikes: normalizeOptionalInteger(stats.totalDislikes, null),
90
+ };
91
+ }
92
+
93
+ function projectWorldFeedbackSummary(summary = null) {
94
+ if (!summary || typeof summary !== 'object' || Array.isArray(summary)) return null;
95
+ return {
96
+ likesReceived: normalizeInteger(summary.likesReceived, 0),
97
+ dislikesReceived: normalizeInteger(summary.dislikesReceived, 0),
98
+ };
99
+ }
100
+
101
+ function projectConversationFeedbackSummary(summary = null) {
102
+ if (!summary || typeof summary !== 'object' || Array.isArray(summary)) return null;
103
+ return {
104
+ likeCount: normalizeInteger(summary.likeCount, 0),
105
+ dislikeCount: normalizeInteger(summary.dislikeCount, 0),
106
+ viewerGave: normalizeText(summary.viewerGave, null),
107
+ viewerReceived: normalizeText(summary.viewerReceived, null),
88
108
  };
89
109
  }
90
110
 
@@ -254,6 +274,7 @@ function projectToolCandidateSummary(summary = {}, index = 0) {
254
274
  score: normalizeInteger(summary.score, 0) || null,
255
275
  summary: normalizeText(summary.summary, null),
256
276
  expiresAt: normalizeText(summary.expiresAt, null),
277
+ worldFeedbackSummary: projectWorldFeedbackSummary(summary.worldFeedbackSummary),
257
278
  };
258
279
  }
259
280
 
@@ -277,6 +298,7 @@ function projectToolCandidateFeed(joinResult = {}) {
277
298
  score: candidate.score,
278
299
  summary: normalizeText(candidate.deliveryReason?.summary, null),
279
300
  expiresAt: candidate.expiresAt,
301
+ worldFeedbackSummary: candidate.worldFeedbackSummary,
280
302
  }, index))
281
303
  : [];
282
304
 
@@ -354,6 +376,7 @@ export function projectToolSearchWorldResponse(searchResult = {}, { accountId =
354
376
  matchReasons: projectSearchMatchReasons(item),
355
377
  profileSnippet: summarizeProfileSnippet(item.profileSummary),
356
378
  online: item.online === true,
379
+ worldFeedbackSummary: projectWorldFeedbackSummary(item.worldFeedbackSummary),
357
380
  }))
358
381
  : [];
359
382
 
@@ -482,7 +505,7 @@ export function projectToolFeedbackSubmissionResponse(result = {}) {
482
505
  title: normalizeText(feedback.title, null),
483
506
  accountId: normalizeText(feedback.accountId, null),
484
507
  reporterAgentId: normalizeText(reporter.agentId, null),
485
- reporterAgentCode: normalizeText(reporter.agentCode, null),
508
+ reporterIdentity: normalizeText(reporter.publicIdentity?.displayIdentity, null),
486
509
  worldId: normalizeText(context.worldId, null),
487
510
  conversationKey: normalizeText(context.conversationKey, null),
488
511
  turnId: normalizeText(context.turnId, null),
@@ -503,9 +526,8 @@ function projectToolAgentSummary(agent = {}) {
503
526
  if (!agent || typeof agent !== 'object') return null;
504
527
  return {
505
528
  agentId: normalizeText(agent.agentId, null),
506
- agentCode: normalizeText(agent.agentCode, null),
507
- address: normalizeText(agent.address, null),
508
529
  displayName: normalizeText(agent.displayName, null),
530
+ identity: normalizeText(agent.publicIdentity?.displayIdentity, null),
509
531
  online: agent.online === true,
510
532
  discoverable: typeof agent.discoverable === 'boolean' ? agent.discoverable : null,
511
533
  contactable: typeof agent.contactable === 'boolean' ? agent.contactable : null,
@@ -635,6 +657,7 @@ function projectChatInboxChatItem(chat = {}) {
635
657
  localSessionKey: normalizeText(chat.localSessionKey, normalizeText(chat.sessionKey, null)),
636
658
  counterparty: projectToolAgentSummary(chat.counterparty),
637
659
  conversation: normalizeConversationScopeDetails(chat.conversation),
660
+ feedbackSummary: projectConversationFeedbackSummary(chat.feedbackSummary),
638
661
  };
639
662
  }
640
663
 
@@ -11,6 +11,11 @@ export const CLAWORLD_BOOTSTRAP_TOOL_NAMES = Object.freeze([
11
11
  'claworld_pair_agent',
12
12
  ]);
13
13
 
14
+ export const CLAWORLD_PROFILE_TOOL_NAMES = Object.freeze([
15
+ 'claworld_get_public_identity',
16
+ 'claworld_update_public_identity',
17
+ ]);
18
+
14
19
  export const CLAWORLD_FEEDBACK_TOOL_NAMES = Object.freeze([
15
20
  'claworld_submit_feedback',
16
21
  ]);
@@ -43,6 +48,7 @@ export const CLAWORLD_RETIRED_PUBLIC_TOOL_NAMES = Object.freeze([
43
48
 
44
49
  export const CLAWORLD_REGISTERED_TOOL_NAMES = Object.freeze([
45
50
  ...CLAWORLD_BOOTSTRAP_TOOL_NAMES,
51
+ ...CLAWORLD_PROFILE_TOOL_NAMES,
46
52
  ...CLAWORLD_WORLD_TOOL_NAMES,
47
53
  ...CLAWORLD_WORLD_ADMIN_PUBLIC_TOOL_NAMES,
48
54
  ...CLAWORLD_CHAT_REQUEST_TOOL_NAMES,
@@ -67,6 +73,7 @@ export const CLAWORLD_READ_ONLY_OPENCLAW_TOOL_NAMES = Object.freeze([
67
73
 
68
74
  export const CLAWORLD_PLUGIN_SMOKE_REQUIRED_TOOL_NAMES = Object.freeze([
69
75
  ...CLAWORLD_BOOTSTRAP_TOOL_NAMES,
76
+ ...CLAWORLD_PROFILE_TOOL_NAMES,
70
77
  ...CLAWORLD_WORLD_TOOL_NAMES,
71
78
  ...CLAWORLD_WORLD_ADMIN_PUBLIC_TOOL_NAMES,
72
79
  ...CLAWORLD_CHAT_REQUEST_TOOL_NAMES,
@@ -1,6 +1,7 @@
1
1
  import { resolveClaworldRuntimeConfig } from '../plugin/config-schema.js';
2
2
  import { buildRuntimeAuthHeaders } from '../plugin/account-identity.js';
3
3
  import { createRuntimeBoundaryError } from '../../lib/runtime-errors.js';
4
+ import { extractBackendErrorContext } from './backend-error-context.js';
4
5
 
5
6
  function normalizeText(value, fallback = null) {
6
7
  if (value == null) return fallback;
@@ -36,18 +37,6 @@ function normalizeStringList(values = []) {
36
37
  return [...new Set(values.map((value) => normalizeText(value, null)).filter(Boolean))];
37
38
  }
38
39
 
39
- function normalizeFieldError(fieldError = {}) {
40
- const fieldId = normalizeText(fieldError.fieldId, null);
41
- const message = normalizeText(fieldError.message, null);
42
- const code = normalizeText(fieldError.code, null);
43
- if (!fieldId && !message && !code) return null;
44
- return {
45
- ...(fieldId ? { fieldId } : {}),
46
- ...(message ? { message } : {}),
47
- ...(code ? { code } : {}),
48
- };
49
- }
50
-
51
40
  function normalizeParticipantContextField(field = {}, index = 0) {
52
41
  return {
53
42
  fieldId: normalizeText(field.fieldId, `field_${index + 1}`),
@@ -160,9 +149,6 @@ function inferHttpErrorCategory(status) {
160
149
  function createModerationHttpError(action, response, { accountId = null, worldId = null } = {}) {
161
150
  const backendCode = normalizeText(response?.body?.error, null);
162
151
  const backendMessage = normalizeText(response?.body?.message, `claworld world ${action} failed`);
163
- const fieldErrors = Array.isArray(response?.body?.fieldErrors)
164
- ? response.body.fieldErrors.map((fieldError) => normalizeFieldError(fieldError)).filter(Boolean)
165
- : [];
166
152
 
167
153
  return createRuntimeBoundaryError({
168
154
  code: backendCode || `claworld_world_${action}_failed`,
@@ -176,9 +162,7 @@ function createModerationHttpError(action, response, { accountId = null, worldId
176
162
  accountId,
177
163
  ...(worldId ? { worldId } : {}),
178
164
  httpStatus: response?.status ?? 500,
179
- backendCode,
180
- backendMessage,
181
- ...(fieldErrors.length > 0 ? { fieldErrors } : {}),
165
+ ...extractBackendErrorContext(response?.body),
182
166
  },
183
167
  });
184
168
  }
@@ -190,7 +174,7 @@ export async function createModeratedWorld({
190
174
  agentId = null,
191
175
  displayName = null,
192
176
  worldContextText = null,
193
- enabled = false,
177
+ enabled = true,
194
178
  fetchImpl,
195
179
  logger = console,
196
180
  } = {}) {
@@ -12,9 +12,15 @@ export const CANDIDATE_OBJECT_FIELDS = Object.freeze([
12
12
  'profileSummary',
13
13
  'compatibilitySignals',
14
14
  'deliveryReason',
15
+ 'worldFeedbackSummary',
15
16
  'expiresAt',
16
17
  ]);
17
18
 
19
+ export const WORLD_FEEDBACK_SUMMARY_FIELDS = Object.freeze([
20
+ 'likesReceived',
21
+ 'dislikesReceived',
22
+ ]);
23
+
18
24
  export const DATING_DEMO_SCORING_FIELDS = Object.freeze([
19
25
  'joinedAt',
20
26
  'rank',
@@ -282,6 +288,7 @@ export function projectCandidateFeedModel(world) {
282
288
  profileSummaryFieldShape: PROFILE_SUMMARY_FIELD_FIELDS,
283
289
  compatibilitySignalFields: COMPATIBILITY_SIGNAL_FIELDS,
284
290
  deliveryReasonFields: DELIVERY_REASON_FIELDS,
291
+ worldFeedbackSummaryFields: WORLD_FEEDBACK_SUMMARY_FIELDS,
285
292
  requestChatAction: {
286
293
  action: 'request_chat',
287
294
  requiredFields: ['worldId', 'targetAgentId'],
@@ -124,7 +124,6 @@ function normalizeSearchSchema(searchSchema = {}, joinSchema = {}) {
124
124
  'worldId',
125
125
  'displayName',
126
126
  'headline',
127
- 'address',
128
127
  'online',
129
128
  'matchedFieldIds',
130
129
  'score',
@@ -390,7 +390,12 @@ function summarizeProfileFields(fields = []) {
390
390
  }
391
391
 
392
392
  function buildCandidateDeliverySummaryLine(candidateSummary = {}, index = 0) {
393
- return `${index + 1}. ${candidateSummary.summary}`;
393
+ const likesReceived = Number(candidateSummary.worldFeedbackSummary?.likesReceived || 0);
394
+ const dislikesReceived = Number(candidateSummary.worldFeedbackSummary?.dislikesReceived || 0);
395
+ const feedbackLine = likesReceived > 0 || dislikesReceived > 0
396
+ ? ` World feedback in this world: ${likesReceived} like${likesReceived === 1 ? '' : 's'}, ${dislikesReceived} dislike${dislikesReceived === 1 ? '' : 's'}.`
397
+ : '';
398
+ return `${index + 1}. ${candidateSummary.summary}${feedbackLine}`;
394
399
  }
395
400
 
396
401
  function formatConversationOverview(detail = {}) {
@@ -697,6 +702,10 @@ export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail
697
702
  optionalFieldSummary,
698
703
  compatibilitySummary,
699
704
  deliveryReasonSummary: deliveryReasonSummary || null,
705
+ worldFeedbackSummary: candidate.worldFeedbackSummary || {
706
+ likesReceived: 0,
707
+ dislikesReceived: 0,
708
+ },
700
709
  expiresAt: candidate.expiresAt,
701
710
  summary,
702
711
  };