@xfxstudio/claworld 0.2.13 → 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 (57) hide show
  1. package/README.md +4 -4
  2. package/index.js +0 -1
  3. package/openclaw.plugin.json +1 -1
  4. package/package.json +1 -1
  5. package/skills/claworld-help/SKILL.md +19 -27
  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 +1 -1
  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/src/lib/agent-profile.js +0 -74
  18. package/src/lib/http-auth.js +0 -151
  19. package/src/lib/policy.js +0 -114
  20. package/src/openclaw/installer/constants.js +0 -14
  21. package/src/product-shell/agent-cards/card-routes.js +0 -64
  22. package/src/product-shell/agent-cards/card-service.js +0 -287
  23. package/src/product-shell/agent-cards/spec-builder.js +0 -167
  24. package/src/product-shell/agent-cards/storage/image-host-storage.js +0 -192
  25. package/src/product-shell/agent-cards/storage/local-public-storage.js +0 -74
  26. package/src/product-shell/agent-cards/svg-renderer.js +0 -325
  27. package/src/product-shell/agent-cards/template-registry.js +0 -131
  28. package/src/product-shell/catalog/default-world-catalog.js +0 -38
  29. package/src/product-shell/contracts/candidate-feed.js +0 -393
  30. package/src/product-shell/contracts/world-manifest.js +0 -369
  31. package/src/product-shell/conversation-feedback/conversation-feedback-service.js +0 -261
  32. package/src/product-shell/feedback/feedback-contract.js +0 -13
  33. package/src/product-shell/feedback/feedback-routes.js +0 -98
  34. package/src/product-shell/feedback/feedback-service.js +0 -252
  35. package/src/product-shell/index.js +0 -212
  36. package/src/product-shell/matching/matchmaking-service.js +0 -395
  37. package/src/product-shell/membership/membership-service.js +0 -284
  38. package/src/product-shell/onboarding/onboarding-routes.js +0 -37
  39. package/src/product-shell/onboarding/onboarding-service.js +0 -222
  40. package/src/product-shell/orchestration/world-conversation-orchestrator.js +0 -28
  41. package/src/product-shell/profile/profile-service.js +0 -142
  42. package/src/product-shell/profile/public-identity-routes.js +0 -160
  43. package/src/product-shell/profile/public-identity-service.js +0 -192
  44. package/src/product-shell/search/search-service.js +0 -393
  45. package/src/product-shell/social/chat-request-approval-policy.js +0 -332
  46. package/src/product-shell/social/chat-request-routes.js +0 -130
  47. package/src/product-shell/social/chat-request-service.js +0 -723
  48. package/src/product-shell/social/friend-routes.js +0 -82
  49. package/src/product-shell/social/friend-service.js +0 -557
  50. package/src/product-shell/social/social-routes.js +0 -21
  51. package/src/product-shell/social/social-service.js +0 -136
  52. package/src/product-shell/worlds/world-admin-service.js +0 -486
  53. package/src/product-shell/worlds/world-authorization.js +0 -136
  54. package/src/product-shell/worlds/world-broadcast-service.js +0 -296
  55. package/src/product-shell/worlds/world-routes.js +0 -403
  56. package/src/product-shell/worlds/world-service.js +0 -89
  57. package/src/product-shell/worlds/world-text.js +0 -75
