@xfxstudio/claworld 0.2.12 → 0.2.14

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 (62) hide show
  1. package/README.md +45 -19
  2. package/index.js +0 -1
  3. package/openclaw.plugin.json +1 -1
  4. package/package.json +1 -5
  5. package/skills/claworld-help/SKILL.md +84 -91
  6. package/skills/claworld-join-and-chat/SKILL.md +9 -9
  7. package/src/openclaw/index.js +0 -3
  8. package/src/openclaw/plugin/account-identity.js +0 -1
  9. package/src/openclaw/plugin/claworld-channel-plugin.js +8 -253
  10. package/src/openclaw/plugin/managed-config.js +1 -7
  11. package/src/openclaw/plugin/onboarding.js +128 -103
  12. package/src/openclaw/plugin/register.js +183 -232
  13. package/src/openclaw/plugin/relay-client.js +8 -5
  14. package/src/openclaw/runtime/product-shell-helper.js +11 -364
  15. package/src/openclaw/runtime/tool-contracts.js +0 -182
  16. package/src/openclaw/runtime/tool-inventory.js +4 -27
  17. package/bin/claworld.mjs +0 -9
  18. package/src/lib/agent-profile.js +0 -74
  19. package/src/lib/http-auth.js +0 -151
  20. package/src/lib/policy.js +0 -114
  21. package/src/openclaw/installer/cli.js +0 -406
  22. package/src/openclaw/installer/constants.js +0 -14
  23. package/src/openclaw/installer/core.js +0 -2122
  24. package/src/openclaw/installer/doctor.js +0 -876
  25. package/src/openclaw/installer/workspace-contract.js +0 -427
  26. package/src/product-shell/agent-cards/card-routes.js +0 -64
  27. package/src/product-shell/agent-cards/card-service.js +0 -287
  28. package/src/product-shell/agent-cards/spec-builder.js +0 -167
  29. package/src/product-shell/agent-cards/storage/image-host-storage.js +0 -192
  30. package/src/product-shell/agent-cards/storage/local-public-storage.js +0 -74
  31. package/src/product-shell/agent-cards/svg-renderer.js +0 -325
  32. package/src/product-shell/agent-cards/template-registry.js +0 -131
  33. package/src/product-shell/catalog/default-world-catalog.js +0 -38
  34. package/src/product-shell/contracts/candidate-feed.js +0 -393
  35. package/src/product-shell/contracts/world-manifest.js +0 -369
  36. package/src/product-shell/conversation-feedback/conversation-feedback-service.js +0 -261
  37. package/src/product-shell/feedback/feedback-contract.js +0 -13
  38. package/src/product-shell/feedback/feedback-routes.js +0 -98
  39. package/src/product-shell/feedback/feedback-service.js +0 -252
  40. package/src/product-shell/index.js +0 -212
  41. package/src/product-shell/matching/matchmaking-service.js +0 -395
  42. package/src/product-shell/membership/membership-service.js +0 -284
  43. package/src/product-shell/onboarding/onboarding-routes.js +0 -37
  44. package/src/product-shell/onboarding/onboarding-service.js +0 -220
  45. package/src/product-shell/orchestration/world-conversation-orchestrator.js +0 -28
  46. package/src/product-shell/profile/profile-service.js +0 -142
  47. package/src/product-shell/profile/public-identity-routes.js +0 -160
  48. package/src/product-shell/profile/public-identity-service.js +0 -192
  49. package/src/product-shell/search/search-service.js +0 -393
  50. package/src/product-shell/social/chat-request-approval-policy.js +0 -332
  51. package/src/product-shell/social/chat-request-routes.js +0 -130
  52. package/src/product-shell/social/chat-request-service.js +0 -723
  53. package/src/product-shell/social/friend-routes.js +0 -82
  54. package/src/product-shell/social/friend-service.js +0 -557
  55. package/src/product-shell/social/social-routes.js +0 -21
  56. package/src/product-shell/social/social-service.js +0 -136
  57. package/src/product-shell/worlds/world-admin-service.js +0 -486
  58. package/src/product-shell/worlds/world-authorization.js +0 -136
  59. package/src/product-shell/worlds/world-broadcast-service.js +0 -296
  60. package/src/product-shell/worlds/world-routes.js +0 -403
  61. package/src/product-shell/worlds/world-service.js +0 -89
  62. package/src/product-shell/worlds/world-text.js +0 -75
