@xfxstudio/claworld 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +12 -29
  2. package/openclaw.plugin.json +9 -33
  3. package/package.json +2 -10
  4. package/skills/claworld-help/SKILL.md +86 -160
  5. package/skills/claworld-join-and-chat/SKILL.md +107 -203
  6. package/skills/claworld-manage-worlds/SKILL.md +75 -392
  7. package/src/lib/chat-request.js +347 -0
  8. package/src/lib/{accepted-chat-kickoff.js → relay/kickoff-text.js} +67 -26
  9. package/src/openclaw/index.js +0 -5
  10. package/src/openclaw/installer/cli.js +14 -16
  11. package/src/openclaw/installer/core.js +13 -14
  12. package/src/openclaw/installer/doctor.js +69 -31
  13. package/src/openclaw/installer/workspace-contract.js +33 -9
  14. package/src/openclaw/plugin/claworld-channel-plugin.js +156 -625
  15. package/src/openclaw/plugin/config-schema.js +4 -16
  16. package/src/openclaw/plugin/managed-config.js +127 -75
  17. package/src/openclaw/plugin/onboarding.js +7 -3
  18. package/src/openclaw/plugin/register.js +40 -339
  19. package/src/openclaw/plugin/relay-client.js +112 -102
  20. package/src/openclaw/protocol/relay-event-protocol.js +34 -22
  21. package/src/openclaw/runtime/canonical-result-builder.js +15 -5
  22. package/src/openclaw/runtime/demo-session-bootstrap.js +0 -4
  23. package/src/openclaw/runtime/feedback-helper.js +3 -2
  24. package/src/openclaw/runtime/inbound-session-router.js +28 -20
  25. package/src/openclaw/runtime/outbound-session-bridge.js +21 -9
  26. package/src/openclaw/runtime/product-shell-helper.js +45 -637
  27. package/src/openclaw/runtime/runtime-path.js +2 -2
  28. package/src/openclaw/runtime/system-message-orchestrator.js +1 -1
  29. package/src/openclaw/runtime/tool-contracts.js +36 -258
  30. package/src/openclaw/runtime/world-moderation-helper.js +11 -65
  31. package/src/product-shell/catalog/default-world-catalog.js +15 -33
  32. package/src/product-shell/contracts/candidate-feed.js +40 -5
  33. package/src/product-shell/contracts/chat-request-approval-policy.js +3 -3
  34. package/src/product-shell/contracts/world-manifest.js +134 -161
  35. package/src/product-shell/contracts/world-orchestration.js +55 -326
  36. package/src/product-shell/feedback/feedback-routes.js +4 -3
  37. package/src/product-shell/feedback/feedback-service.js +11 -8
  38. package/src/product-shell/index.js +6 -7
  39. package/src/product-shell/matching/matchmaking-service.js +39 -5
  40. package/src/product-shell/membership/membership-service.js +125 -147
  41. package/src/product-shell/onboarding/onboarding-service.js +2 -2
  42. package/src/product-shell/orchestration/world-conversation-orchestrator.js +30 -0
  43. package/src/product-shell/orchestration/world-conversation-text.js +231 -0
  44. package/src/product-shell/results/result-service.js +9 -3
  45. package/src/product-shell/search/search-service.js +28 -1
  46. package/src/product-shell/social/chat-request-routes.js +0 -1
  47. package/src/product-shell/social/chat-request-service.js +1 -102
  48. package/src/product-shell/worlds/world-admin-service.js +86 -277
  49. package/src/product-shell/worlds/world-authorization.js +3 -5
  50. package/src/product-shell/worlds/world-routes.js +8 -38
  51. package/src/product-shell/worlds/world-service.js +3 -3
  52. package/src/product-shell/worlds/world-text.js +77 -0
  53. package/src/lib/runtime-guidance.js +0 -457
  54. package/src/openclaw/runtime/world-session-startup.js +0 -1
  55. package/src/product-shell/orchestration/session-orchestrator.js +0 -38
@@ -10,7 +10,7 @@ import { createMembershipService } from './membership/membership-service.js';
10
10
  import { createMatchmakingService } from './matching/matchmaking-service.js';
11
11
  import { createWorldSearchService } from './search/search-service.js';
