@xfxstudio/claworld 0.2.23 → 0.2.25
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.
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/claworld-a2a-channel-agent/SKILL.md +200 -0
- package/skills/claworld-help/SKILL.md +77 -3
- package/skills/claworld-join-and-chat/SKILL.md +96 -36
- package/src/lib/chat-request.js +2 -1
- package/src/lib/relay/agent-readable-markdown.js +308 -0
- package/src/lib/relay/kickoff-progress.js +162 -0
- package/src/lib/relay/kickoff-text.js +6 -82
- package/src/lib/relay/shared.js +30 -0
- package/src/openclaw/index.js +6 -0
- package/src/openclaw/plugin/account-identity.js +11 -2
- package/src/openclaw/plugin/claworld-channel-plugin.js +418 -64
- package/src/openclaw/plugin/managed-config.js +19 -0
- package/src/openclaw/plugin/register-tooling.js +46 -0
- package/src/openclaw/plugin/register.js +121 -30
- package/src/openclaw/plugin/relay-client.js +38 -1
- package/src/openclaw/plugin-version.js +67 -0
- package/src/openclaw/runtime/product-shell-helper.js +19 -13
- package/src/openclaw/runtime/tool-contracts.js +71 -16
- package/src/openclaw/runtime/world-membership-helper.js +320 -0
- package/src/openclaw/runtime/world-moderation-helper.js +18 -1
- package/src/product-shell/contracts/world-orchestration.js +9 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveClaworldRuntimeConfig } from '../plugin/config-schema.js';
|
|
2
|
+
import { buildRuntimeAuthHeaders } from '../plugin/account-identity.js';
|
|
2
3
|
import { createRuntimeBoundaryError } from '../../lib/runtime-errors.js';
|
|
3
4
|
import { extractBackendErrorContext } from './backend-error-context.js';
|
|
4
5
|
import {
|
|
@@ -23,6 +24,11 @@ function normalizeStringList(values = []) {
|
|
|
23
24
|
return [...new Set(values.map((value) => normalizeText(value, null)).filter(Boolean))];
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
function normalizeWorldRole(worldRole, fallback = null) {
|
|
28
|
+
const normalized = normalizeText(worldRole, fallback);
|
|
29
|
+
return ['owner', 'member'].includes(normalized) ? normalized : fallback;
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
function sentenceCase(value, fallback = '') {
|
|
27
33
|
const normalized = normalizeText(value, fallback);
|
|
28
34
|
if (!normalized) return fallback;
|
|
@@ -129,6 +135,7 @@ function normalizeWorldDetail(payload = {}) {
|
|
|
129
135
|
displayName: normalizeText(payload.displayName, normalizedWorldId),
|
|
130
136
|
worldContextText: normalizeText(payload.worldContextText, ''),
|
|
131
137
|
ownerAgentId: normalizeText(payload.ownerAgentId, null),
|
|
138
|
+
worldRole: normalizeWorldRole(payload.worldRole, null),
|
|
132
139
|
enabled: typeof payload.enabled === 'boolean' ? payload.enabled : null,
|
|
133
140
|
requiredFieldCount: normalizeInteger(payload.requiredFieldCount, requiredFields.length) || requiredFields.length,
|
|
134
141
|
optionalFieldCount: normalizeInteger(payload.optionalFieldCount, optionalFields.length) || optionalFields.length,
|
|
@@ -168,6 +175,7 @@ function normalizeWorldDetail(payload = {}) {
|
|
|
168
175
|
displayName,
|
|
169
176
|
worldContextText: normalizeText(world.worldContextText || payload.worldContextText, ''),
|
|
170
177
|
ownerAgentId: normalizeText(management.ownerAgentId, null),
|
|
178
|
+
worldRole: normalizeWorldRole(payload.worldRole, null),
|
|
171
179
|
enabled: typeof management.enabled === 'boolean' ? management.enabled : null,
|
|
172
180
|
statusLabel: normalizeText(management.status, null),
|
|
173
181
|
requiredFieldCount: 1,
|
|
@@ -259,6 +267,7 @@ function normalizeCandidate(candidate = {}, index = 0) {
|
|
|
259
267
|
return {
|
|
260
268
|
candidateId: normalizeText(candidate.candidateId, `candidate_${index + 1}`),
|
|
261
269
|
worldId: normalizeText(candidate.worldId, 'unknown-world'),
|
|
270
|
+
worldRole: normalizeWorldRole(candidate.worldRole, null),
|
|
262
271
|
sourceMembershipId: normalizeText(candidate.sourceMembershipId, null),
|
|
263
272
|
online: candidate.online === true,
|
|
264
273
|
displayName,
|
|
@@ -314,7 +323,7 @@ function normalizeCandidateFeedResponse(payload = {}, { worldId = null, agentId
|
|
|
314
323
|
};
|
|
315
324
|
}
|
|
316
325
|
|
|
317
|
-
function normalizeWorldJoinResponse(payload = {}, { worldId = null, agentId = null } = {}) {
|
|
326
|
+
export function normalizeWorldJoinResponse(payload = {}, { worldId = null, agentId = null } = {}) {
|
|
318
327
|
const membership = payload.membership && typeof payload.membership === 'object' ? payload.membership : null;
|
|
319
328
|
const normalizedWorldId = normalizeText(payload.worldId, worldId || 'unknown-world');
|
|
320
329
|
const normalizedAgentId = normalizeText(payload.agentId || membership?.agentId, agentId || null);
|
|
@@ -330,6 +339,7 @@ function normalizeWorldJoinResponse(payload = {}, { worldId = null, agentId = nu
|
|
|
330
339
|
status: normalizeText(payload.status, membershipStatus === 'active' ? 'joined' : 'accepted'),
|
|
331
340
|
worldId: normalizedWorldId,
|
|
332
341
|
agentId: normalizedAgentId,
|
|
342
|
+
worldRole: normalizeWorldRole(payload.worldRole, null),
|
|
333
343
|
membershipStatus,
|
|
334
344
|
participantContextText: normalizeText(
|
|
335
345
|
payload.participantContextText,
|
|
@@ -512,11 +522,10 @@ export async function fetchWorldDetail({
|
|
|
512
522
|
const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
|
|
513
523
|
const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
|
|
514
524
|
const detail = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds/${encodeURIComponent(resolvedWorldId)}`, {
|
|
515
|
-
headers: {
|
|
525
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
516
526
|
accept: 'application/json',
|
|
517
527
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
518
|
-
|
|
519
|
-
},
|
|
528
|
+
}),
|
|
520
529
|
});
|
|
521
530
|
|
|
522
531
|
if (!detail.ok) {
|
|
@@ -563,12 +572,11 @@ export async function joinWorld({
|
|
|
563
572
|
const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
|
|
564
573
|
const joinResult = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds/${encodeURIComponent(resolvedWorldId)}/join`, {
|
|
565
574
|
method: 'POST',
|
|
566
|
-
headers: {
|
|
575
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
567
576
|
accept: 'application/json',
|
|
568
577
|
'content-type': 'application/json',
|
|
569
578
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
570
|
-
|
|
571
|
-
},
|
|
579
|
+
}),
|
|
572
580
|
body: JSON.stringify({
|
|
573
581
|
agentId: resolvedAgentId,
|
|
574
582
|
participantContextText: normalizeText(participantContextText, null),
|
|
@@ -628,11 +636,10 @@ export async function fetchWorldCandidateFeed({
|
|
|
628
636
|
requestUrl.searchParams.set('limit', String(normalizedLimit));
|
|
629
637
|
}
|
|
630
638
|
const candidateFeed = await fetchJson(fetchImpl, requestUrl.toString(), {
|
|
631
|
-
headers: {
|
|
639
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
632
640
|
accept: 'application/json',
|
|
633
641
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
634
|
-
|
|
635
|
-
},
|
|
642
|
+
}),
|
|
636
643
|
});
|
|
637
644
|
|
|
638
645
|
if (!candidateFeed.ok) {
|
|
@@ -674,11 +681,10 @@ export async function resolveWorldSelectionFlow({
|
|
|
674
681
|
const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
|
|
675
682
|
const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
|
|
676
683
|
const worlds = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds`, {
|
|
677
|
-
headers: {
|
|
684
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
678
685
|
accept: 'application/json',
|
|
679
686
|
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
680
|
-
|
|
681
|
-
},
|
|
687
|
+
}),
|
|
682
688
|
});
|
|
683
689
|
|
|
684
690
|
if (!worlds.ok) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { normalizeAcceptedChatKickoffRecord } from '../../lib/relay/kickoff-progress.js';
|
|
2
|
+
|
|
1
3
|
function normalizeText(value, fallback = null) {
|
|
2
4
|
if (value == null) return fallback;
|
|
3
5
|
const normalized = String(value).trim();
|
|
@@ -159,6 +161,7 @@ function projectToolCandidateDeliverySummary(
|
|
|
159
161
|
candidateId: normalizeText(summary.candidateId, null),
|
|
160
162
|
sourceMembershipId: normalizeText(summary.sourceMembershipId, null),
|
|
161
163
|
displayName: normalizeText(summary.displayName, null),
|
|
164
|
+
worldRole: projectWorldRole(summary.worldRole, null),
|
|
162
165
|
headline: normalizeText(summary.headline, null),
|
|
163
166
|
online: summary.online === true,
|
|
164
167
|
rank: normalizeOptionalInteger(summary.rank, null),
|
|
@@ -229,9 +232,13 @@ export function projectToolWorldDetail(worldDetail = {}) {
|
|
|
229
232
|
worldDetail.world?.worldContextText,
|
|
230
233
|
worldDetail.worldContextText || '',
|
|
231
234
|
),
|
|
232
|
-
ownerAgentId: normalizeText(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
+
ownerAgentId: normalizeText(
|
|
236
|
+
worldDetail.management?.ownerAgentId,
|
|
237
|
+
normalizeText(worldDetail.ownerAgentId, null),
|
|
238
|
+
),
|
|
239
|
+
worldRole: projectWorldRole(worldDetail.worldRole, null),
|
|
240
|
+
enabled: normalizeOptionalBoolean(worldDetail.management?.enabled, normalizeOptionalBoolean(worldDetail.enabled, null)),
|
|
241
|
+
status: normalizeText(worldDetail.management?.status, normalizeText(worldDetail.statusLabel, null)),
|
|
235
242
|
participantContextField: projectParticipantContextField(worldDetail.participantContextField),
|
|
236
243
|
};
|
|
237
244
|
}
|
|
@@ -241,6 +248,7 @@ function projectToolCandidateSummary(summary = {}, index = 0) {
|
|
|
241
248
|
candidateId: normalizeText(summary.candidateId, `candidate_${index + 1}`),
|
|
242
249
|
displayName: normalizeText(summary.displayName, `Candidate ${index + 1}`),
|
|
243
250
|
agentCode: normalizeText(summary.agentCode, null)?.toUpperCase() || null,
|
|
251
|
+
worldRole: projectWorldRole(summary.worldRole, null),
|
|
244
252
|
headline: normalizeText(summary.headline, null),
|
|
245
253
|
online: summary.online === true,
|
|
246
254
|
rank: normalizeInteger(summary.rank, 0) || null,
|
|
@@ -302,6 +310,7 @@ export function projectToolJoinWorldResponse(
|
|
|
302
310
|
status: joinResult.membershipStatus === 'active' ? 'joined' : 'accepted',
|
|
303
311
|
worldId: joinResult.worldId,
|
|
304
312
|
accountId: normalizeText(accountId, null),
|
|
313
|
+
worldRole: projectWorldRole(joinResult.worldRole, null),
|
|
305
314
|
membershipStatus: joinResult.membershipStatus || 'unknown',
|
|
306
315
|
participantContextText: normalizeText(
|
|
307
316
|
joinResult.participantContextText,
|
|
@@ -327,6 +336,10 @@ export function projectToolCreateWorldResponse(world = {}, { accountId = null }
|
|
|
327
336
|
status: normalizeText(world.status, null),
|
|
328
337
|
enabled: normalizeOptionalBoolean(world.enabled, null),
|
|
329
338
|
worldRole: projectWorldRole(world.worldRole, null),
|
|
339
|
+
ownerJoin:
|
|
340
|
+
world.ownerJoin && typeof world.ownerJoin === 'object'
|
|
341
|
+
? projectToolJoinWorldResponse(world.ownerJoin, { accountId })
|
|
342
|
+
: null,
|
|
330
343
|
schemaVersion: normalizeOptionalInteger(world.schemaVersion, null),
|
|
331
344
|
createdAt: normalizeText(world.createdAt, null),
|
|
332
345
|
};
|
|
@@ -371,6 +384,41 @@ export function projectToolManagedWorldResponse(world = {}, { accountId = null }
|
|
|
371
384
|
};
|
|
372
385
|
}
|
|
373
386
|
|
|
387
|
+
function projectToolWorldMembershipSummary(membership = {}) {
|
|
388
|
+
return {
|
|
389
|
+
membershipId: normalizeText(membership.membershipId, null),
|
|
390
|
+
worldId: normalizeText(membership.worldId, null),
|
|
391
|
+
displayName: normalizeText(membership.displayName, null),
|
|
392
|
+
worldContextText: normalizeText(membership.worldContextText, null),
|
|
393
|
+
ownerAgentId: normalizeText(membership.ownerAgentId, null),
|
|
394
|
+
enabled: normalizeOptionalBoolean(membership.enabled, null),
|
|
395
|
+
worldStatus: normalizeText(membership.worldStatus, null),
|
|
396
|
+
worldRole: projectWorldRole(membership.worldRole, null),
|
|
397
|
+
membershipStatus: normalizeText(membership.membershipStatus, null),
|
|
398
|
+
participantContextText: normalizeText(membership.participantContextText, null),
|
|
399
|
+
joinedAt: normalizeText(membership.joinedAt, null),
|
|
400
|
+
updatedAt: normalizeText(membership.updatedAt, null),
|
|
401
|
+
nextAction: normalizeText(membership.nextAction, null),
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function projectToolWorldMembershipListResponse(payload = {}, { accountId = null } = {}) {
|
|
406
|
+
return {
|
|
407
|
+
accountId: normalizeText(accountId, null),
|
|
408
|
+
memberships: Array.isArray(payload.items)
|
|
409
|
+
? payload.items.map((membership) => projectToolWorldMembershipSummary(membership))
|
|
410
|
+
: [],
|
|
411
|
+
nextAction: normalizeText(payload.nextAction, null),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export function projectToolWorldMembershipResponse(payload = {}, { accountId = null } = {}) {
|
|
416
|
+
return {
|
|
417
|
+
accountId: normalizeText(accountId, null),
|
|
418
|
+
...projectToolWorldMembershipSummary(payload),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
374
422
|
export function projectToolFeedbackSubmissionResponse(result = {}) {
|
|
375
423
|
const feedback = result.feedback && typeof result.feedback === 'object' ? result.feedback : {};
|
|
376
424
|
const reporter = feedback.reporter && typeof feedback.reporter === 'object' ? feedback.reporter : {};
|
|
@@ -437,20 +485,27 @@ function normalizeConversationScopeDetails(input = {}) {
|
|
|
437
485
|
}
|
|
438
486
|
|
|
439
487
|
function projectChatRequestKickoff(kickoff = {}) {
|
|
440
|
-
|
|
488
|
+
const normalizedKickoff = normalizeAcceptedChatKickoffRecord(kickoff, { fallbackStatus: 'skipped' });
|
|
489
|
+
if (!normalizedKickoff) return null;
|
|
441
490
|
return {
|
|
442
|
-
status: normalizeText(
|
|
443
|
-
deliveredAt: normalizeText(
|
|
444
|
-
senderKickoffDeliveredAt: normalizeText(
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
491
|
+
status: normalizeText(normalizedKickoff.status, 'skipped'),
|
|
492
|
+
deliveredAt: normalizeText(normalizedKickoff.deliveredAt, null),
|
|
493
|
+
senderKickoffDeliveredAt: normalizeText(
|
|
494
|
+
normalizedKickoff.senderKickoffDeliveredAt,
|
|
495
|
+
normalizeText(normalizedKickoff.deliveredAt, null),
|
|
496
|
+
),
|
|
497
|
+
openerAcceptedAt: normalizeText(normalizedKickoff.openerAcceptedAt, null),
|
|
498
|
+
openerDeliveredAt: normalizeText(normalizedKickoff.openerDeliveredAt, null),
|
|
499
|
+
liveChatEstablishedAt: normalizeText(normalizedKickoff.liveChatEstablishedAt, null),
|
|
500
|
+
conversationKey: normalizeText(normalizedKickoff.conversationKey, null),
|
|
501
|
+
localSessionKey: normalizeText(
|
|
502
|
+
normalizedKickoff.localSessionKey,
|
|
503
|
+
normalizeText(normalizedKickoff.sessionKey, null),
|
|
504
|
+
),
|
|
505
|
+
turnId: normalizeText(normalizedKickoff.turnId, null),
|
|
506
|
+
deliveryId: normalizeText(normalizedKickoff.deliveryId, null),
|
|
507
|
+
created: typeof normalizedKickoff.created === 'boolean' ? normalizedKickoff.created : null,
|
|
508
|
+
reason: normalizeText(normalizedKickoff.reason, null),
|
|
454
509
|
};
|
|
455
510
|
}
|
|
456
511
|
|
|
@@ -0,0 +1,320 @@
|
|
|
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
|
+
|
|
6
|
+
function normalizeText(value, fallback = null) {
|
|
7
|
+
if (value == null) return fallback;
|
|
8
|
+
const normalized = String(value).trim();
|
|
9
|
+
return normalized || fallback;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function normalizeOptionalBoolean(value, fallback = null) {
|
|
13
|
+
if (typeof value === 'boolean') return value;
|
|
14
|
+
return fallback;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalizeWorldRole(worldRole, fallback = null) {
|
|
18
|
+
const normalized = normalizeText(worldRole, fallback);
|
|
19
|
+
return ['owner', 'member'].includes(normalized) ? normalized : fallback;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizeManagedWorldMembership(payload = {}) {
|
|
23
|
+
return {
|
|
24
|
+
membershipId: normalizeText(payload.membershipId, null),
|
|
25
|
+
worldId: normalizeText(payload.worldId, null),
|
|
26
|
+
displayName: normalizeText(payload.displayName, null),
|
|
27
|
+
worldContextText: normalizeText(payload.worldContextText, null),
|
|
28
|
+
ownerAgentId: normalizeText(payload.ownerAgentId, null),
|
|
29
|
+
enabled: normalizeOptionalBoolean(payload.enabled, null),
|
|
30
|
+
worldStatus: normalizeText(payload.worldStatus, null),
|
|
31
|
+
worldRole: normalizeWorldRole(payload.worldRole, null),
|
|
32
|
+
membershipStatus: normalizeText(payload.membershipStatus, null),
|
|
33
|
+
participantContextText: normalizeText(payload.participantContextText, null),
|
|
34
|
+
joinedAt: normalizeText(payload.joinedAt, null),
|
|
35
|
+
updatedAt: normalizeText(payload.updatedAt, null),
|
|
36
|
+
nextAction: normalizeText(payload.nextAction, null),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeMembershipList(payload = {}) {
|
|
41
|
+
return {
|
|
42
|
+
items: Array.isArray(payload.items)
|
|
43
|
+
? payload.items.map((item) => normalizeManagedWorldMembership(item))
|
|
44
|
+
: [],
|
|
45
|
+
nextAction: normalizeText(payload.nextAction, null),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function fetchJson(fetchImpl, url, init = {}) {
|
|
50
|
+
let response;
|
|
51
|
+
try {
|
|
52
|
+
response = await fetchImpl(url, init);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw createRuntimeBoundaryError({
|
|
55
|
+
code: 'relay_fetch_failed',
|
|
56
|
+
category: 'transport',
|
|
57
|
+
status: 502,
|
|
58
|
+
message: `fetch failed: ${error?.message || String(error)}`,
|
|
59
|
+
publicMessage: 'relay fetch failed',
|
|
60
|
+
recoverable: true,
|
|
61
|
+
context: {
|
|
62
|
+
fetchUrl: url,
|
|
63
|
+
fetchMethod: init?.method || 'GET',
|
|
64
|
+
},
|
|
65
|
+
cause: error,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
let body = null;
|
|
69
|
+
try {
|
|
70
|
+
body = await response.json();
|
|
71
|
+
} catch {
|
|
72
|
+
body = null;
|
|
73
|
+
}
|
|
74
|
+
return { ok: response.ok, status: response.status, body };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeRelayHttpBaseUrl(serverUrl) {
|
|
78
|
+
const parsed = new URL(serverUrl);
|
|
79
|
+
if (parsed.protocol === 'ws:') parsed.protocol = 'http:';
|
|
80
|
+
if (parsed.protocol === 'wss:') parsed.protocol = 'https:';
|
|
81
|
+
parsed.pathname = '';
|
|
82
|
+
parsed.search = '';
|
|
83
|
+
parsed.hash = '';
|
|
84
|
+
return parsed.toString().replace(/\/$/, '');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function inferHttpErrorCategory(status) {
|
|
88
|
+
if (status === 401) return 'auth';
|
|
89
|
+
if (status === 403) return 'policy';
|
|
90
|
+
if (status === 404) return 'input';
|
|
91
|
+
if (status === 409) return 'conflict';
|
|
92
|
+
if (status >= 400 && status < 500) return 'input';
|
|
93
|
+
return 'runtime';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function createWorldMembershipHttpError(action, response, { accountId = null, worldId = null } = {}) {
|
|
97
|
+
const backendCode = normalizeText(response?.body?.error, null);
|
|
98
|
+
const backendMessage = normalizeText(response?.body?.message, `claworld world membership ${action} failed`);
|
|
99
|
+
|
|
100
|
+
return createRuntimeBoundaryError({
|
|
101
|
+
code: backendCode || `claworld_world_membership_${action}_failed`,
|
|
102
|
+
category: inferHttpErrorCategory(response?.status),
|
|
103
|
+
status: response?.status ?? 500,
|
|
104
|
+
message: `claworld world membership ${action} failed: ${response?.status ?? 500}`,
|
|
105
|
+
publicMessage: backendMessage,
|
|
106
|
+
recoverable: Number(response?.status) >= 400 && Number(response?.status) < 500,
|
|
107
|
+
context: {
|
|
108
|
+
action: `world_membership_${action}`,
|
|
109
|
+
accountId,
|
|
110
|
+
...(worldId ? { worldId } : {}),
|
|
111
|
+
httpStatus: response?.status ?? 500,
|
|
112
|
+
...extractBackendErrorContext(response?.body),
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function fetchWorldMemberships({
|
|
118
|
+
cfg = {},
|
|
119
|
+
accountId = null,
|
|
120
|
+
runtimeConfig = null,
|
|
121
|
+
agentId = null,
|
|
122
|
+
status = null,
|
|
123
|
+
includeInactive = false,
|
|
124
|
+
includeDisabled = true,
|
|
125
|
+
fetchImpl,
|
|
126
|
+
logger = console,
|
|
127
|
+
} = {}) {
|
|
128
|
+
if (typeof fetchImpl !== 'function') {
|
|
129
|
+
throw new Error('fetch is unavailable for claworld world membership helper');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const resolvedAgentId = normalizeText(agentId, null);
|
|
133
|
+
if (!resolvedAgentId) {
|
|
134
|
+
throw new Error('claworld world membership helper requires agentId');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
|
|
138
|
+
const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
|
|
139
|
+
const requestUrl = new URL(`${baseUrl}/v1/world-memberships`);
|
|
140
|
+
requestUrl.searchParams.set('agentId', resolvedAgentId);
|
|
141
|
+
if (normalizeText(status, null)) requestUrl.searchParams.set('status', normalizeText(status, null));
|
|
142
|
+
if (includeInactive) requestUrl.searchParams.set('includeInactive', 'true');
|
|
143
|
+
requestUrl.searchParams.set('includeDisabled', includeDisabled ? 'true' : 'false');
|
|
144
|
+
const result = await fetchJson(fetchImpl, requestUrl.toString(), {
|
|
145
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
146
|
+
accept: 'application/json',
|
|
147
|
+
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
148
|
+
}),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (!result.ok) {
|
|
152
|
+
logger.error?.('[claworld:membership] world memberships fetch failed', {
|
|
153
|
+
status: result.status,
|
|
154
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
155
|
+
body: result.body,
|
|
156
|
+
});
|
|
157
|
+
throw createWorldMembershipHttpError('list', result, {
|
|
158
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return normalizeMembershipList(result.body);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export async function fetchWorldMembership({
|
|
166
|
+
cfg = {},
|
|
167
|
+
accountId = null,
|
|
168
|
+
runtimeConfig = null,
|
|
169
|
+
agentId = null,
|
|
170
|
+
worldId = null,
|
|
171
|
+
includeDisabled = true,
|
|
172
|
+
fetchImpl,
|
|
173
|
+
logger = console,
|
|
174
|
+
} = {}) {
|
|
175
|
+
if (typeof fetchImpl !== 'function') {
|
|
176
|
+
throw new Error('fetch is unavailable for claworld world membership helper');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const resolvedAgentId = normalizeText(agentId, null);
|
|
180
|
+
if (!resolvedAgentId) {
|
|
181
|
+
throw new Error('claworld world membership helper requires agentId');
|
|
182
|
+
}
|
|
183
|
+
const resolvedWorldId = normalizeText(worldId, null);
|
|
184
|
+
if (!resolvedWorldId) {
|
|
185
|
+
throw new Error('claworld world membership helper requires worldId');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
|
|
189
|
+
const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
|
|
190
|
+
const requestUrl = new URL(`${baseUrl}/v1/worlds/${encodeURIComponent(resolvedWorldId)}/membership`);
|
|
191
|
+
requestUrl.searchParams.set('agentId', resolvedAgentId);
|
|
192
|
+
requestUrl.searchParams.set('includeDisabled', includeDisabled ? 'true' : 'false');
|
|
193
|
+
const result = await fetchJson(fetchImpl, requestUrl.toString(), {
|
|
194
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
195
|
+
accept: 'application/json',
|
|
196
|
+
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
197
|
+
}),
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
if (!result.ok) {
|
|
201
|
+
logger.error?.('[claworld:membership] world membership fetch failed', {
|
|
202
|
+
status: result.status,
|
|
203
|
+
worldId: resolvedWorldId,
|
|
204
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
205
|
+
body: result.body,
|
|
206
|
+
});
|
|
207
|
+
throw createWorldMembershipHttpError('get', result, {
|
|
208
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
209
|
+
worldId: resolvedWorldId,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return normalizeManagedWorldMembership(result.body);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export async function updateWorldMembershipProfile({
|
|
217
|
+
cfg = {},
|
|
218
|
+
accountId = null,
|
|
219
|
+
runtimeConfig = null,
|
|
220
|
+
agentId = null,
|
|
221
|
+
worldId = null,
|
|
222
|
+
participantContextText = null,
|
|
223
|
+
fetchImpl,
|
|
224
|
+
logger = console,
|
|
225
|
+
} = {}) {
|
|
226
|
+
if (typeof fetchImpl !== 'function') {
|
|
227
|
+
throw new Error('fetch is unavailable for claworld world membership helper');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const resolvedAgentId = normalizeText(agentId, null);
|
|
231
|
+
if (!resolvedAgentId) {
|
|
232
|
+
throw new Error('claworld world membership helper requires agentId');
|
|
233
|
+
}
|
|
234
|
+
const resolvedWorldId = normalizeText(worldId, null);
|
|
235
|
+
if (!resolvedWorldId) {
|
|
236
|
+
throw new Error('claworld world membership helper requires worldId');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
|
|
240
|
+
const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
|
|
241
|
+
const result = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds/${encodeURIComponent(resolvedWorldId)}/membership`, {
|
|
242
|
+
method: 'PATCH',
|
|
243
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
244
|
+
accept: 'application/json',
|
|
245
|
+
'content-type': 'application/json',
|
|
246
|
+
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
247
|
+
}),
|
|
248
|
+
body: JSON.stringify({
|
|
249
|
+
agentId: resolvedAgentId,
|
|
250
|
+
participantContextText: normalizeText(participantContextText, null),
|
|
251
|
+
}),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
if (!result.ok) {
|
|
255
|
+
logger.error?.('[claworld:membership] world membership profile update failed', {
|
|
256
|
+
status: result.status,
|
|
257
|
+
worldId: resolvedWorldId,
|
|
258
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
259
|
+
body: result.body,
|
|
260
|
+
});
|
|
261
|
+
throw createWorldMembershipHttpError('update_profile', result, {
|
|
262
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
263
|
+
worldId: resolvedWorldId,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return normalizeManagedWorldMembership(result.body);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export async function leaveWorldMembership({
|
|
271
|
+
cfg = {},
|
|
272
|
+
accountId = null,
|
|
273
|
+
runtimeConfig = null,
|
|
274
|
+
agentId = null,
|
|
275
|
+
worldId = null,
|
|
276
|
+
fetchImpl,
|
|
277
|
+
logger = console,
|
|
278
|
+
} = {}) {
|
|
279
|
+
if (typeof fetchImpl !== 'function') {
|
|
280
|
+
throw new Error('fetch is unavailable for claworld world membership helper');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const resolvedAgentId = normalizeText(agentId, null);
|
|
284
|
+
if (!resolvedAgentId) {
|
|
285
|
+
throw new Error('claworld world membership helper requires agentId');
|
|
286
|
+
}
|
|
287
|
+
const resolvedWorldId = normalizeText(worldId, null);
|
|
288
|
+
if (!resolvedWorldId) {
|
|
289
|
+
throw new Error('claworld world membership helper requires worldId');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const resolvedRuntimeConfig = runtimeConfig || resolveClaworldRuntimeConfig(cfg, accountId);
|
|
293
|
+
const baseUrl = normalizeRelayHttpBaseUrl(resolvedRuntimeConfig.serverUrl);
|
|
294
|
+
const result = await fetchJson(fetchImpl, `${baseUrl}/v1/worlds/${encodeURIComponent(resolvedWorldId)}/membership/leave`, {
|
|
295
|
+
method: 'POST',
|
|
296
|
+
headers: buildRuntimeAuthHeaders(resolvedRuntimeConfig, {
|
|
297
|
+
accept: 'application/json',
|
|
298
|
+
'content-type': 'application/json',
|
|
299
|
+
...(resolvedRuntimeConfig.apiKey ? { 'x-api-key': resolvedRuntimeConfig.apiKey } : {}),
|
|
300
|
+
}),
|
|
301
|
+
body: JSON.stringify({
|
|
302
|
+
agentId: resolvedAgentId,
|
|
303
|
+
}),
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
if (!result.ok) {
|
|
307
|
+
logger.error?.('[claworld:membership] world membership leave failed', {
|
|
308
|
+
status: result.status,
|
|
309
|
+
worldId: resolvedWorldId,
|
|
310
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
311
|
+
body: result.body,
|
|
312
|
+
});
|
|
313
|
+
throw createWorldMembershipHttpError('leave', result, {
|
|
314
|
+
accountId: resolvedRuntimeConfig.accountId || accountId || null,
|
|
315
|
+
worldId: resolvedWorldId,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return normalizeManagedWorldMembership(result.body);
|
|
320
|
+
}
|
|
@@ -2,6 +2,7 @@ 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
4
|
import { extractBackendErrorContext } from './backend-error-context.js';
|
|
5
|
+
import { normalizeWorldJoinResponse } from './product-shell-helper.js';
|
|
5
6
|
|
|
6
7
|
function normalizeText(value, fallback = null) {
|
|
7
8
|
if (value == null) return fallback;
|
|
@@ -85,6 +86,20 @@ function normalizeManagedWorld(payload = {}) {
|
|
|
85
86
|
};
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
function normalizeCreatedWorld(payload = {}) {
|
|
90
|
+
const world = normalizeManagedWorld(payload);
|
|
91
|
+
return {
|
|
92
|
+
...world,
|
|
93
|
+
ownerJoin:
|
|
94
|
+
payload.ownerJoin && typeof payload.ownerJoin === 'object'
|
|
95
|
+
? normalizeWorldJoinResponse(payload.ownerJoin, {
|
|
96
|
+
worldId: world.worldId,
|
|
97
|
+
agentId: world.ownerAgentId,
|
|
98
|
+
})
|
|
99
|
+
: null,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
88
103
|
function normalizeOwnedWorldSummary(payload = {}) {
|
|
89
104
|
return {
|
|
90
105
|
worldId: normalizeText(payload.worldId, null),
|
|
@@ -174,6 +189,7 @@ export async function createModeratedWorld({
|
|
|
174
189
|
agentId = null,
|
|
175
190
|
displayName = null,
|
|
176
191
|
worldContextText = null,
|
|
192
|
+
participantContextText = null,
|
|
177
193
|
enabled = true,
|
|
178
194
|
fetchImpl,
|
|
179
195
|
logger = console,
|
|
@@ -200,6 +216,7 @@ export async function createModeratedWorld({
|
|
|
200
216
|
agentId: resolvedAgentId,
|
|
201
217
|
displayName,
|
|
202
218
|
worldContextText,
|
|
219
|
+
participantContextText: normalizeText(participantContextText, null),
|
|
203
220
|
enabled,
|
|
204
221
|
}),
|
|
205
222
|
});
|
|
@@ -215,7 +232,7 @@ export async function createModeratedWorld({
|
|
|
215
232
|
});
|
|
216
233
|
}
|
|
217
234
|
|
|
218
|
-
return
|
|
235
|
+
return normalizeCreatedWorld(created.body);
|
|
219
236
|
}
|
|
220
237
|
|
|
221
238
|
export async function fetchOwnedWorlds({
|
|
@@ -259,6 +259,11 @@ function normalizeDeliveryReason(reason = {}) {
|
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
+
function normalizeWorldRole(worldRole, fallback = null) {
|
|
263
|
+
const normalized = normalizeText(worldRole, fallback);
|
|
264
|
+
return ['owner', 'member'].includes(normalized) ? normalized : fallback;
|
|
265
|
+
}
|
|
266
|
+
|
|
262
267
|
function normalizeCandidate(candidate = {}, index = 0) {
|
|
263
268
|
const normalizedRank = normalizeNumber(candidate.rank, null);
|
|
264
269
|
const displayName = normalizeText(
|
|
@@ -280,6 +285,7 @@ function normalizeCandidate(candidate = {}, index = 0) {
|
|
|
280
285
|
return {
|
|
281
286
|
candidateId: normalizeText(candidate.candidateId, `candidate_${index + 1}`),
|
|
282
287
|
worldId: normalizeText(candidate.worldId, 'unknown-world'),
|
|
288
|
+
worldRole: normalizeWorldRole(candidate.worldRole, null),
|
|
283
289
|
sourceMembershipId: normalizeText(candidate.sourceMembershipId, null),
|
|
284
290
|
online: candidate.online === true,
|
|
285
291
|
displayName,
|
|
@@ -622,6 +628,7 @@ export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail
|
|
|
622
628
|
.filter(Boolean);
|
|
623
629
|
const deliveryReasonSummary = sentenceCase(candidate.deliveryReason.summary, '');
|
|
624
630
|
const availabilitySummary = candidate.online === true ? 'Online now.' : 'Currently offline.';
|
|
631
|
+
const roleSummary = candidate.worldRole ? `World role: ${candidate.worldRole}.` : null;
|
|
625
632
|
const scoreSummary = candidate.score == null
|
|
626
633
|
? null
|
|
627
634
|
: `Score ${candidate.score}${candidate.rank == null ? '' : `, rank ${candidate.rank}`}.`;
|
|
@@ -631,6 +638,7 @@ export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail
|
|
|
631
638
|
optionalFieldSummary.length > 0 ? `Optional context: ${optionalFieldSummary.join('; ')}.` : null,
|
|
632
639
|
compatibilitySummary.length > 0 ? compatibilitySummary.join(' ') : null,
|
|
633
640
|
deliveryReasonSummary || null,
|
|
641
|
+
roleSummary,
|
|
634
642
|
availabilitySummary,
|
|
635
643
|
scoreSummary,
|
|
636
644
|
].filter(Boolean).join(' ');
|
|
@@ -639,6 +647,7 @@ export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail
|
|
|
639
647
|
candidateId: candidate.candidateId,
|
|
640
648
|
sourceMembershipId: candidate.sourceMembershipId,
|
|
641
649
|
online: candidate.online === true,
|
|
650
|
+
worldRole: candidate.worldRole,
|
|
642
651
|
agentCode: candidate.agentCode,
|
|
643
652
|
requestChat: candidate.requestChat,
|
|
644
653
|
displayName: name,
|