@@ -1,395 +0,0 @@
1
- import { buildCandidateFeed, normalizeCandidateFeedLimit, projectCandidateFeedModel } from '../contracts/candidate-feed.js';
2
- import { WORLD_ACTIONS } from '../worlds/world-authorization.js';
3
-
4
- const DATING_DEMO_SCORING_SIGNALS = Object.freeze([
5
- {
6
- signalId: 'intent_exact_match',
7
- label: 'Intent Exact Match',
8
- weight: 50,
9
- sourceFieldIds: ['intent'],
10
- summary: 'Add 50 points when both active memberships declare the same normalized intent.',
11
- },
12
- {
13
- signalId: 'same_location',
14
- label: 'Same Location',
15
- weight: 30,
16
- sourceFieldIds: ['location'],
17
- summary: 'Add 30 points when both active memberships declare the same normalized location.',
18
- },
19
- {
20
- signalId: 'shared_interests',
21
- label: 'Shared Interests',
22
- weight: 20,
23
- sourceFieldIds: ['interests'],
24
- summary: 'Add 10 points per shared normalized interest, capped at 20 points.',
25
- },
26
- ]);
27
-
28
- function createConfigurationError() {
29
- const error = new Error('membership_store_unavailable');
30
- error.code = 'membership_store_unavailable';
31
- error.status = 500;
32
- return error;
33
- }
34
-
35
- function createAgentNotFoundError(agentId) {
36
- const error = new Error(`agent_not_found:${agentId}`);
37
- error.code = 'agent_not_found';
38
- error.status = 404;
39
- return error;
40
- }
41
-
42
- function createInvalidCandidateFeedRequestError(fieldId, message = `${fieldId} is required`) {
43
- const error = new Error(`invalid_candidate_feed_request:${fieldId}`);
44
- error.code = 'invalid_candidate_feed_request';
45
- error.status = 400;
46
- error.responseBody = {
47
- error: error.code,
48
- message: 'candidate feed request is missing required fields',
49
- fieldErrors: [
50
- {
51
- fieldId,
52
- message,
53
- },
54
- ],
55
- };
56
- return error;
57
- }
58
-
59
- function createCandidateFeedMembershipNotActiveError(worldId, agentId) {
60
- const error = new Error(`candidate_feed_membership_not_active:${worldId}:${agentId}`);
61
- error.code = 'candidate_feed_membership_not_active';
62
- error.status = 409;
63
- error.responseBody = {
64
- error: error.code,
65
- message: 'agent must have an active world membership before requesting candidate feed',
66
- worldId,
67
- agentId,
68
- requiredMembershipStatus: 'active',
69
- };
70
- return error;
71
- }
72
-
73
- function normalizeAgentId(agentId) {
74
- const normalized = String(agentId || '').trim();
75
- return normalized || null;
76
- }
77
-
78
- function normalizeText(value) {
79
- return String(value || '').trim().toLowerCase();
80
- }
81
-
82
- function normalizeStringList(value) {
83
- if (!Array.isArray(value)) return [];
84
- return [...new Set(
85
- value
86
- .map((item) => normalizeText(item))
87
- .filter(Boolean),
88
- )].sort((left, right) => left.localeCompare(right));
89
- }
90
-
91
- function projectScoringSignals(worldId) {
92
- if (worldId !== 'dating-demo-world') return [];
93
- return DATING_DEMO_SCORING_SIGNALS.map((signal) => ({ ...signal }));
94
- }
95
-
96
- function buildStrategy(world) {
97
- return {
98
- worldId: world.worldId,
99
- mode: world.matching.mode,
100
- cadence: world.matching.cadence,
101
- strategySummary: world.matching.strategySummary,
102
- candidateSources: world.matching.candidateSources,
103
- inputFields: world.joinSchema.requiredFields.map((field) => field.fieldId),
104
- candidateFeedModel: projectCandidateFeedModel(world),
105
- scoringSignals: projectScoringSignals(world.worldId),
106
- status: world.worldId === 'dating-demo-world' ? 'candidate_scoring_ready' : 'scaffold_ready',
107
- };
108
- }
109
-
110
- function buildInterestBreakdown(requesterInterests, candidateInterests) {
111
- const sharedValues = requesterInterests.filter((interest) => candidateInterests.includes(interest));
112
- const contribution = Math.min(sharedValues.length * 10, 20);
113
-
114
- return {
115
- signalId: 'shared_interests',
116
- label: 'Shared Interests',
117
- weight: 20,
118
- sourceFieldIds: ['interests'],
119
- matched: sharedValues.length > 0,
120
- requesterValue: requesterInterests,
121
- candidateValue: candidateInterests,
122
- sharedValues,
123
- overlapCount: sharedValues.length,
124
- contribution,
125
- };
126
- }
127
-
128
- function buildDatingDemoScoreDetails(viewerMembership, candidateMembership) {
129
- const viewerIntent = normalizeText(viewerMembership.profileSnapshot?.intent);
130
- const candidateIntent = normalizeText(candidateMembership.profileSnapshot?.intent);
131
- const viewerLocation = normalizeText(viewerMembership.profileSnapshot?.location);
132
- const candidateLocation = normalizeText(candidateMembership.profileSnapshot?.location);
133
- const viewerInterests = normalizeStringList(viewerMembership.profileSnapshot?.interests);
134
- const candidateInterests = normalizeStringList(candidateMembership.profileSnapshot?.interests);
135
-
136
- const scoreBreakdown = [
137
- {
138
- signalId: 'intent_exact_match',
139
- label: 'Intent Exact Match',
140
- weight: 50,
141
- sourceFieldIds: ['intent'],
142
- matched: viewerIntent !== '' && viewerIntent === candidateIntent,
143
- requesterValue: viewerIntent,
144
- candidateValue: candidateIntent,
145
- contribution: viewerIntent !== '' && viewerIntent === candidateIntent ? 50 : 0,
146
- },
147
- {
148
- signalId: 'same_location',
149
- label: 'Same Location',
150
- weight: 30,
151
- sourceFieldIds: ['location'],
152
- matched: viewerLocation !== '' && viewerLocation === candidateLocation,
153
- requesterValue: viewerLocation,
154
- candidateValue: candidateLocation,
155
- contribution: viewerLocation !== '' && viewerLocation === candidateLocation ? 30 : 0,
156
- },
157
- buildInterestBreakdown(viewerInterests, candidateInterests),
158
- ];
159
-
160
- return {
161
- joinedAt: candidateMembership.joinedAt,
162
- score: scoreBreakdown.reduce((sum, signal) => sum + signal.contribution, 0),
163
- scoreBreakdown,
164
- scoringInputs: {
165
- requester: {
166
- intent: viewerIntent,
167
- location: viewerLocation,
168
- interests: viewerInterests,
169
- },
170
- candidate: {
171
- intent: candidateIntent,
172
- location: candidateLocation,
173
- interests: candidateInterests,
174
- },
175
- overlap: {
176
- sharedInterests: scoreBreakdown[2].sharedValues,
177
- },
178
- },
179
- };
180
- }
181
-
182
- function compareRankedCandidates(left, right) {
183
- if (right.score !== left.score) return right.score - left.score;
184
-
185
- const rightSharedInterestCount = right.scoringInputs.overlap.sharedInterests.length;
186
- const leftSharedInterestCount = left.scoringInputs.overlap.sharedInterests.length;
187
- if (rightSharedInterestCount !== leftSharedInterestCount) {
188
- return rightSharedInterestCount - leftSharedInterestCount;
189
- }
190
-
191
- if (left.joinedAt !== right.joinedAt) {
192
- return String(left.joinedAt).localeCompare(String(right.joinedAt));
193
- }
194
-
195
- return String(left.sourceMembershipId).localeCompare(String(right.sourceMembershipId));
196
- }
197
-
198
- function buildViewerContext({ world, membershipStore, normalizedAgentId, worldAuthorizationService }) {
199
- const viewerAgent = membershipStore.getAgent(normalizedAgentId);
200
- if (!viewerAgent) {
201
- throw createAgentNotFoundError(normalizedAgentId);
202
- }
203
-
204
- const authorization = worldAuthorizationService.evaluateWorldAction({
205
- worldId: world.worldId,
206
- actorAgentId: normalizedAgentId,
207
- action: WORLD_ACTIONS.VIEW_CANDIDATE_FEED,
208
- });
209
-
210
- if (!authorization.allowed || authorization.membership?.status !== 'active') {
211
- throw createCandidateFeedMembershipNotActiveError(world.worldId, normalizedAgentId);
212
- }
213
-
214
- return {
215
- viewerAgent,
216
- viewerMembership: authorization.membership,
217
- };
218
- }
219
-
220
- function buildBaseFeed({
221
- world,
222
- membershipStore,
223
- normalizedAgentId,
224
- limit,
225
- worldAuthorizationService,
226
- resolvePresence,
227
- }) {
228
- const { viewerAgent, viewerMembership } = buildViewerContext({
229
- world,
230
- membershipStore,
231
- normalizedAgentId,
232
- worldAuthorizationService,
233
- });
234
- const activeMemberships = membershipStore.listMemberships({
235
- worldId: world.worldId,
236
- status: 'active',
237
- });
238
- const normalizedLimit = normalizeCandidateFeedLimit(limit);
239
- const nowMs = typeof membershipStore.nowMs === 'function' ? membershipStore.nowMs() : Date.now();
240
- const baseFeed = buildCandidateFeed({
241
- world,
242
- viewerMembership,
243
- viewerAgent,
244
- candidateMemberships: activeMemberships,
245
- getAgent: (candidateAgentId) => membershipStore.getAgent(candidateAgentId),
246
- getPresence: (candidateAgentId) => resolvePresence(candidateAgentId),
247
- nowMs,
248
- limit: activeMemberships.length,
249
- });
250
-
251
- return {
252
- viewerMembership,
253
- activeMemberships,
254
- normalizedLimit,
255
- baseFeed,
256
- };
257
- }
258
-
259
- function buildDatingDemoFeed({
260
- world,
261
- membershipStore,
262
- normalizedAgentId,
263
- limit,
264
- worldAuthorizationService,
265
- resolvePresence,
266
- conversationFeedbackService = null,
267
- }) {
268
- const { viewerMembership, activeMemberships, normalizedLimit, baseFeed } = buildBaseFeed({
269
- world,
270
- membershipStore,
271
- normalizedAgentId,
272
- limit,
273
- worldAuthorizationService,
274
- resolvePresence,
275
- });
276
- const membershipById = new Map(activeMemberships.map((membership) => [membership.membershipId, membership]));
277
- const rankedCandidates = baseFeed.candidates
278
- .map((candidate) => {
279
- const candidateMembership = membershipById.get(candidate.sourceMembershipId);
280
- return {
281
- ...candidate,
282
- worldFeedbackSummary: conversationFeedbackService?.summarizeWorldAgent?.({
283
- worldId: world.worldId,
284
- agentId: candidate.targetAgentId,
285
- }) || {
286
- likesReceived: 0,
287
- dislikesReceived: 0,
288
- },
289
- ...buildDatingDemoScoreDetails(viewerMembership, candidateMembership),
290
- };
291
- })
292
- .sort(compareRankedCandidates);
293
-
294
- const candidates = rankedCandidates
295
- .slice(0, normalizedLimit)
296
- .map((candidate, index) => ({
297
- ...candidate,
298
- rank: index + 1,
299
- }));
300
-
301
- return {
302
- ...baseFeed,
303
- agentId: normalizedAgentId,
304
- limit: normalizedLimit,
305
- candidateSource: 'active_memberships_online',
306
- strategy: buildStrategy(world),
307
- totalCandidates: rankedCandidates.length,
308
- candidates,
309
- };
310
- }
311
-
312
- export function createMatchmakingService({
313
- worldService,
314
- worldAuthorizationService,
315
- store = null,
316
- presence = null,
317
- conversationFeedbackService = null,
318
- } = {}) {
319
- function assertStore() {
320
- if (!store) throw createConfigurationError();
321
- return store;
322
- }
323
-
324
- function resolvePresence(agentId) {
325
- if (!presence) {
326
- return {
327
- online: true,
328
- connectedAt: null,
329
- lastHeartbeatAt: null,
330
- };
331
- }
332
- return presence.getPresence(agentId);
333
- }
334
-
335
- return {
336
- describeStrategy(worldId) {
337
- const world = worldService.requireWorld(worldId);
338
- return buildStrategy(world);
339
- },
340
- listCandidateFeed({ worldId, agentId, limit } = {}) {
341
- const world = worldService.requireWorld(worldId);
342
- const membershipStore = assertStore();
343
- const normalizedAgentId = normalizeAgentId(agentId);
344
-
345
- if (!normalizedAgentId) {
346
- throw createInvalidCandidateFeedRequestError('agentId');
347
- }
348
-
349
- if (world.worldId === 'dating-demo-world') {
350
- return buildDatingDemoFeed({
351
- world,
352
- membershipStore,
353
- normalizedAgentId,
354
- limit,
355
- worldAuthorizationService,
356
- resolvePresence,
357
- conversationFeedbackService,
358
- });
359
- }
360
-
361
- const { normalizedLimit, baseFeed } = buildBaseFeed({
362
- world,
363
- membershipStore,
364
- normalizedAgentId,
365
- limit,
366
- worldAuthorizationService,
367
- resolvePresence,
368
- });
369
-
370
- return {
371
- ...baseFeed,
372
- agentId: normalizedAgentId,
373
- limit: normalizedLimit,
374
- candidateSource: 'active_memberships_online',
375
- strategy: buildStrategy(world),
376
- totalCandidates: baseFeed.candidates.length,
377
- candidates: baseFeed.candidates
378
- .slice(0, normalizedLimit)
379
- .map((candidate) => ({
380
- ...candidate,
381
- worldFeedbackSummary: conversationFeedbackService?.summarizeWorldAgent?.({
382
- worldId: world.worldId,
383
- agentId: candidate.targetAgentId,
384
- }) || {
385
- likesReceived: 0,
386
- dislikesReceived: 0,
387
- },
388
- })),
389
- };
390
- },
391
- listCandidates(options = {}) {
392
- return this.listCandidateFeed(options);
393
- },
394
- };
395
- }
@@ -1,284 +0,0 @@
1
- import { buildResolvedWorldJoinOrchestration } from '../contracts/world-orchestration.js';
2
- import { buildParticipantContextText } from '../worlds/world-text.js';
3
-
4
- function normalizeText(value, fallback = null) {
5
- if (value == null) return fallback;
6
- const normalized = String(value).trim();
7
- return normalized || fallback;
8
- }
9
-
10
- function createConfigurationError() {
11
- const error = new Error('membership_store_unavailable');
12
- error.code = 'membership_store_unavailable';
13
- error.status = 500;
14
- return error;
15
- }
16
-
17
- function createAgentNotFoundError(agentId) {
18
- const error = new Error(`agent_not_found:${agentId}`);
19
- error.code = 'agent_not_found';
20
- error.status = 404;
21
- return error;
22
- }
23
-
24
- function createInvalidJoinRequestError(fieldId, message = `${fieldId} is required`) {
25
- const error = new Error(`invalid_join_request:${fieldId}`);
26
- error.code = 'invalid_join_request';
27
- error.status = 400;
28
- error.responseBody = {
29
- error: error.code,
30
- message: 'join request is missing required fields',
31
- fieldErrors: [
32
- {
33
- fieldId,
34
- message,
35
- },
36
- ],
37
- };
38
- return error;
39
- }
40
-
41
- function normalizeAgentId(agentId) {
42
- const normalized = String(agentId || '').trim();
43
- return normalized || null;
44
- }
45
-
46
- function normalizeProfileSnapshot(profileSnapshot = null, participantContextText = null) {
47
- const base = profileSnapshot && typeof profileSnapshot === 'object' && !Array.isArray(profileSnapshot)
48
- ? { ...profileSnapshot }
49
- : {};
50
- const normalizedParticipantContextText = normalizeText(
51
- participantContextText,
52
- normalizeText(base.participantContextText, null),
53
- );
54
- if (normalizedParticipantContextText) {
55
- base.participantContextText = normalizedParticipantContextText;
56
- }
57
- return base;
58
- }
59
-
60
- function buildNextStageSummary() {
61
- return {
62
- stage: 'candidate_review',
63
- summary:
64
- 'Review the backend-authored candidate feed or use world search, then choose one target agent and create a world-scoped chat request.',
65
- };
66
- }
67
-
68
- export function createMembershipService({ worldService, store = null, publicIdentityService = null } = {}) {
69
- function assertStore() {
70
- if (!store) throw createConfigurationError();
71
- return store;
72
- }
73
-
74
- function resolveNormalizedParticipantContextText({ participantContextText = null, profileSnapshot = null, agent = null, world = null } = {}) {
75
- return buildParticipantContextText({
76
- world,
77
- agent,
78
- participantContextText,
79
- profileSnapshot,
80
- });
81
- }
82
-
83
- return {
84
- evaluateJoin({ worldId, participantContextText = null, profile = null, profileSnapshot = null } = {}) {
85
- const world = worldService.requireWorld(worldId);
86
- const normalizedParticipantContextText = resolveNormalizedParticipantContextText({
87
- world,
88
- participantContextText,
89
- profileSnapshot: profileSnapshot || profile,
90
- });
91
- const accepted = Boolean(normalizedParticipantContextText);
92
-
93
- return {
94
- worldId: world.worldId,
95
- accepted,
96
- status: accepted ? 'eligible' : 'needs_profile',
97
- participantContextText: normalizedParticipantContextText,
98
- missingFields: accepted
99
- ? []
100
- : [
101
- {
102
- fieldId: 'participantContextText',
103
- label: 'Entry Profile',
104
- description: 'A short text describing who you are in this world and what context you bring into it.',
105
- },
106
- ],
107
- nextMissingField: accepted
108
- ? null
109
- : {
110
- fieldId: 'participantContextText',
111
- label: 'Entry Profile',
112
- description: 'A short text describing who you are in this world and what context you bring into it.',
113
- },
114
- missingFieldGuidance: {
115
- mode: accepted ? 'complete' : 'single_text_field',
116
- orderedMissingFields: accepted
117
- ? []
118
- : [
119
- {
120
- fieldId: 'participantContextText',
121
- label: 'Entry Profile',
122
- description: 'A short text describing who you are in this world and what context you bring into it.',
123
- },
124
- ],
125
- orderedMissingFieldIds: accepted ? [] : ['participantContextText'],
126
- nextMissingField: accepted
127
- ? null
128
- : {
129
- fieldId: 'participantContextText',
130
- label: 'Entry Profile',
131
- description: 'A short text describing who you are in this world and what context you bring into it.',
132
- },
133
- remainingRequiredFieldCount: accepted ? 0 : 1,
134
- },
135
- nextAction: accepted ? 'call_join_world' : 'retry_join_world_after_profile_update',
136
- };
137
- },
138
-
139
- async createMembership({ worldId, agentId, participantContextText, profile = null, profileSnapshot = null } = {}) {
140
- const world = worldService.requireWorld(worldId);
141
- const membershipStore = assertStore();
142
- const normalizedAgentId = normalizeAgentId(agentId);
143
- if (!normalizedAgentId) throw createInvalidJoinRequestError('agentId');
144
-
145
- const agent = membershipStore.getAgent(normalizedAgentId);
146
- if (!agent) throw createAgentNotFoundError(normalizedAgentId);
147
-
148
- const normalizedParticipantContextText = resolveNormalizedParticipantContextText({
149
- world,
150
- agent,
151
- participantContextText,
152
- profileSnapshot: profileSnapshot || profile,
153
- });
154
- if (!normalizedParticipantContextText) {
155
- throw createInvalidJoinRequestError(
156
- 'participantContextText',
157
- 'participantContextText is required',
158
- );
159
- }
160
-
161
- const existingMembership = membershipStore.listMemberships({
162
- worldId,
163
- agentId: normalizedAgentId,
164
- })[0] || null;
165
-
166
- if (existingMembership) {
167
- return { membership: existingMembership, created: false };
168
- }
169
-
170
- const membership = await membershipStore.createMembership({
171
- worldId,
172
- agentId: normalizedAgentId,
173
- status: 'joined',
174
- profileSnapshot: normalizeProfileSnapshot(profileSnapshot || profile, normalizedParticipantContextText),
175
- participantContextText: normalizedParticipantContextText,
176
- });
177
-
178
- return { membership, created: true };
179
- },
180
-
181
- async joinWorld({
182
- worldId,
183
- agentId,
184
- participantContextText,
185
- profile = null,
186
- profileSnapshot = null,
187
- } = {}) {
188
- const world = worldService.requireWorld(worldId);
189
- const membershipStore = assertStore();
190
- const normalizedAgentId = normalizeAgentId(agentId);
191
-
192
- if (!normalizedAgentId) throw createInvalidJoinRequestError('agentId');
193
-
194
- const agent = membershipStore.getAgent(normalizedAgentId);
195
- if (!agent) throw createAgentNotFoundError(normalizedAgentId);
196
- publicIdentityService?.assertPublicIdentityReady?.({
197
- agentId: normalizedAgentId,
198
- capability: 'join world',
199
- });
200
-
201
- const normalizedParticipantContextText = resolveNormalizedParticipantContextText({
202
- world,
203
- agent,
204
- participantContextText,
205
- profileSnapshot: profileSnapshot || profile,
206
- });
207
- if (!normalizedParticipantContextText) {
208
- throw createInvalidJoinRequestError(
209
- 'participantContextText',
210
- 'participantContextText is required',
211
- );
212
- }
213
-
214
- const existingMembership = membershipStore.listMemberships({
215
- worldId: world.worldId,
216
- agentId: normalizedAgentId,
217
- })[0] || null;
218
-
219
- const membership = existingMembership
220
- ? await membershipStore.updateMembership(existingMembership.membershipId, {
221
- status: 'active',
222
- profileSnapshot: normalizeProfileSnapshot(
223
- {
224
- ...(existingMembership.profileSnapshot && typeof existingMembership.profileSnapshot === 'object'
225
- ? existingMembership.profileSnapshot
226
- : {}),
227
- ...((profileSnapshot || profile) && typeof (profileSnapshot || profile) === 'object' && !Array.isArray(profileSnapshot || profile)
228
- ? (profileSnapshot || profile)
229
- : {}),
230
- },
231
- normalizedParticipantContextText,
232
- ),
233
- participantContextText: normalizedParticipantContextText,
234
- })
235
- : await membershipStore.createMembership({
236
- worldId: world.worldId,
237
- agentId: normalizedAgentId,
238
- status: 'active',
239
- profileSnapshot: normalizeProfileSnapshot(profileSnapshot || profile, normalizedParticipantContextText),
240
- participantContextText: normalizedParticipantContextText,
241
- });
242
-
243
- const joinResult = {
244
- status: 'joined',
245
- worldId: world.worldId,
246
- membership,
247
- membershipStatus: membership.status,
248
- created: !existingMembership,
249
- participantContextText: normalizedParticipantContextText,
250
- nextAction: 'review_candidate_feed',
251
- nextStageSummary: buildNextStageSummary(),
252
- };
253
-
254
- return {
255
- ...joinResult,
256
- orchestration: buildResolvedWorldJoinOrchestration({
257
- joinResult,
258
- candidateDelivery: null,
259
- }) || null,
260
- };
261
- },
262
-
263
- getMembership({ worldId, agentId, includeDisabled = false } = {}) {
264
- const normalizedAgentId = normalizeAgentId(agentId);
265
- if (!normalizedAgentId) return null;
266
- worldService.requireWorld(worldId, { includeDisabled });
267
- return assertStore().listMemberships({ worldId, agentId: normalizedAgentId })[0] || null;
268
- },
269
-
270
- listMemberships({ worldId, agentId = null, status = null, includeDisabled = false } = {}) {
271
- worldService.requireWorld(worldId, { includeDisabled });
272
- return assertStore().listMemberships({ worldId, agentId, status });
273
- },
274
-
275
- listMembershipsAcrossWorlds({ agentId = null, status = null } = {}) {
276
- return assertStore().listMemberships({ agentId, status });
277
- },
278
-
279
- countMemberships({ worldId, agentId = null, status = null, includeDisabled = false } = {}) {
280
- worldService.requireWorld(worldId, { includeDisabled });
281
- return assertStore().countMemberships({ worldId, agentId, status });
282
- },
283
- };
284
- }