@xfxstudio/claworld 2026.4.14-testing.1 → 2026.4.16-testing.1

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 (47) hide show
  1. package/openclaw.plugin.json +1 -1
  2. package/package.json +1 -1
  3. package/index.js +0 -50
  4. package/setup-entry.js +0 -6
  5. package/skills/claworld-a2a-channel-agent/SKILL.md +0 -218
  6. package/skills/claworld-help/SKILL.md +0 -304
  7. package/skills/claworld-join-and-chat/SKILL.md +0 -515
  8. package/skills/claworld-manage-worlds/SKILL.md +0 -283
  9. package/skills/claworld-manage-worlds/references/world-context-templates.md +0 -145
  10. package/src/lib/chat-request.js +0 -366
  11. package/src/lib/public-identity.js +0 -175
  12. package/src/lib/relay/agent-readable-markdown.js +0 -385
  13. package/src/lib/relay/kickoff-progress.js +0 -162
  14. package/src/lib/relay/kickoff-text.js +0 -191
  15. package/src/lib/relay/shared.js +0 -30
  16. package/src/lib/runtime-errors.js +0 -149
  17. package/src/openclaw/index.js +0 -51
  18. package/src/openclaw/plugin/account-identity.js +0 -73
  19. package/src/openclaw/plugin/claworld-channel-plugin.js +0 -3483
  20. package/src/openclaw/plugin/config-schema.js +0 -392
  21. package/src/openclaw/plugin/lifecycle.js +0 -114
  22. package/src/openclaw/plugin/managed-config.js +0 -1054
  23. package/src/openclaw/plugin/onboarding.js +0 -312
  24. package/src/openclaw/plugin/register-tooling.js +0 -728
  25. package/src/openclaw/plugin/register.js +0 -1609
  26. package/src/openclaw/plugin/relay-client-shared.js +0 -146
  27. package/src/openclaw/plugin/relay-client.js +0 -1469
  28. package/src/openclaw/plugin/runtime-backup.js +0 -105
  29. package/src/openclaw/plugin/runtime.js +0 -12
  30. package/src/openclaw/plugin-version.js +0 -67
  31. package/src/openclaw/protocol/relay-event-protocol.js +0 -43
  32. package/src/openclaw/runtime/backend-error-context.js +0 -91
  33. package/src/openclaw/runtime/canonical-result-builder.js +0 -126
  34. package/src/openclaw/runtime/demo-session-bootstrap.js +0 -32
  35. package/src/openclaw/runtime/feedback-helper.js +0 -145
  36. package/src/openclaw/runtime/inbound-session-router.js +0 -44
  37. package/src/openclaw/runtime/outbound-session-bridge.js +0 -29
  38. package/src/openclaw/runtime/product-shell-helper.js +0 -931
  39. package/src/openclaw/runtime/runtime-path.js +0 -19
  40. package/src/openclaw/runtime/system-message-orchestrator.js +0 -1
  41. package/src/openclaw/runtime/tool-contracts.js +0 -939
  42. package/src/openclaw/runtime/tool-inventory.js +0 -83
  43. package/src/openclaw/runtime/world-membership-helper.js +0 -320
  44. package/src/openclaw/runtime/world-moderation-helper.js +0 -508
  45. package/src/product-shell/contracts/chat-request-approval-policy.js +0 -93
  46. package/src/product-shell/contracts/world-orchestration.js +0 -734
  47. package/src/product-shell/orchestration/world-conversation-text.js +0 -229