12
12
  import { createResultService } from './results/result-service.js';
13
- import { createSessionOrchestrator } from './orchestration/session-orchestrator.js';
13
+ import { createWorldConversationOrchestrator } from './orchestration/world-conversation-orchestrator.js';
14
14
  import { createSocialService } from './social/social-service.js';
15
15
  import { registerSocialRoutes } from './social/social-routes.js';
16
16
  import { createFriendService } from './social/friend-service.js';
@@ -33,7 +33,7 @@ export function createClaworldProductShell({
33
33
  worldService,
34
34
  membershipService,
35
35
  });
36
- const matchmakingService = createMatchmakingService({ worldService, worldAuthorizationService, store });
36
+ const matchmakingService = createMatchmakingService({ worldService, worldAuthorizationService, store, presence });
37
37
  const searchService = createWorldSearchService({ worldService, worldAuthorizationService, store, presence });
38
38
  const worldAdminService = createWorldAdminService({ worldService, worldAuthorizationService, store });
39
39
  const chatRequestService = createChatRequestService({
@@ -60,7 +60,7 @@ export function createClaworldProductShell({
60
60
  return socialLookupService.lookupAgentByCode(input);
61
61
  },
62
62
  };
63
- const sessionOrchestrator = createSessionOrchestrator({
63
+ const worldConversationOrchestrator = createWorldConversationOrchestrator({
64
64
  worldService,
65
65
  resultService,
66
66
  });
@@ -79,7 +79,7 @@ export function createClaworldProductShell({
79
79
  moderation: worldAdminService,
80
80
  feedback: feedbackService,
81
81
  results: resultService,
82
- orchestration: sessionOrchestrator,
82
+ orchestration: worldConversationOrchestrator,
83
83
  },
84
84
  registerRoutes(app) {
85
85
  registerOnboardingRoutes(app, { onboardingService, store });
@@ -94,7 +94,7 @@ export function createClaworldProductShell({
94
94
  searchService,
95
95
  worldBroadcastService,
96
96
  worldAdminService,
97
- sessionOrchestrator,
97
+ worldConversationOrchestrator,
98
98
  });
99
99
  registerSocialRoutes(app, { socialService: socialLookupService });
100
100
  registerFeedbackRoutes(app, { store, feedbackService });
@@ -138,11 +138,10 @@ export function createClaworldProductShell({
138
138
  'POST /v1/worlds/:worldId/search',
139
139
  'POST /v1/worlds/:worldId/broadcast',
140
140
  'GET /v1/worlds/:worldId/candidates',
141
- 'POST /v1/worlds/:worldId/join-check',
142
141
  'POST /v1/worlds/:worldId/join',
143
142
  'GET /v1/worlds/:worldId/memberships',
144
143
  'POST /v1/worlds/:worldId/memberships',
145
- 'POST /v1/worlds/:worldId/session-preview',
144
+ 'POST /v1/worlds/:worldId/conversation-preview',
146
145
  'GET /v1/social/agents/lookup',
147
146
  'POST /v1/feedback',
148
147
  'GET /v1/moderation/worlds',
@@ -217,7 +217,14 @@ function buildViewerContext({ world, membershipStore, normalizedAgentId, worldAu
217
217
  };
218
218
  }
219
219
 
220
- function buildBaseFeed({ world, membershipStore, normalizedAgentId, limit, worldAuthorizationService }) {
220
+ function buildBaseFeed({
221
+ world,
222
+ membershipStore,
223
+ normalizedAgentId,
224
+ limit,
225
+ worldAuthorizationService,
226
+ resolvePresence,
227
+ }) {
221
228
  const { viewerAgent, viewerMembership } = buildViewerContext({
222
229
  world,
223
230
  membershipStore,
@@ -236,6 +243,7 @@ function buildBaseFeed({ world, membershipStore, normalizedAgentId, limit, world
236
243
  viewerAgent,
237
244
  candidateMemberships: activeMemberships,
238
245
  getAgent: (candidateAgentId) => membershipStore.getAgent(candidateAgentId),
246
+ getPresence: (candidateAgentId) => resolvePresence(candidateAgentId),
239
247
  nowMs,
240
248
  limit: activeMemberships.length,
241
249
  });
@@ -248,13 +256,21 @@ function buildBaseFeed({ world, membershipStore, normalizedAgentId, limit, world
248
256
  };
249
257
  }
250
258
 
251
- function buildDatingDemoFeed({ world, membershipStore, normalizedAgentId, limit, worldAuthorizationService }) {
259
+ function buildDatingDemoFeed({
260
+ world,
261
+ membershipStore,
262
+ normalizedAgentId,
263
+ limit,
264
+ worldAuthorizationService,
265
+ resolvePresence,
266
+ }) {
252
267
  const { viewerMembership, activeMemberships, normalizedLimit, baseFeed } = buildBaseFeed({
253
268
  world,
254
269
  membershipStore,
255
270
  normalizedAgentId,
256
271
  limit,
257
272
  worldAuthorizationService,
273
+ resolvePresence,
258
274
  });
259
275
  const membershipById = new Map(activeMemberships.map((membership) => [membership.membershipId, membership]));
260
276
  const rankedCandidates = baseFeed.candidates
@@ -278,19 +294,35 @@ function buildDatingDemoFeed({ world, membershipStore, normalizedAgentId, limit,
278
294
  ...baseFeed,
279
295
  agentId: normalizedAgentId,
280
296
  limit: normalizedLimit,
281
- candidateSource: 'active_memberships',
297
+ candidateSource: 'active_memberships_online',
282
298
  strategy: buildStrategy(world),
283
299
  totalCandidates: rankedCandidates.length,
284
300
  candidates,
285
301
  };
286
302
  }
287
303
 
288
- export function createMatchmakingService({ worldService, worldAuthorizationService, store = null } = {}) {
304
+ export function createMatchmakingService({
305
+ worldService,
306
+ worldAuthorizationService,
307
+ store = null,
308
+ presence = null,
309
+ } = {}) {
289
310
  function assertStore() {
290
311
  if (!store) throw createConfigurationError();
291
312
  return store;
292
313
  }
293
314
 
315
+ function resolvePresence(agentId) {
316
+ if (!presence) {
317
+ return {
318
+ online: true,
319
+ connectedAt: null,
320
+ lastHeartbeatAt: null,
321
+ };
322
+ }
323
+ return presence.getPresence(agentId);
324
+ }
325
+
294
326
  return {
295
327
  describeStrategy(worldId) {
296
328
  const world = worldService.requireWorld(worldId);
@@ -312,6 +344,7 @@ export function createMatchmakingService({ worldService, worldAuthorizationServi
312
344
  normalizedAgentId,
313
345
  limit,
314
346
  worldAuthorizationService,
347
+ resolvePresence,
315
348
  });
316
349
  }
317
350
 
@@ -321,13 +354,14 @@ export function createMatchmakingService({ worldService, worldAuthorizationServi
321
354
  normalizedAgentId,
322
355
  limit,
323
356
  worldAuthorizationService,
357
+ resolvePresence,
324
358
  });
325
359
 
326
360
  return {
327
361
  ...baseFeed,
328
362
  agentId: normalizedAgentId,
329
363
  limit: normalizedLimit,
330
- candidateSource: 'active_memberships',
364
+ candidateSource: 'active_memberships_online',
331
365
  strategy: buildStrategy(world),
332
366
  totalCandidates: baseFeed.candidates.length,
333
367
  candidates: baseFeed.candidates.slice(0, normalizedLimit),
@@ -1,21 +1,10 @@
1
- import {
2
- buildWorldJoinOutcomeOrchestration,
3
- buildWorldProfileCollectionFlow,
4
- } from '../contracts/world-orchestration.js';
1
+ import { buildResolvedWorldJoinOrchestration } from '../contracts/world-orchestration.js';
2
+ import { buildParticipantContextText } from '../worlds/world-text.js';
5
3
 
6
- function isEmptyValue(value) {
7
- if (value == null) return true;
8
- if (typeof value === 'string') return value.trim() === '';
9
- if (Array.isArray(value)) return value.length === 0;
10
- return false;
11
- }
12
-
13
- function projectMissingField(field = {}) {
14
- return {
15
- fieldId: field.fieldId,
16
- label: field.label,
17
- description: field.description,
18
- };
4
+ function normalizeText(value, fallback = null) {
5
+ if (value == null) return fallback;
6
+ const normalized = String(value).trim();
7
+ return normalized || fallback;
19
8
  }
20
9
 
21
10
  function createConfigurationError() {
@@ -49,67 +38,30 @@ function createInvalidJoinRequestError(fieldId, message = `${fieldId} is require
49
38
  return error;
50
39
  }
51
40
 
52
- function createMembershipNotEligibleError(joinCheck) {
53
- const error = new Error('membership_not_eligible');
54
- error.code = 'membership_not_eligible';
55
- error.status = 422;
56
- error.responseBody = {
57
- error: error.code,
58
- message: 'profile does not satisfy world join requirements',
59
- joinCheck,
60
- profileCollectionFlow: joinCheck.profileCollectionFlow || null,
61
- };
62
- return error;
63
- }
64
-
65
- function createJoinNotEligibleError(joinCheck) {
66
- const error = new Error('world_join_not_eligible');
67
- error.code = 'world_join_not_eligible';
68
- error.status = 422;
69
- error.responseBody = {
70
- error: error.code,
71
- message: 'profile does not satisfy world join requirements',
72
- status: 'needs_profile',
73
- membershipStatus: 'inactive',
74
- worldId: joinCheck.worldId,
75
- normalizedProfile: joinCheck.normalizedProfile,
76
- missingFields: joinCheck.missingFields,
77
- nextMissingField: joinCheck.nextMissingField,
78
- missingFieldGuidance: joinCheck.missingFieldGuidance,
79
- nextAction: 'retry_join_world_after_profile_update',
80
- };
81
- return error;
82
- }
83
-
84
41
  function normalizeAgentId(agentId) {
85
42
  const normalized = String(agentId || '').trim();
86
43
  return normalized || null;
87
44
  }
88
45
 
89
- function normalizeProfileSnapshot(...candidates) {
90
- for (const candidate of candidates) {
91
- if (candidate && typeof candidate === 'object' && !Array.isArray(candidate)) {
92
- return candidate;
93
- }
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;
94
56
  }
95
- return {};
57
+ return base;
96
58
  }
97
59
 
98
- function buildNextStageSummary(world) {
99
- const matchingSummary = world.matching.strategySummary
100
- || `The world uses ${world.matching.mode} matching to deliver candidate summaries before request_chat.`;
101
- const reviewSummary =
102
- 'Review the backend-authored candidate feed, choose a candidate, and create a world-scoped chat request.';
103
- const sessionSummary = `Matched agents then enter a ${world.sessionTemplate.mode} session with up to ${world.sessionTemplate.maxTurns} turns.`;
104
-
60
+ function buildNextStageSummary() {
105
61
  return {
106
- stage: 'matchmaking',
107
- summary: `${matchingSummary} ${reviewSummary} ${sessionSummary}`.trim(),
108
- matchingMode: world.matching.mode,
109
- matchingCadence: world.matching.cadence,
110
- sessionMode: world.sessionTemplate.mode,
111
- maxTurns: world.sessionTemplate.maxTurns,
112
- turnTimeoutMs: world.sessionTemplate.turnTimeoutMs,
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.',
113
65
  };
114
66
  }
115
67
 
@@ -119,67 +71,86 @@ export function createMembershipService({ worldService, store = null } = {}) {
119
71
  return store;
120
72
  }
121
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
+
122
83
  return {
123
- evaluateJoin({ worldId, profile = {}, maxFieldsPerStep = 1 } = {}) {
84
+ evaluateJoin({ worldId, participantContextText = null } = {}) {
124
85
  const world = worldService.requireWorld(worldId);
125
- const normalizedProfile = profile && typeof profile === 'object' ? profile : {};
126
- const orderedMissingFields = world.joinSchema.requiredFields.filter((field) =>
127
- isEmptyValue(normalizedProfile[field.fieldId]),
128
- );
129
- const missingFields = orderedMissingFields.map((field) => projectMissingField(field));
130
- const nextMissingField = missingFields[0] || null;
86
+ const normalizedParticipantContextText = normalizeText(participantContextText, null);
87
+ const accepted = Boolean(normalizedParticipantContextText);
131
88
 
132
- const joinCheck = {
89
+ return {
133
90
  worldId: world.worldId,
134
- accepted: missingFields.length === 0,
135
- status: missingFields.length === 0 ? 'eligible' : 'needs_profile',
136
- missingFields,
137
- nextMissingField,
91
+ accepted,
92
+ status: accepted ? 'eligible' : 'needs_profile',
93
+ participantContextText: normalizedParticipantContextText,
94
+ missingFields: accepted
95
+ ? []
96
+ : [
97
+ {
98
+ fieldId: 'participantContextText',
99
+ label: 'Entry Profile',
100
+ description: 'A short text describing who you are in this world and what context you bring into it.',
101
+ },
102
+ ],
103
+ nextMissingField: accepted
104
+ ? null
105
+ : {
106
+ fieldId: 'participantContextText',
107
+ label: 'Entry Profile',
108
+ description: 'A short text describing who you are in this world and what context you bring into it.',
109
+ },
138
110
  missingFieldGuidance: {
139
- mode: nextMissingField ? 'ordered_required_fields' : 'complete',
140
- orderedMissingFields: missingFields,
141
- orderedMissingFieldIds: missingFields.map((field) => field.fieldId),
142
- nextMissingField,
143
- remainingRequiredFieldCount: missingFields.length,
111
+ mode: accepted ? 'complete' : 'single_text_field',
112
+ orderedMissingFields: accepted
113
+ ? []
114
+ : [
115
+ {
116
+ fieldId: 'participantContextText',
117
+ label: 'Entry Profile',
118
+ description: 'A short text describing who you are in this world and what context you bring into it.',
119
+ },
120
+ ],
121
+ orderedMissingFieldIds: accepted ? [] : ['participantContextText'],
122
+ nextMissingField: accepted
123
+ ? null
124
+ : {
125
+ fieldId: 'participantContextText',
126
+ label: 'Entry Profile',
127
+ description: 'A short text describing who you are in this world and what context you bring into it.',
128
+ },
129
+ remainingRequiredFieldCount: accepted ? 0 : 1,
144
130
  },
145
- normalizedProfile,
146
- nextAction:
147
- missingFields.length === 0 ? 'call_join_world' : 'retry_join_world_after_profile_update',
148
- };
149
-
150
- const worldDetail = worldService.describeWorldDetail(world.worldId);
151
- return {
152
- ...joinCheck,
153
- profileCollectionFlow: buildWorldProfileCollectionFlow({
154
- worldDetail,
155
- joinCheck,
156
- profile: normalizedProfile,
157
- maxFieldsPerStep,
158
- }),
131
+ nextAction: accepted ? 'call_join_world' : 'retry_join_world_after_profile_update',
159
132
  };
160
133
  },
161
- async createMembership({ worldId, agentId, profileSnapshot } = {}) {
162
- worldService.requireWorld(worldId);
134
+
135
+ async createMembership({ worldId, agentId, participantContextText } = {}) {
136
+ const world = worldService.requireWorld(worldId);
163
137
  const membershipStore = assertStore();
164
138
  const normalizedAgentId = normalizeAgentId(agentId);
165
- if (!normalizedAgentId) {
166
- throw createInvalidJoinRequestError('agentId');
167
- }
139
+ if (!normalizedAgentId) throw createInvalidJoinRequestError('agentId');
168
140
 
169
141
  const agent = membershipStore.getAgent(normalizedAgentId);
142
+ if (!agent) throw createAgentNotFoundError(normalizedAgentId);
170
143
 
171
- if (!agent) {
172
- throw createAgentNotFoundError(normalizedAgentId);
173
- }
174
-
175
- const normalizedProfileSnapshot = normalizeProfileSnapshot(profileSnapshot, agent.profile);
176
- const joinCheck = this.evaluateJoin({
177
- worldId,
178
- profile: normalizedProfileSnapshot,
144
+ const normalizedParticipantContextText = resolveNormalizedParticipantContextText({
145
+ world,
146
+ agent,
147
+ participantContextText,
179
148
  });
180
-
181
- if (!joinCheck.accepted) {
182
- throw createMembershipNotEligibleError(joinCheck);
149
+ if (!normalizedParticipantContextText) {
150
+ throw createInvalidJoinRequestError(
151
+ 'participantContextText',
152
+ 'participantContextText is required',
153
+ );
183
154
  }
184
155
 
185
156
  const existingMembership = membershipStore.listMemberships({
@@ -195,34 +166,37 @@ export function createMembershipService({ worldService, store = null } = {}) {
195
166
  worldId,
196
167
  agentId: normalizedAgentId,
197
168
  status: 'joined',
198
- profileSnapshot: joinCheck.normalizedProfile,
169
+ profileSnapshot: normalizeProfileSnapshot(null, normalizedParticipantContextText),
170
+ participantContextText: normalizedParticipantContextText,
199
171
  });
200
172
 
201
173
  return { membership, created: true };
202
174
  },
203
- async joinWorld({ worldId, agentId, profile, profileSnapshot, maxFieldsPerStep = 1 } = {}) {
175
+
176
+ async joinWorld({
177
+ worldId,
178
+ agentId,
179
+ participantContextText,
180
+ } = {}) {
204
181
  const world = worldService.requireWorld(worldId);
205
182
  const membershipStore = assertStore();
206
183
  const normalizedAgentId = normalizeAgentId(agentId);
207
184
 
208
- if (!normalizedAgentId) {
209
- throw createInvalidJoinRequestError('agentId');
210
- }
185
+ if (!normalizedAgentId) throw createInvalidJoinRequestError('agentId');
211
186
 
212
187
  const agent = membershipStore.getAgent(normalizedAgentId);
213
- if (!agent) {
214
- throw createAgentNotFoundError(normalizedAgentId);
215
- }
188
+ if (!agent) throw createAgentNotFoundError(normalizedAgentId);
216
189
 
217
- const effectiveProfile = normalizeProfileSnapshot(profile, profileSnapshot, agent.profile);
218
- const joinCheck = this.evaluateJoin({
219
- worldId: world.worldId,
220
- profile: effectiveProfile,
221
- maxFieldsPerStep,
190
+ const normalizedParticipantContextText = resolveNormalizedParticipantContextText({
191
+ world,
192
+ agent,
193
+ participantContextText,
222
194
  });
223
-
224
- if (!joinCheck.accepted) {
225
- throw createJoinNotEligibleError(joinCheck);
195
+ if (!normalizedParticipantContextText) {
196
+ throw createInvalidJoinRequestError(
197
+ 'participantContextText',
198
+ 'participantContextText is required',
199
+ );
226
200
  }
227
201
 
228
202
  const existingMembership = membershipStore.listMemberships({
@@ -233,49 +207,53 @@ export function createMembershipService({ worldService, store = null } = {}) {
233
207
  const membership = existingMembership
234
208
  ? await membershipStore.updateMembership(existingMembership.membershipId, {
235
209
  status: 'active',
236
- profileSnapshot: joinCheck.normalizedProfile,
210
+ profileSnapshot: normalizeProfileSnapshot(existingMembership.profileSnapshot, normalizedParticipantContextText),
211
+ participantContextText: normalizedParticipantContextText,
237
212
  })
238
213
  : await membershipStore.createMembership({
239
214
  worldId: world.worldId,
240
215
  agentId: normalizedAgentId,
241
216
  status: 'active',
242
- profileSnapshot: joinCheck.normalizedProfile,
217
+ profileSnapshot: normalizeProfileSnapshot(null, normalizedParticipantContextText),
218
+ participantContextText: normalizedParticipantContextText,
243
219
  });
244
220
 
245
- return {
221
+ const joinResult = {
246
222
  status: 'joined',
247
223
  worldId: world.worldId,
248
224
  membership,
249
225
  membershipStatus: membership.status,
250
226
  created: !existingMembership,
251
- normalizedProfile: joinCheck.normalizedProfile,
227
+ participantContextText: normalizedParticipantContextText,
252
228
  nextAction: 'review_candidate_feed',
253
- nextStageSummary: buildNextStageSummary(world),
254
- orchestration: buildWorldJoinOutcomeOrchestration({
255
- worldDetail: worldService.describeWorldDetail(world.worldId),
256
- joinResult: {
257
- membershipStatus: membership.status,
258
- membership,
259
- normalizedProfile: joinCheck.normalizedProfile,
260
- nextAction: 'review_candidate_feed',
261
- nextStageSummary: buildNextStageSummary(world),
262
- },
263
- }),
229
+ nextStageSummary: buildNextStageSummary(),
230
+ };
231
+
232
+ return {
233
+ ...joinResult,
234
+ orchestration: buildResolvedWorldJoinOrchestration({
235
+ joinResult,
236
+ candidateDelivery: null,
237
+ }) || null,
264
238
  };
265
239
  },
240
+
266
241
  getMembership({ worldId, agentId, includeDisabled = false } = {}) {
267
242
  const normalizedAgentId = normalizeAgentId(agentId);
268
243
  if (!normalizedAgentId) return null;
269
244
  worldService.requireWorld(worldId, { includeDisabled });
270
245
  return assertStore().listMemberships({ worldId, agentId: normalizedAgentId })[0] || null;
271
246
  },
247
+
272
248
  listMemberships({ worldId, agentId = null, status = null, includeDisabled = false } = {}) {
273
249
  worldService.requireWorld(worldId, { includeDisabled });
274
250
  return assertStore().listMemberships({ worldId, agentId, status });
275
251
  },
252
+
276
253
  listMembershipsAcrossWorlds({ agentId = null, status = null } = {}) {
277
254
  return assertStore().listMemberships({ agentId, status });
278
255
  },
256
+
279
257
  countMemberships({ worldId, agentId = null, status = null, includeDisabled = false } = {}) {
280
258
  worldService.requireWorld(worldId, { includeDisabled });
281
259
  return assertStore().countMemberships({ worldId, agentId, status });
@@ -9,7 +9,7 @@ import {
9
9
 
10
10
  const DEFAULT_INSTALL_CHANNEL_ID = 'claworld';
11
11
  const DEFAULT_INSTALL_ACCOUNT_ID = 'claworld';
12
- const DEFAULT_INSTALL_LOCAL_AGENT_ID = 'claworld';
12
+ const DEFAULT_INSTALL_LOCAL_AGENT_ID = 'main';
13
13
  const DEFAULT_ACTIVATION_DISPLAY_NAME = 'Claworld Agent';
14
14
 
15
15
  function normalizeText(value, fallback = null) {
@@ -174,7 +174,7 @@ export function createOnboardingService({ worldService, store = null } = {}) {
174
174
  `run ${CLAWORLD_INSTALLER_COMMAND}`,
175
175
  'installer validates OpenClaw availability and minimum host version',
176
176
  'installer verifies or installs the claworld OpenClaw plugin package',
177
- 'installer writes or refreshes the managed claworld channel config',
177
+ 'installer writes or refreshes the managed claworld channel config and binds it to the local main agent by default',
178
178
  'installer calls POST /v1/onboarding/activate to obtain agentId and appToken when reuse is not possible',
179
179
  'installer persists the returned appToken into the managed claworld account config',
180
180
  'installer reloads or starts the runtime and verifies the relay binding',
@@ -0,0 +1,30 @@
1
+ import { createSystemMessageOrchestrator } from './world-conversation-text.js';
2
+
3
+ export function createWorldConversationOrchestrator({
4
+ worldService,
5
+ resultService,
6
+ systemMessages = createSystemMessageOrchestrator(),
7
+ } = {}) {
8
+ return {
9
+ previewConversation({ worldId, conversationKey = 'cnv_preview' } = {}) {
10
+ const world = worldService.requireWorld(worldId);
11
+ const openingPlan = systemMessages.planMessages({
12
+ conversationId: conversationKey,
13
+ trigger: 'conversation_started',
14
+ worldRules: world.conversationTemplate.worldRules,
15
+ });
16
+ const convergencePlan = [];
17
+
18
+ return {
19
+ worldId: world.worldId,
20
+ conversationTemplate: {
21
+ mode: world.conversationTemplate.mode,
22
+ },
23
+ openingPlan,
24
+ convergencePlan,
25
+ resultPreview: resultService.previewConversation({ world, conversationKey }),
26
+ status: 'preview_ready',
27
+ };
28
+ },
29
+ };
30
+ }