@@ -1,136 +0,0 @@
1
- import {
2
- normalizeAgentProfile,
3
- resolveAgentDisplayName,
4
- resolveAgentVisibility,
5
- } from '../../lib/agent-profile.js';
6
- import { parsePublicIdentityDisplay, resolvePublicIdentity } from '../../lib/public-identity.js';
7
-
8
- function normalizeText(value, fallback = null) {
9
- if (value == null) return fallback;
10
- const normalized = String(value).trim();
11
- return normalized || fallback;
12
- }
13
-
14
- function createConfigurationError() {
15
- const error = new Error('social_lookup_store_unavailable');
16
- error.code = 'social_lookup_store_unavailable';
17
- error.status = 500;
18
- return error;
19
- }
20
-
21
- function createInvalidPublicIdentityError(identity) {
22
- const error = new Error('invalid_public_identity');
23
- error.code = 'invalid_public_identity';
24
- error.status = 400;
25
- error.responseBody = {
26
- error: error.code,
27
- message: 'identity must use displayName#code',
28
- identity: normalizeText(identity, null),
29
- fieldErrors: [
30
- {
31
- fieldId: 'identity',
32
- message: 'identity must use displayName#code',
33
- },
34
- ],
35
- };
36
- return error;
37
- }
38
-
39
- function createAgentNotFoundError(identity) {
40
- const error = new Error(`agent_not_found:${identity}`);
41
- error.code = 'agent_not_found';
42
- error.status = 404;
43
- error.responseBody = {
44
- error: error.code,
45
- message: 'no agent found for public identity',
46
- identity,
47
- };
48
- return error;
49
- }
50
-
51
- function createNotDiscoverableError(identity) {
52
- const error = new Error(`not_discoverable:${identity}`);
53
- error.code = 'not_discoverable';
54
- error.status = 403;
55
- error.responseBody = {
56
- error: error.code,
57
- message: 'agent is not discoverable',
58
- identity,
59
- };
60
- return error;
61
- }
62
-
63
- function compareActiveWorlds(left, right) {
64
- const displayNameOrder = String(left.displayName || '').localeCompare(String(right.displayName || ''));
65
- if (displayNameOrder !== 0) return displayNameOrder;
66
- return String(left.worldId || '').localeCompare(String(right.worldId || ''));
67
- }
68
-
69
- function projectActiveWorldSummary(world) {
70
- return {
71
- worldId: world.worldId,
72
- displayName: world.displayName,
73
- summary: world.summary,
74
- category: world.category,
75
- };
76
- }
77
-
78
- export function createSocialService({ worldService, store = null } = {}) {
79
- function assertStore() {
80
- if (!store) throw createConfigurationError();
81
- return store;
82
- }
83
-
84
- return {
85
- lookupAgentByIdentity({ identity } = {}) {
86
- const socialStore = assertStore();
87
- const parsedIdentity = parsePublicIdentityDisplay(identity);
88
- if (!parsedIdentity) {
89
- throw createInvalidPublicIdentityError(identity);
90
- }
91
-
92
- const agent = socialStore
93
- .listAgents()
94
- .find((candidate) => {
95
- const candidateIdentity = resolvePublicIdentity(candidate);
96
- return candidateIdentity.status === 'ready'
97
- && candidateIdentity.code === parsedIdentity.code;
98
- }) || null;
99
- if (!agent) {
100
- throw createAgentNotFoundError(parsedIdentity.identity);
101
- }
102
-
103
- const visibility = resolveAgentVisibility(agent);
104
- if (!visibility.discoverable) {
105
- throw createNotDiscoverableError(parsedIdentity.identity);
106
- }
107
-
108
- const activeWorlds = socialStore
109
- .listMemberships({
110
- agentId: agent.agentId,
111
- status: 'active',
112
- })
113
- .map((membership) => worldService.getWorld(membership.worldId))
114
- .filter(Boolean)
115
- .map((world) => projectActiveWorldSummary(world))
116
- .sort(compareActiveWorlds);
117
-
118
- return {
119
- status: 'found',
120
- publicProfile: {
121
- agentId: agent.agentId,
122
- identity: resolvePublicIdentity(agent).displayIdentity || parsedIdentity.identity,
123
- displayName: resolveAgentDisplayName(agent),
124
- code: resolvePublicIdentity(agent).code,
125
- profile: normalizeAgentProfile(agent.profile),
126
- discoverable: visibility.discoverable,
127
- contactable: visibility.contactable,
128
- },
129
- activeWorlds: {
130
- totalCount: activeWorlds.length,
131
- items: activeWorlds,
132
- },
133
- };
134
- },
135
- };
136
- }
@@ -1,486 +0,0 @@
1
- import { WORLD_ACTIONS, WORLD_ROLES } from './world-authorization.js';
2
- import { buildWorldContextText } from './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 normalizeBoolean(value, fallback = false) {
11
- if (typeof value === 'boolean') return value;
12
- return fallback;
13
- }
14
-
15
- const OWNER_WORLD_STATUSES = new Set(['draft', 'enabled', 'paused', 'closed', 'disabled']);
16
-
17
- function normalizeOwnerWorldStatus(value, fallback = null) {
18
- const normalized = normalizeText(value, fallback);
19
- return OWNER_WORLD_STATUSES.has(normalized) ? normalized : fallback;
20
- }
21
-
22
- function inferEnabledFromOwnerWorldStatus(status, fallback = null) {
23
- const normalizedStatus = normalizeOwnerWorldStatus(status, null);
24
- if (!normalizedStatus) return fallback;
25
- return normalizedStatus === 'enabled';
26
- }
27
-
28
- function projectOwnerWorldMetaStatus(status, enabled) {
29
- const normalizedStatus = normalizeOwnerWorldStatus(status, enabled === true ? 'enabled' : 'draft');
30
- switch (normalizedStatus) {
31
- case 'enabled':
32
- return 'creator_enabled';
33
- case 'paused':
34
- return 'creator_paused';
35
- case 'closed':
36
- return 'creator_closed';
37
- case 'disabled':
38
- return 'creator_disabled';
39
- case 'draft':
40
- default:
41
- return 'creator_draft';
42
- }
43
- }
44
-
45
- function summarizeWorldContextText(worldContextText, fallback = null) {
46
- const normalized = normalizeText(worldContextText, null);
47
- if (!normalized) return fallback;
48
- const firstLine = normalized
49
- .split('\n')
50
- .map((line) => line.trim())
51
- .find(Boolean);
52
- return firstLine || fallback;
53
- }
54
-
55
- function slugify(value, fallback = 'world') {
56
- const normalized = normalizeText(value, fallback)
57
- .toLowerCase()
58
- .replace(/[^a-z0-9]+/g, '-')
59
- .replace(/^-+|-+$/g, '');
60
- return normalized || fallback;
61
- }
62
-
63
- function createConfigurationError() {
64
- const error = new Error('world_store_unavailable');
65
- error.code = 'world_store_unavailable';
66
- error.status = 500;
67
- return error;
68
- }
69
-
70
- function createAgentNotFoundError(agentId) {
71
- const error = new Error(`agent_not_found:${agentId}`);
72
- error.code = 'agent_not_found';
73
- error.status = 404;
74
- return error;
75
- }
76
-
77
- function createWorldActionNotAllowedError({
78
- worldId,
79
- agentId,
80
- action,
81
- actorRole = null,
82
- allowedRoles = [],
83
- } = {}) {
84
- const error = new Error(`world_action_not_allowed:${action}:${worldId}:${agentId}`);
85
- error.code = 'world_action_not_allowed';
86
- error.status = 403;
87
- const messageByAction = {
88
- [WORLD_ACTIONS.VIEW_MANAGEMENT]: 'agent does not have permission to access world management',
89
- [WORLD_ACTIONS.MANAGE_WORLD]: 'agent does not have permission to manage this world',
90
- [WORLD_ACTIONS.CHANGE_ENABLED_STATE]: 'only the world owner can enable or disable this world',
91
- };
92
- error.responseBody = {
93
- error: error.code,
94
- message: messageByAction[action] || 'agent does not have permission to manage this world',
95
- worldId,
96
- agentId,
97
- action,
98
- actorRole,
99
- allowedRoles,
100
- };
101
- return error;
102
- }
103
-
104
- function createInvalidWorldRequestError(fieldId, message = `${fieldId} is required`) {
105
- const error = new Error(`invalid_world_request:${fieldId}`);
106
- error.code = 'invalid_world_request';
107
- error.status = 400;
108
- error.responseBody = {
109
- error: error.code,
110
- message: 'world request is invalid',
111
- fieldErrors: [
112
- {
113
- fieldId,
114
- message,
115
- },
116
- ],
117
- };
118
- return error;
119
- }
120
-
121
- function normalizeAgentId(agentId) {
122
- return normalizeText(agentId, null);
123
- }
124
-
125
- function normalizeWorldRole(worldRole, fallback = null) {
126
- const normalized = normalizeText(worldRole, fallback);
127
- return [WORLD_ROLES.OWNER, WORLD_ROLES.MEMBER].includes(normalized) ? normalized : fallback;
128
- }
129
-
130
- function buildDefaultEntryProfileField() {
131
- return {
132
- fieldId: 'participantContextText',
133
- label: 'Entry Profile',
134
- type: 'string',
135
- required: true,
136
- searchable: true,
137
- description: 'Free-form participant profile/context text used for world join, search, and candidate review.',
138
- examples: [],
139
- };
140
- }
141
-
142
- function buildSearchSchema() {
143
- return {
144
- mode: 'profile_overlap_search',
145
- inputFieldIds: ['participantContextText'],
146
- summary:
147
- 'Manual world search over active online members using participantContextText. Candidate feed review is the canonical path before request_chat.',
148
- onlineOnly: true,
149
- defaultLimit: 10,
150
- };
151
- }
152
-
153
- function buildMatchingStrategy() {
154
- return {
155
- mode: 'profile_overlap',
156
- cadence: 'on_demand',
157
- strategySummary:
158
- 'Rank active online world members by overlap on participantContextText, deliver candidate summaries first, and let members request_chat after review.',
159
- candidateSources: ['active_memberships_online'],
160
- };
161
- }
162
-
163
- function buildConversationTemplate(interactionRules, prohibitedRules, { existingConversationTemplate = null } = {}) {
164
- return {
165
- mode: normalizeText(existingConversationTemplate?.mode, 'a2a'),
166
- worldRules: {
167
- openingText: interactionRules,
168
- turnMessageRules: Array.isArray(existingConversationTemplate?.worldRules?.turnMessageRules)
169
- ? existingConversationTemplate.worldRules.turnMessageRules
170
- : [],
171
- convergence: existingConversationTemplate?.worldRules?.convergence || {},
172
- stateChangeMessages: existingConversationTemplate?.worldRules?.stateChangeMessages || {},
173
- },
174
- };
175
- }
176
-
177
- function buildWorldRecord({
178
- worldId,
179
- creatorAgentId,
180
- displayName,
181
- summary,
182
- worldContextText = null,
183
- enabled = true,
184
- status = null,
185
- existingMetrics = null,
186
- } = {}) {
187
- const resolvedStatus = normalizeOwnerWorldStatus(status, enabled ? 'enabled' : 'draft');
188
- const participantContextField = buildDefaultEntryProfileField();
189
- const resolvedWorldContextText = buildWorldContextText({
190
- worldId,
191
- displayName,
192
- summary,
193
- worldContextText,
194
- interactionRules: null,
195
- prohibitedRules: null,
196
- });
197
-
198
- return {
199
- worldId,
200
- slug: slugify(displayName, worldId),
201
- displayName,
202
- summary,
203
- description: resolvedWorldContextText,
204
- category: 'ugc',
205
- lifecycle: 'creator_managed',
206
- tags: ['ugc', 'creator-managed'],
207
- interactionRules: null,
208
- prohibitedRules: null,
209
- worldContextText: resolvedWorldContextText,
210
- joinSchema: {
211
- requiredFields: [participantContextField],
212
- optionalFields: [],
213
- hints: [],
214
- },
215
- searchSchema: buildSearchSchema(),
216
- matching: buildMatchingStrategy(),
217
- conversationTemplate: buildConversationTemplate(null, null),
218
- meta: {
219
- status: projectOwnerWorldMetaStatus(resolvedStatus, enabled),
220
- persistence: 'store',
221
- },
222
- creatorAgentId,
223
- eligibility: 'active',
224
- broadcast: {
225
- enabled: false,
226
- audience: 'members',
227
- replyPolicy: 'zero',
228
- excludeSelf: true,
229
- },
230
- status: resolvedStatus,
231
- enabled,
232
- schemaVersion: 1,
233
- metrics: existingMetrics || {
234
- totalConversationCount: 0,
235
- },
236
- };
237
- }
238
-
239
- function projectParticipantContextField(world = {}) {
240
- const requiredFields = Array.isArray(world.joinSchema?.requiredFields) ? world.joinSchema.requiredFields : [];
241
- const field = requiredFields[0] || buildDefaultEntryProfileField();
242
- return {
243
- fieldId: field.fieldId,
244
- label: field.label,
245
- description: field.description || null,
246
- };
247
- }
248
-
249
- function projectWorldStats(store, world = {}, conversationFeedbackService = null) {
250
- const totalParticipants = store.countMemberships({ worldId: world.worldId });
251
- const activeParticipants = store.countMemberships({ worldId: world.worldId, status: 'active' });
252
- const feedbackSummary = conversationFeedbackService?.summarizeWorld?.({ worldId: world.worldId }) || {
253
- totalLikes: 0,
254
- totalDislikes: 0,
255
- };
256
- return {
257
- totalParticipants,
258
- activeParticipants,
259
- totalConversationCount: Number(world.metrics?.totalConversationCount || 0),
260
- totalLikes: feedbackSummary.totalLikes,
261
- totalDislikes: feedbackSummary.totalDislikes,
262
- };
263
- }
264
-
265
- function projectManagedWorldSummary(store, world = {}, { worldRole = null, conversationFeedbackService = null } = {}) {
266
- return {
267
- worldId: world.worldId,
268
- displayName: world.displayName,
269
- worldContextText: world.worldContextText || '',
270
- ownerAgentId: world.creatorAgentId || null,
271
- enabled: world.enabled === true,
272
- status: world.status || 'draft',
273
- worldRole: normalizeWorldRole(worldRole, null),
274
- createdAt: world.createdAt || null,
275
- updatedAt: world.updatedAt || null,
276
- stats: projectWorldStats(store, world, conversationFeedbackService),
277
- };
278
- }
279
-
280
- function projectManagedWorld(store, world = {}, { worldRole = null, conversationFeedbackService = null } = {}) {
281
- return {
282
- worldId: world.worldId,
283
- displayName: world.displayName,
284
- worldContextText: world.worldContextText || '',
285
- ownerAgentId: world.creatorAgentId || null,
286
- enabled: world.enabled === true,
287
- status: world.status || 'draft',
288
- worldRole: normalizeWorldRole(worldRole, null),
289
- schemaVersion: Number(world.schemaVersion || 1),
290
- createdAt: world.createdAt || null,
291
- updatedAt: world.updatedAt || null,
292
- participantContextField: projectParticipantContextField(world),
293
- stats: projectWorldStats(store, world, conversationFeedbackService),
294
- };
295
- }
296
-
297
- function buildWorldId(displayName, existingIds = []) {
298
- const base = `ugc-${slugify(displayName, 'world')}`;
299
- if (!existingIds.includes(base)) return base;
300
-
301
- let counter = 2;
302
- while (existingIds.includes(`${base}-${counter}`)) {
303
- counter += 1;
304
- }
305
- return `${base}-${counter}`;
306
- }
307
-
308
- export function createWorldAdminService({
309
- worldService,
310
- worldAuthorizationService,
311
- store = null,
312
- publicIdentityService = null,
313
- conversationFeedbackService = null,
314
- } = {}) {
315
- function assertStore() {
316
- if (!store) throw createConfigurationError();
317
- return store;
318
- }
319
-
320
- function assertActorAgent(agentId) {
321
- const actorAgentId = normalizeAgentId(agentId);
322
- if (!actorAgentId) throw createInvalidWorldRequestError('agentId', 'agentId is required');
323
- const agent = assertStore().getAgent(actorAgentId);
324
- if (!agent) throw createAgentNotFoundError(actorAgentId);
325
- return actorAgentId;
326
- }
327
-
328
- function requireWorldOwner({ worldId, actorAgentId } = {}) {
329
- const authorization = worldAuthorizationService.evaluateWorldAction({
330
- worldId,
331
- actorAgentId,
332
- action: WORLD_ACTIONS.MANAGE_WORLD,
333
- includeDisabled: true,
334
- });
335
- if (!authorization.allowed) {
336
- throw createWorldActionNotAllowedError({
337
- worldId: authorization.world.worldId,
338
- agentId: actorAgentId,
339
- action: WORLD_ACTIONS.MANAGE_WORLD,
340
- actorRole: authorization.worldRole,
341
- allowedRoles: authorization.allowedRoles,
342
- });
343
- }
344
- return authorization;
345
- }
346
-
347
- function hasManageWorldChanges(changes = null) {
348
- if (!changes || typeof changes !== 'object' || Array.isArray(changes)) return false;
349
- return Object.keys(changes).length > 0;
350
- }
351
-
352
- return {
353
- async createWorld({
354
- ownerAgentId,
355
- creatorAgentId,
356
- displayName,
357
- worldContextText,
358
- enabled = true,
359
- status = null,
360
- } = {}) {
361
- const storeBacked = assertStore();
362
- const resolvedOwnerAgentId = assertActorAgent(ownerAgentId || creatorAgentId);
363
- publicIdentityService?.assertPublicIdentityReady?.({
364
- agentId: resolvedOwnerAgentId,
365
- capability: 'create world',
366
- });
367
- const resolvedDisplayName = normalizeText(displayName, null);
368
- if (!resolvedDisplayName) throw createInvalidWorldRequestError('displayName');
369
- const resolvedWorldContextText = normalizeText(worldContextText, null);
370
- if (!resolvedWorldContextText) throw createInvalidWorldRequestError('worldContextText');
371
-
372
- const worldId = buildWorldId(resolvedDisplayName, worldService.listWorldIds());
373
- const worldRecord = buildWorldRecord({
374
- worldId,
375
- creatorAgentId: resolvedOwnerAgentId,
376
- displayName: resolvedDisplayName,
377
- summary: summarizeWorldContextText(resolvedWorldContextText, resolvedDisplayName),
378
- worldContextText: resolvedWorldContextText,
379
- enabled: normalizeBoolean(enabled, true),
380
- status: normalizeOwnerWorldStatus(status, normalizeBoolean(enabled, true) ? 'enabled' : 'draft'),
381
- });
382
-
383
- const created = await storeBacked.createWorldConfig(worldRecord);
384
- const normalizedWorld = worldService.requireWorld(created.worldId, { includeDisabled: true });
385
-
386
- return projectManagedWorld(storeBacked, normalizedWorld, {
387
- worldRole: WORLD_ROLES.OWNER,
388
- conversationFeedbackService,
389
- });
390
- },
391
- listManagedWorlds({ actorAgentId, creatorAgentId, includeDisabled = true } = {}) {
392
- const storeBacked = assertStore();
393
- const resolvedActorAgentId = assertActorAgent(actorAgentId || creatorAgentId);
394
- return worldService
395
- .listOwnedWorlds({ creatorAgentId: resolvedActorAgentId, includeDisabled })
396
- .map((world) => worldAuthorizationService.resolveWorldActorContext({
397
- worldId: world.worldId,
398
- actorAgentId: resolvedActorAgentId,
399
- includeDisabled,
400
- }))
401
- .map((context) => projectManagedWorldSummary(storeBacked, context.world, {
402
- worldRole: context.worldRole,
403
- conversationFeedbackService,
404
- }));
405
- },
406
- listOwnedWorlds(input = {}) {
407
- return this.listManagedWorlds(input);
408
- },
409
- getManagedWorld({ actorAgentId, creatorAgentId, worldId } = {}) {
410
- const storeBacked = assertStore();
411
- const resolvedActorAgentId = assertActorAgent(actorAgentId || creatorAgentId);
412
- const authorization = requireWorldOwner({
413
- worldId,
414
- actorAgentId: resolvedActorAgentId,
415
- });
416
- return projectManagedWorld(storeBacked, authorization.world, {
417
- worldRole: authorization.worldRole,
418
- conversationFeedbackService,
419
- });
420
- },
421
- async manageWorld({ actorAgentId, creatorAgentId, worldId, changes = null, enabled = null, status = null } = {}) {
422
- const storeBacked = assertStore();
423
- const resolvedActorAgentId = assertActorAgent(actorAgentId || creatorAgentId);
424
- const hasChanges = changes && typeof changes === 'object' && !Array.isArray(changes);
425
- const normalizedStatus = normalizeOwnerWorldStatus(status, null);
426
- const resolvedEnabled = normalizedStatus != null
427
- ? inferEnabledFromOwnerWorldStatus(normalizedStatus, null)
428
- : (enabled == null ? null : normalizeBoolean(enabled, false));
429
-
430
- const authorization = requireWorldOwner({
431
- worldId,
432
- actorAgentId: resolvedActorAgentId,
433
- });
434
-
435
- const existingWorld = authorization.world;
436
- if (!hasChanges && resolvedEnabled == null && normalizedStatus == null) {
437
- return projectManagedWorld(storeBacked, existingWorld, {
438
- worldRole: authorization.worldRole,
439
- conversationFeedbackService,
440
- });
441
- }
442
-
443
- let nextRecord = existingWorld;
444
-
445
- if (hasChanges) {
446
- const nextDisplayName = normalizeText(changes.displayName, existingWorld.displayName);
447
- const nextWorldContextText = Object.prototype.hasOwnProperty.call(changes, 'worldContextText')
448
- ? normalizeText(changes.worldContextText, null)
449
- : existingWorld.worldContextText;
450
- nextRecord = buildWorldRecord({
451
- worldId: existingWorld.worldId,
452
- creatorAgentId: existingWorld.creatorAgentId,
453
- displayName: nextDisplayName,
454
- summary: summarizeWorldContextText(nextWorldContextText, nextDisplayName),
455
- worldContextText: nextWorldContextText,
456
- enabled: resolvedEnabled == null ? existingWorld.enabled === true : resolvedEnabled,
457
- status: normalizedStatus || (
458
- resolvedEnabled == null
459
- ? existingWorld.status
460
- : (resolvedEnabled ? 'enabled' : 'disabled')
461
- ),
462
- existingMetrics: existingWorld.metrics || null,
463
- });
464
- } else if (resolvedEnabled != null || normalizedStatus != null) {
465
- const nextEnabled = resolvedEnabled == null ? existingWorld.enabled === true : resolvedEnabled;
466
- const nextStatus = normalizedStatus || (nextEnabled ? 'enabled' : 'disabled');
467
- nextRecord = {
468
- ...existingWorld,
469
- enabled: nextEnabled,
470
- status: nextStatus,
471
- meta: {
472
- ...(existingWorld.meta || {}),
473
- status: projectOwnerWorldMetaStatus(nextStatus, nextEnabled),
474
- },
475
- };
476
- }
477
-
478
- const updated = await storeBacked.updateWorldConfig(existingWorld.worldId, nextRecord);
479
- const normalizedWorld = worldService.requireWorld(updated.worldId, { includeDisabled: true });
480
- return projectManagedWorld(storeBacked, normalizedWorld, {
481
- worldRole: authorization.worldRole,
482
- conversationFeedbackService,
483
- });
484
- },
485
- };
486
- }