@@ -1,931 +0,0 @@
1
- import { resolveClaworldRuntimeConfig } from '../plugin/config-schema.js';
2
- import { buildRuntimeAuthHeaders } from '../plugin/account-identity.js';
3
- import { createRuntimeBoundaryError } from '../../lib/runtime-errors.js';
4
- import { extractBackendErrorContext } from './backend-error-context.js';
5
- import {
6
- buildCandidateDeliverySummary as buildBackendCandidateDeliverySummary,
7
- resolveWorldSelection as resolveBackendWorldSelection,
8
- } from '../../product-shell/contracts/world-orchestration.js';
9
-
10
- function normalizeText(value, fallback = null) {
11
- if (value == null) return fallback;
12
- const normalized = String(value).trim();
13
- return normalized || fallback;
14
- }
15
-
16
- function normalizeInteger(value, fallback = 0) {
17
- const parsed = Number(value);
18
- if (!Number.isFinite(parsed)) return fallback;
19
- return Math.max(0, Math.trunc(parsed));
20
- }
21
-
22
- function normalizeStringList(values = []) {
23
- if (!Array.isArray(values)) return [];
24
- return [...new Set(values.map((value) => normalizeText(value, null)).filter(Boolean))];
25
- }
26
-
27
- function normalizeWorldRole(worldRole, fallback = null) {
28
- const normalized = normalizeText(worldRole, fallback);
29
- return ['owner', 'member'].includes(normalized) ? normalized : fallback;
30
- }
31
-
32
- function sentenceCase(value, fallback = '') {
33
- const normalized = normalizeText(value, fallback);
34
- if (!normalized) return fallback;
35
- return /[.!?]$/.test(normalized) ? normalized : `${normalized}.`;
36
- }
37
-
38
- function quoteExample(example) {
39
- return `"${String(example).trim()}"`;
40
- }
41
-
42
- function joinAsNaturalLanguage(values = []) {
43
- const items = values.filter(Boolean);
44
- if (items.length === 0) return '';
45
- if (items.length === 1) return items[0];
46
- if (items.length === 2) return `${items[0]} and ${items[1]}`;
47
- return `${items.slice(0, -1).join(', ')}, and ${items.at(-1)}`;
48
- }
49
-
50
- function normalizeWorldSummary(world = {}) {
51
- const summary = world.agentSummary && typeof world.agentSummary === 'object' ? world.agentSummary : world;
52
- const rawWorldId = world.worldId || summary.worldId;
53
-
54
- return {
55
- worldId: normalizeText(rawWorldId, 'unknown-world'),
56
- displayName: normalizeText(summary.displayName || world.displayName, normalizeText(rawWorldId, 'Unknown World')),
57
- summary: normalizeText(summary.summary || world.summary, null),
58
- worldContextText: normalizeText(summary.worldContextText || world.worldContextText, ''),
59
- hotness: normalizeInteger(summary.hotness || world.hotness || world.activatedMemberCount, 0),
60
- activatedMemberCount: normalizeInteger(summary.activatedMemberCount || world.activatedMemberCount || summary.hotness || world.hotness, 0),
61
- tags: normalizeStringList(summary.tags || world.tags),
62
- matchScore: normalizeInteger(summary.matchScore || world.matchScore, 0),
63
- matchedFieldIds: normalizeStringList(summary.matchedFieldIds || world.matchedFieldIds),
64
- matchedTerms: normalizeStringList(summary.matchedTerms || world.matchedTerms),
65
- reasonSummary: normalizeText(summary.reasonSummary || world.reasonSummary, null),
66
- requiredFieldCount: normalizeInteger(summary.requiredFieldCount || world.requiredFieldCount, 0),
67
- detailAction: world.detailAction && typeof world.detailAction === 'object' ? world.detailAction : null,
68
- joinAction: world.joinAction && typeof world.joinAction === 'object' ? world.joinAction : null,
69
- };
70
- }
71
-
72
- function normalizeField(field = {}, index = 0, { required = false } = {}) {
73
- const fieldId = normalizeText(field.fieldId || field.id, `field_${index + 1}`);
74
- return {
75
- fieldId,
76
- label: normalizeText(field.label, fieldId),
77
- type: normalizeText(field.type, 'string'),
78
- source: normalizeText(field.source, 'profile'),
79
- required: field.required === true || required,
80
- description: normalizeText(field.description, null),
81
- examples: normalizeStringList(field.examples),
82
- constraints: field.constraints && typeof field.constraints === 'object' ? field.constraints : {},
83
- };
84
- }
85
-
86
- function buildParticipantContextField(field = null) {
87
- if (field && typeof field === 'object' && !Array.isArray(field)) {
88
- return normalizeField(field, 0, { required: true });
89
- }
90
- return normalizeField({
91
- fieldId: 'participantContextText',
92
- label: 'Entry Profile',
93
- type: 'string',
94
- source: 'membership',
95
- required: true,
96
- description: 'A short text describing who you are in this world and what context you bring into it.',
97
- examples: [],
98
- constraints: {},
99
- }, 0, { required: true });
100
- }
101
-
102
- function normalizeSearchSchema(payload = {}, { worldId = null, fallbackFields = [] } = {}) {
103
- const rawInputFields = Array.isArray(payload.inputFields) && payload.inputFields.length > 0
104
- ? payload.inputFields
105
- : fallbackFields;
106
- const inputFields = rawInputFields.map((field, index) => normalizeField(field, index, { required: false }));
107
- const inputFieldIds = normalizeStringList(
108
- Array.isArray(payload.inputFieldIds)
109
- ? payload.inputFieldIds
110
- : inputFields.map((field) => field.fieldId),
111
- );
112
-
113
- return {
114
- modelId: normalizeText(payload.modelId, worldId ? `${worldId}.search.v1` : 'world.search.v1'),
115
- worldId: normalizeText(payload.worldId, worldId || 'unknown-world'),
116
- mode: normalizeText(payload.mode, 'membership_profile_search'),
117
- previewRoute: normalizeText(payload.previewRoute, worldId ? `/v1/worlds/${worldId}/search` : '/v1/worlds/:worldId/search'),
118
- inputFieldIds,
119
- inputFields,
120
- resultFields: normalizeStringList(payload.resultFields),
121
- viewerRequirement: normalizeText(payload.viewerRequirement, 'active_membership'),
122
- onlineOnly: payload.onlineOnly !== false,
123
- defaultLimit: normalizeInteger(payload.defaultLimit, 10),
124
- summary: normalizeText(payload.summary, ''),
125
- hints: normalizeStringList(payload.hints),
126
- status: normalizeText(payload.status, 'phase1_world_search'),
127
- };
128
- }
129
-
130
- function normalizeWorldDetail(payload = {}) {
131
- if (Array.isArray(payload.requiredFields) || Array.isArray(payload.optionalFields)) {
132
- const requiredFields = Array.isArray(payload.requiredFields)
133
- ? payload.requiredFields.map((field, index) => normalizeField(field, index, { required: true }))
134
- : [];
135
- const optionalFields = Array.isArray(payload.optionalFields)
136
- ? payload.optionalFields.map((field, index) => normalizeField(field, index, { required: false }))
137
- : [];
138
- const normalizedWorldId = normalizeText(payload.worldId, 'unknown-world');
139
-
140
- return {
141
- status: normalizeText(payload.status, 'ready'),
142
- source: normalizeText(payload.source, 'product_shell'),
143
- worldId: normalizedWorldId,
144
- displayName: normalizeText(payload.displayName, normalizedWorldId),
145
- worldContextText: normalizeText(payload.worldContextText, ''),
146
- ownerAgentId: normalizeText(payload.ownerAgentId, null),
147
- worldRole: normalizeWorldRole(payload.worldRole, null),
148
- enabled: typeof payload.enabled === 'boolean' ? payload.enabled : null,
149
- requiredFieldCount: normalizeInteger(payload.requiredFieldCount, requiredFields.length) || requiredFields.length,
150
- optionalFieldCount: normalizeInteger(payload.optionalFieldCount, optionalFields.length) || optionalFields.length,
151
- requiredFields,
152
- optionalFields,
153
- hints: normalizeStringList(payload.hints),
154
- nextAction: normalizeText(payload.nextAction, 'call_join_world'),
155
- searchSchema: normalizeSearchSchema(payload.searchSchema || {}, {
156
- worldId: normalizedWorldId,
157
- fallbackFields: requiredFields,
158
- }),
159
- };
160
- }
161
-
162
- const world = payload.world && typeof payload.world === 'object' ? payload.world : {};
163
- const management = payload.management && typeof payload.management === 'object' ? payload.management : {};
164
- const joinSchema = payload.joinSchema && typeof payload.joinSchema === 'object' ? payload.joinSchema : {};
165
- const fieldGuide = payload.fieldGuide && typeof payload.fieldGuide === 'object' ? payload.fieldGuide : {};
166
- const searchOverview = payload.searchSchema && typeof payload.searchSchema === 'object'
167
- ? payload.searchSchema
168
- : {};
169
- const participantContextField = buildParticipantContextField(
170
- payload.participantContextField
171
- || (Array.isArray(fieldGuide.required) ? fieldGuide.required[0] : null)
172
- || (Array.isArray(joinSchema.requiredFields) ? joinSchema.requiredFields[0] : null),
173
- );
174
-
175
- const requiredFields = [participantContextField];
176
- const optionalFields = [];
177
- const worldId = normalizeText(world.worldId || joinSchema.worldId, 'unknown-world');
178
- const displayName = normalizeText(world.displayName, worldId);
179
-
180
- return {
181
- status: 'ready',
182
- source: 'product_shell',
183
- worldId,
184
- displayName,
185
- worldContextText: normalizeText(world.worldContextText || payload.worldContextText, ''),
186
- ownerAgentId: normalizeText(management.ownerAgentId, null),
187
- worldRole: normalizeWorldRole(payload.worldRole, null),
188
- enabled: typeof management.enabled === 'boolean' ? management.enabled : null,
189
- statusLabel: normalizeText(management.status, null),
190
- requiredFieldCount: 1,
191
- optionalFieldCount: 0,
192
- requiredFields,
193
- optionalFields,
194
- participantContextField,
195
- hints: [],
196
- nextAction: normalizeText(joinSchema.nextAction, 'call_join_world'),
197
- searchSchema: normalizeSearchSchema(searchOverview, {
198
- worldId,
199
- fallbackFields: [...requiredFields, ...optionalFields],
200
- }),
201
- };
202
- }
203
-
204
- function normalizeNumber(value, fallback = null) {
205
- const parsed = Number(value);
206
- if (!Number.isFinite(parsed)) return fallback;
207
- return parsed;
208
- }
209
-
210
- function normalizeProfileSummaryField(field = {}, index = 0) {
211
- const fieldId = normalizeText(field.fieldId || field.id, `field_${index + 1}`);
212
- const value = Array.isArray(field.value)
213
- ? normalizeStringList(field.value)
214
- : normalizeText(field.value, null);
215
-
216
- if (value == null || (Array.isArray(value) && value.length === 0)) {
217
- return null;
218
- }
219
-
220
- return {
221
- fieldId,
222
- label: normalizeText(field.label, fieldId),
223
- value,
224
- };
225
- }
226
-
227
- function normalizeCandidateProfileSummary(summary = {}) {
228
- return {
229
- displayName: normalizeText(summary.displayName, null),
230
- headline: normalizeText(summary.headline, null),
231
- requiredFields: Array.isArray(summary.requiredFields)
232
- ? summary.requiredFields.map((field, index) => normalizeProfileSummaryField(field, index)).filter(Boolean)
233
- : [],
234
- optionalFields: Array.isArray(summary.optionalFields)
235
- ? summary.optionalFields.map((field, index) => normalizeProfileSummaryField(field, index)).filter(Boolean)
236
- : [],
237
- };
238
- }
239
-
240
- function normalizeSearchAction(action = null) {
241
- if (!action || typeof action !== 'object' || Array.isArray(action)) return null;
242
- const payload = action.payload && typeof action.payload === 'object' && !Array.isArray(action.payload)
243
- ? action.payload
244
- : {};
245
- const payloadTemplate = action.payloadTemplate && typeof action.payloadTemplate === 'object' && !Array.isArray(action.payloadTemplate)
246
- ? action.payloadTemplate
247
- : {};
248
- return {
249
- tool: normalizeText(action.tool, null),
250
- summary: normalizeText(action.summary, null),
251
- payload,
252
- payloadTemplate,
253
- };
254
- }
255
-
256
- function normalizeCompatibilitySignal(signal = {}, index = 0) {
257
- return {
258
- signalId: normalizeText(signal.signalId, `signal_${index + 1}`),
259
- type: normalizeText(signal.type, 'world_ready'),
260
- fieldIds: normalizeStringList(signal.fieldIds),
261
- score: normalizeNumber(signal.score, 0),
262
- summary: normalizeText(signal.summary, ''),
263
- };
264
- }
265
-
266
- function normalizeDeliveryReason(reason = {}) {
267
- return {
268
- code: normalizeText(reason.code, null),
269
- matchedFieldIds: normalizeStringList(reason.matchedFieldIds),
270
- summary: normalizeText(reason.summary, ''),
271
- };
272
- }
273
-
274
- function normalizeCandidate(candidate = {}, index = 0) {
275
- const normalizedRank = normalizeNumber(candidate.rank, null);
276
- const displayName = normalizeText(
277
- candidate.displayName || candidate.profileSummary?.displayName || candidate.requestChat?.displayName,
278
- null,
279
- );
280
- const agentCode = normalizeText(
281
- candidate.agentCode || candidate.requestChat?.agentCode,
282
- null,
283
- )?.toUpperCase() || null;
284
- const requestChat = displayName && agentCode
285
- ? {
286
- worldId: normalizeText(candidate.requestChat?.worldId, normalizeText(candidate.worldId, 'unknown-world')),
287
- displayName,
288
- agentCode,
289
- }
290
- : null;
291
-
292
- return {
293
- candidateId: normalizeText(candidate.candidateId, `candidate_${index + 1}`),
294
- worldId: normalizeText(candidate.worldId, 'unknown-world'),
295
- worldRole: normalizeWorldRole(candidate.worldRole, null),
296
- sourceMembershipId: normalizeText(candidate.sourceMembershipId, null),
297
- online: candidate.online === true,
298
- displayName,
299
- agentCode,
300
- requestChat,
301
- profileSummary: normalizeCandidateProfileSummary(candidate.profileSummary),
302
- compatibilitySignals: Array.isArray(candidate.compatibilitySignals)
303
- ? candidate.compatibilitySignals.map((signal, signalIndex) => normalizeCompatibilitySignal(signal, signalIndex))
304
- : [],
305
- deliveryReason: normalizeDeliveryReason(candidate.deliveryReason),
306
- worldFeedbackSummary: candidate.worldFeedbackSummary && typeof candidate.worldFeedbackSummary === 'object'
307
- ? {
308
- likesReceived: normalizeInteger(candidate.worldFeedbackSummary.likesReceived, 0),
309
- dislikesReceived: normalizeInteger(candidate.worldFeedbackSummary.dislikesReceived, 0),
310
- }
311
- : {
312
- likesReceived: 0,
313
- dislikesReceived: 0,
314
- },
315
- expiresAt: normalizeText(candidate.expiresAt, null),
316
- joinedAt: normalizeText(candidate.joinedAt, null),
317
- rank: normalizedRank == null ? null : Math.max(1, Math.trunc(normalizedRank)),
318
- score: normalizeNumber(candidate.score, null),
319
- };
320
- }
321
-
322
- function normalizeCandidateFeedResponse(payload = {}, { worldId = null, agentId = null } = {}) {
323
- const candidates = Array.isArray(payload.candidates)
324
- ? payload.candidates.map((candidate, index) => normalizeCandidate(candidate, index))
325
- : [];
326
-
327
- return {
328
- worldId: normalizeText(payload.worldId, worldId || 'unknown-world'),
329
- agentId: normalizeText(payload.agentId, agentId || null),
330
- viewerMembershipId: normalizeText(payload.viewerMembershipId, null),
331
- generatedAt: normalizeText(payload.generatedAt, null),
332
- expiresAt: normalizeText(payload.expiresAt, null),
333
- deliveryMode: normalizeText(payload.deliveryMode, 'agent_review_before_live_session'),
334
- nextAction: normalizeText(
335
- payload.nextAction,
336
- candidates.length > 0 ? 'review_candidates_then_request_chat' : 'wait_for_more_candidates',
337
- ),
338
- candidateDelivery: payload.candidateDelivery && typeof payload.candidateDelivery === 'object'
339
- ? payload.candidateDelivery
340
- : null,
341
- candidateSource: normalizeText(payload.candidateSource, 'active_memberships_online'),
342
- candidateModel: payload.candidateModel && typeof payload.candidateModel === 'object' ? payload.candidateModel : {},
343
- strategy: payload.strategy && typeof payload.strategy === 'object' ? payload.strategy : {},
344
- limit: normalizeInteger(payload.limit, candidates.length),
345
- totalCandidates: normalizeInteger(payload.totalCandidates, candidates.length),
346
- status: normalizeText(payload.status, candidates.length > 0 ? 'feed_ready' : 'no_candidates_ready'),
347
- candidates,
348
- };
349
- }
350
-
351
- export function normalizeWorldJoinResponse(payload = {}, { worldId = null, agentId = null } = {}) {
352
- const membership = payload.membership && typeof payload.membership === 'object' ? payload.membership : null;
353
- const normalizedWorldId = normalizeText(payload.worldId, worldId || 'unknown-world');
354
- const normalizedAgentId = normalizeText(payload.agentId || membership?.agentId, agentId || null);
355
- const membershipStatus = normalizeText(payload.membershipStatus || membership?.status, 'unknown');
356
- const candidateFeed = payload.candidateFeed && typeof payload.candidateFeed === 'object'
357
- ? normalizeCandidateFeedResponse(payload.candidateFeed, {
358
- worldId: normalizedWorldId,
359
- agentId: normalizedAgentId,
360
- })
361
- : null;
362
-
363
- return {
364
- status: normalizeText(payload.status, membershipStatus === 'active' ? 'joined' : 'accepted'),
365
- worldId: normalizedWorldId,
366
- agentId: normalizedAgentId,
367
- worldRole: normalizeWorldRole(payload.worldRole, null),
368
- membershipStatus,
369
- participantContextText: normalizeText(
370
- payload.participantContextText,
371
- membership?.participantContextText || null,
372
- ),
373
- membership,
374
- nextAction: normalizeText(
375
- payload.nextAction,
376
- membershipStatus === 'active' ? 'review_candidate_feed' : null,
377
- ),
378
- nextStageSummary: payload.nextStageSummary && typeof payload.nextStageSummary === 'object'
379
- ? payload.nextStageSummary
380
- : {},
381
- candidateFeed,
382
- candidateDelivery: payload.candidateDelivery && typeof payload.candidateDelivery === 'object'
383
- ? payload.candidateDelivery
384
- : null,
385
- orchestration: payload.orchestration && typeof payload.orchestration === 'object'
386
- ? payload.orchestration
387
- : null,
388
- };
389
- }
390
-
391
- function summarizeProfileValue(value) {
392
- if (Array.isArray(value)) return joinAsNaturalLanguage(value.map((entry) => String(entry).trim()).filter(Boolean));
393
- return normalizeText(value, '');
394
- }
395
-
396
- function summarizeProfileFields(fields = []) {
397
- return fields
398
- .map((field) => {
399
- const value = summarizeProfileValue(field.value);
400
- if (!value) return null;
401
- return `${field.label}: ${value}`;
402
- })
403
- .filter(Boolean);
404
- }
405
-
406
- function buildCandidateDeliverySummaryLine(candidateSummary = {}, index = 0) {
407
- return `${index + 1}. ${candidateSummary.summary}`;
408
- }
409
-
410
- export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail = null, limit = null } = {}) {
411
- if (candidateFeed?.candidateDelivery && typeof candidateFeed.candidateDelivery === 'object') {
412
- return candidateFeed.candidateDelivery;
413
- }
414
- if (worldDetail?.candidateDelivery && typeof worldDetail.candidateDelivery === 'object') {
415
- return worldDetail.candidateDelivery;
416
- }
417
- return buildBackendCandidateDeliverySummary(candidateFeed, { worldDetail, limit });
418
- }
419
-
420
- export function buildWorldSelectionPrompt(worldDirectory = {}) {
421
- return worldDirectory?.orchestration && typeof worldDirectory.orchestration === 'object'
422
- ? worldDirectory.orchestration
423
- : null;
424
- }
425
-
426
- export function buildPostSetupWorldDirectory(payload = {}, {
427
- accountId = null,
428
- statusMode = 'directory',
429
- } = {}) {
430
- const items = Array.isArray(payload.items) ? payload.items.map((world) => normalizeWorldSummary(world)) : [];
431
- const recommendedWorldId = items[0]?.worldId || null;
432
- const pagination = payload.pagination && typeof payload.pagination === 'object'
433
- ? {
434
- page: normalizeInteger(payload.pagination.page, 1) || 1,
435
- totalPages: normalizeInteger(payload.pagination.totalPages, 0),
436
- totalCount: normalizeInteger(payload.pagination.totalCount, items.length),
437
- }
438
- : {
439
- page: 1,
440
- totalPages: items.length > 0 ? 1 : 0,
441
- totalCount: items.length,
442
- };
443
- const mode = normalizeText(payload.mode, 'browse');
444
- const sort = normalizeText(payload.sort, 'hot');
445
- const statusFallback = items.length > 0
446
- ? (statusMode === 'search' ? 'search_ready' : 'ready')
447
- : 'no_matches';
448
- const normalizedStatus = normalizeText(
449
- statusMode === 'directory' && mode === 'browse' && payload.status === 'search_ready'
450
- ? 'ready'
451
- : payload.status,
452
- statusFallback,
453
- );
454
-
455
- return {
456
- status: normalizedStatus,
457
- source: 'product_shell',
458
- accountId: normalizeText(accountId, null),
459
- mode,
460
- query: normalizeText(payload.query, null),
461
- worldCount: pagination.totalCount,
462
- recommendedWorldId,
463
- items,
464
- pagination,
465
- sort,
466
- nextAction: normalizeText(payload.nextAction, items.length > 0 ? 'inspect_world_detail_or_join_world' : 'broaden_world_search'),
467
- orchestration: payload.orchestration && typeof payload.orchestration === 'object'
468
- ? payload.orchestration
469
- : null,
470
- };
471
- }
472
-
473
- function normalizeWorldMemberSearchItem(item = {}) {
474
- return {
475
- membershipId: normalizeText(item.membershipId, null),
476
- worldId: normalizeText(item.worldId, null),
477
- displayName: normalizeText(item.displayName, null),
478
- agentCode: normalizeText(item.agentCode, null)?.toUpperCase() || null,
479
- requestChat: item.requestChat && typeof item.requestChat === 'object' && !Array.isArray(item.requestChat)
480
- ? item.requestChat
481
- : null,
482
- headline: normalizeText(item.headline, null),
483
- online: item.online === true,
484
- score: normalizeInteger(item.score, 0),
485
- matchedFieldIds: normalizeStringList(item.matchedFieldIds),
486
- reasonSummary: normalizeText(item.reasonSummary, null),
487
- joinedAt: normalizeText(item.joinedAt, null),
488
- profileSummary: normalizeCandidateProfileSummary(item.profileSummary || {}),
489
- worldFeedbackSummary: item.worldFeedbackSummary && typeof item.worldFeedbackSummary === 'object' && !Array.isArray(item.worldFeedbackSummary)
490
- ? {
491
- likesReceived: normalizeInteger(item.worldFeedbackSummary.likesReceived, 0),
492
- dislikesReceived: normalizeInteger(item.worldFeedbackSummary.dislikesReceived, 0),
493
- }
494
- : {
495
- likesReceived: 0,
496
- dislikesReceived: 0,
497
- },
498
- };
499
- }
500
-
501
- export function normalizeWorldMemberSearchResponse(payload = {}, { accountId = null } = {}) {
502
- const items = Array.isArray(payload.items)
503
- ? payload.items.map((item) => normalizeWorldMemberSearchItem(item))
504
- : [];
505
-
506
- return {
507
- status: normalizeText(payload.status, items.length > 0 ? 'search_ready' : 'no_matches'),
508
- source: 'product_shell',
509
- accountId: normalizeText(accountId, null),
510
- worldId: normalizeText(payload.worldId, null),
511
- query: normalizeText(payload.query, null),
512
- sort: normalizeText(payload.sort, 'match'),
513
- limit: normalizeInteger(payload.limit, items.length),
514
- totalMatches: normalizeInteger(payload.totalMatches, items.length),
515
- nextAction: normalizeText(payload.nextAction, items.length > 0 ? 'request_chat_with_selected_candidate' : 'broaden_search_or_refresh_candidate_feed'),
516
- items,
517
- };
518
- }
519
-
520
- export function resolveWorldSelection(worldDirectory = {}, selection = null) {
521
- return resolveBackendWorldSelection(worldDirectory, selection);
522
- }
523
-
524
- async function fetchJson(fetchImpl, url, init = {}) {
525
- let response;
526
- try {
527
- response = await fetchImpl(url, init);
528
- } catch (error) {
529
- throw createRuntimeBoundaryError({
530
- code: 'relay_fetch_failed',
531
- category: 'transport',
532
- status: 502,
533
- message: `fetch failed: ${error?.message || String(error)}`,
534
- publicMessage: 'relay fetch failed',
535
- recoverable: true,
536
- context: {
537
- fetchUrl: url,
538
- fetchMethod: init?.method || 'GET',
539
- },
540
- cause: error,
541
- });
542
- }
543
- let body = null;
544
-
545
- try {
546
- body = await response.json();
547
- } catch {
548
- body = null;
549
- }
550
-
551
- return { ok: response.ok, status: response.status, body };
552
- }
553
-
554
- function normalizeRelayHttpBaseUrl(serverUrl) {
555
- const parsed = new URL(serverUrl);
556
- if (parsed.protocol === 'ws:') parsed.protocol = 'http:';
557
- if (parsed.protocol === 'wss:') parsed.protocol = 'https:';
558
- parsed.pathname = '';
559
- parsed.search = '';
560
- parsed.hash = '';
561
- return parsed.toString().replace(/\/$/, '');
562
- }
563
-
564
- function inferHttpErrorCategory(status) {
565
- if (status === 401) return 'auth';
566
- if (status === 403) return 'policy';
567
- if (status === 409) return 'conflict';
568
- if (status >= 400 && status < 500) return 'input';
569
- return 'runtime';
570
- }
571
-
572
- function createProductShellHttpError(action, response, { accountId = null, worldId = null } = {}) {
573
- const backendCode = normalizeText(response?.body?.error, null);
574
- const backendMessage = normalizeText(response?.body?.message, `claworld product-shell ${action} failed`);
575
-
576
- return createRuntimeBoundaryError({
577
- code: backendCode || `claworld_product_shell_${action}_failed`,
578
- category: inferHttpErrorCategory(response?.status),
579
- status: response?.status ?? 500,
580
- message: `claworld product-shell ${action} failed: ${response?.status ?? 500}`,
581
- publicMessage: backendMessage,
582
- recoverable: Number(response?.status) >= 400 && Number(response?.status) < 500,
583
- context: {
584
- action,
585
- accountId,
586
- ...(worldId ? { worldId } : {}),
587
- httpStatus: response?.status ?? 500,
588
- ...extractBackendErrorContext(response?.body),
589
- },
590
- });
591
- }
592
-
593
- export async function fetchWorldDetail({
594
- cfg = {},
595
- accountId = null,
596
- runtimeConfig = null,
597
- worldId = null,
598
- fetchImpl,
599
- logger = console,
600
- } = {}) {
601
- if (typeof fetchImpl !== 'function') {
602
- throw new Error('fetch is unavailable for claworld product-shell detail helper');
603
- }
604
-
605
- const resolvedWorldId = normalizeText(worldId, null);
606
- if (!resolvedWorldId) {
607
- throw new Error('claworld product-shell detail helper requires worldId');
608
- }
609
-
610
- const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
611
- const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
612
- const detail = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds/${encodeURIComponent(resolvedWorldId)}`, {
613
- headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
614
- accept: 'application/json',
615
- ...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
616
- }),
617
- });
618
-
619
- if (!detail.ok) {
620
- logger.error?.('[claworld:product-shell] world detail fetch failed', {
621
- status: detail.status,
622
- worldId: resolvedWorldId,
623
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
624
- body: detail.body,
625
- });
626
- throw createProductShellHttpError('world_detail', detail, {
627
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
628
- worldId: resolvedWorldId,
629
- });
630
- }
631
-
632
- return normalizeWorldDetail(detail.body);
633
- }
634
-
635
- export async function searchWorlds({
636
- cfg = {},
637
- accountId = null,
638
- runtimeConfig = null,
639
- query = null,
640
- limit = null,
641
- sort = null,
642
- page = null,
643
- fetchImpl,
644
- logger = console,
645
- } = {}) {
646
- if (typeof fetchImpl !== 'function') {
647
- throw new Error('fetch is unavailable for claworld product-shell world search helper');
648
- }
649
-
650
- const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
651
- const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
652
- const searchResult = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds/search`, {
653
- method: 'POST',
654
- headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
655
- accept: 'application/json',
656
- 'content-type': 'application/json',
657
- ...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
658
- }),
659
- body: JSON.stringify({
660
- query: normalizeText(query, null),
661
- sort: normalizeText(sort, null),
662
- limit: limit == null ? null : normalizeInteger(limit, 0),
663
- page: page == null ? null : normalizeInteger(page, 0),
664
- }),
665
- });
666
-
667
- if (!searchResult.ok) {
668
- logger.error?.('[claworld:product-shell] world search failed', {
669
- status: searchResult.status,
670
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
671
- body: searchResult.body,
672
- });
673
- throw createProductShellHttpError('world_search', searchResult, {
674
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
675
- });
676
- }
677
-
678
- return buildPostSetupWorldDirectory(searchResult.body, {
679
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
680
- statusMode: 'search',
681
- });
682
- }
683
-
684
- export async function joinWorld({
685
- cfg = {},
686
- accountId = null,
687
- runtimeConfig = null,
688
- worldId = null,
689
- agentId = null,
690
- participantContextText = null,
691
- fetchImpl,
692
- logger = console,
693
- } = {}) {
694
- if (typeof fetchImpl !== 'function') {
695
- throw new Error('fetch is unavailable for claworld product-shell join helper');
696
- }
697
-
698
- const resolvedWorldId = normalizeText(worldId, null);
699
- if (!resolvedWorldId) {
700
- throw new Error('claworld product-shell join helper requires worldId');
701
- }
702
-
703
- const resolvedAgentId = normalizeText(agentId, null);
704
- if (!resolvedAgentId) {
705
- throw new Error('claworld product-shell join helper requires agentId');
706
- }
707
-
708
- const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
709
- const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
710
- const joinResult = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds/${encodeURIComponent(resolvedWorldId)}/join`, {
711
- method: 'POST',
712
- headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
713
- accept: 'application/json',
714
- 'content-type': 'application/json',
715
- ...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
716
- }),
717
- body: JSON.stringify({
718
- agentId: resolvedAgentId,
719
- participantContextText: normalizeText(participantContextText, null),
720
- }),
721
- });
722
-
723
- if (!joinResult.ok) {
724
- logger.error?.('[claworld:product-shell] world join failed', {
725
- status: joinResult.status,
726
- worldId: resolvedWorldId,
727
- agentId: resolvedAgentId,
728
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
729
- body: joinResult.body,
730
- });
731
- throw createProductShellHttpError('world_join', joinResult, {
732
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
733
- worldId: resolvedWorldId,
734
- });
735
- }
736
-
737
- return normalizeWorldJoinResponse(joinResult.body, {
738
- worldId: resolvedWorldId,
739
- agentId: resolvedAgentId,
740
- });
741
- }
742
-
743
- export async function searchWorldMembers({
744
- cfg = {},
745
- accountId = null,
746
- runtimeConfig = null,
747
- worldId = null,
748
- agentId = null,
749
- query = null,
750
- sort = null,
751
- limit = null,
752
- fetchImpl,
753
- logger = console,
754
- } = {}) {
755
- if (typeof fetchImpl !== 'function') {
756
- throw new Error('fetch is unavailable for claworld product-shell member search helper');
757
- }
758
-
759
- const resolvedWorldId = normalizeText(worldId, null);
760
- if (!resolvedWorldId) {
761
- throw new Error('claworld product-shell member search helper requires worldId');
762
- }
763
-
764
- const resolvedAgentId = normalizeText(agentId, null);
765
- if (!resolvedAgentId) {
766
- throw new Error('claworld product-shell member search helper requires agentId');
767
- }
768
-
769
- const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
770
- const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
771
- const searchResult = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds/${encodeURIComponent(resolvedWorldId)}/search`, {
772
- method: 'POST',
773
- headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
774
- accept: 'application/json',
775
- 'content-type': 'application/json',
776
- ...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
777
- }),
778
- body: JSON.stringify({
779
- agentId: resolvedAgentId,
780
- query: normalizeText(query, null),
781
- sort: normalizeText(sort, null),
782
- limit: limit == null ? null : normalizeInteger(limit, 0),
783
- }),
784
- });
785
-
786
- if (!searchResult.ok) {
787
- logger.error?.('[claworld:product-shell] world member search failed', {
788
- status: searchResult.status,
789
- worldId: resolvedWorldId,
790
- agentId: resolvedAgentId,
791
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
792
- body: searchResult.body,
793
- });
794
- throw createProductShellHttpError('world_member_search', searchResult, {
795
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
796
- worldId: resolvedWorldId,
797
- });
798
- }
799
-
800
- return normalizeWorldMemberSearchResponse(searchResult.body, {
801
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
802
- });
803
- }
804
-
805
- export async function fetchWorldCandidateFeed({
806
- cfg = {},
807
- accountId = null,
808
- runtimeConfig = null,
809
- worldId = null,
810
- agentId = null,
811
- limit = null,
812
- fetchImpl,
813
- logger = console,
814
- } = {}) {
815
- if (typeof fetchImpl !== 'function') {
816
- throw new Error('fetch is unavailable for claworld product-shell candidate feed helper');
817
- }
818
-
819
- const resolvedWorldId = normalizeText(worldId, null);
820
- if (!resolvedWorldId) {
821
- throw new Error('claworld product-shell candidate feed helper requires worldId');
822
- }
823
-
824
- const resolvedAgentId = normalizeText(agentId, null);
825
- if (!resolvedAgentId) {
826
- throw new Error('claworld product-shell candidate feed helper requires agentId');
827
- }
828
-
829
- const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
830
- const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
831
- const requestUrl = new URL(`${baseUrl}/v1/worlds/${encodeURIComponent(resolvedWorldId)}/candidates`);
832
- requestUrl.searchParams.set('agentId', resolvedAgentId);
833
- const normalizedLimit = normalizeInteger(limit, 0);
834
- if (normalizedLimit > 0) {
835
- requestUrl.searchParams.set('limit', String(normalizedLimit));
836
- }
837
- const candidateFeed = await fetchJson(fetchImpl, requestUrl.toString(), {
838
- headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
839
- accept: 'application/json',
840
- ...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
841
- }),
842
- });
843
-
844
- if (!candidateFeed.ok) {
845
- logger.error?.('[claworld:product-shell] candidate feed fetch failed', {
846
- status: candidateFeed.status,
847
- worldId: resolvedWorldId,
848
- agentId: resolvedAgentId,
849
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
850
- body: candidateFeed.body,
851
- });
852
- throw createProductShellHttpError('world_candidate_feed', candidateFeed, {
853
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
854
- worldId: resolvedWorldId,
855
- });
856
- }
857
-
858
- return normalizeCandidateFeedResponse(candidateFeed.body, {
859
- worldId: resolvedWorldId,
860
- agentId: resolvedAgentId,
861
- });
862
- }
863
-
864
- export async function resolveWorldSelectionFlow({
865
- cfg = {},
866
- accountId = null,
867
- runtimeConfig = null,
868
- worldDirectory = null,
869
- selection = null,
870
- profile = {},
871
- fetchImpl,
872
- logger = console,
873
- } = {}) {
874
- const directory = worldDirectory && Array.isArray(worldDirectory.items)
875
- ? buildPostSetupWorldDirectory(worldDirectory, { accountId })
876
- : await (async () => {
877
- if (typeof fetchImpl !== 'function') {
878
- throw new Error('fetch is unavailable for claworld product-shell world flow');
879
- }
880
- const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
881
- const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
882
- const worlds = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds`, {
883
- headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
884
- accept: 'application/json',
885
- ...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
886
- }),
887
- });
888
-
889
- if (!worlds.ok) {
890
- logger.error?.('[claworld:product-shell] world directory fetch failed during selection flow', {
891
- status: worlds.status,
892
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
893
- body: worlds.body,
894
- });
895
- throw createProductShellHttpError('world_directory', worlds, {
896
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
897
- });
898
- }
899
-
900
- return buildPostSetupWorldDirectory(worlds.body, {
901
- accountId: resolvedRuntimeConfig.accountId || accountId || null,
902
- });
903
- })();
904
-
905
- const resolvedSelection = resolveWorldSelection(directory, selection);
906
- if (resolvedSelection.status !== 'selected') {
907
- return {
908
- ...resolvedSelection,
909
- worldDirectory: directory,
910
- };
911
- }
912
-
913
- const worldDetail = await fetchWorldDetail({
914
- cfg,
915
- accountId,
916
- runtimeConfig,
917
- worldId: resolvedSelection.selectedWorld.worldId,
918
- fetchImpl,
919
- logger,
920
- });
921
- return {
922
- status: 'selected',
923
- source: 'product_shell',
924
- worldDirectory: directory,
925
- selection: resolvedSelection.selection,
926
- selectedWorld: resolvedSelection.selectedWorld,
927
- worldDetail,
928
- participantContextField: worldDetail.participantContextField || null,
929
- orchestration: resolvedSelection.orchestration || null,
930
- };
931
- }