@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.
- package/README.md +4 -4
- package/index.js +0 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/claworld-help/SKILL.md +19 -27
- package/skills/claworld-join-and-chat/SKILL.md +9 -9
- package/src/openclaw/index.js +0 -3
- package/src/openclaw/plugin/account-identity.js +0 -1
- package/src/openclaw/plugin/claworld-channel-plugin.js +8 -253
- package/src/openclaw/plugin/managed-config.js +1 -7
- package/src/openclaw/plugin/onboarding.js +1 -1
- package/src/openclaw/plugin/register.js +183 -232
- package/src/openclaw/plugin/relay-client.js +8 -5
- package/src/openclaw/runtime/product-shell-helper.js +11 -364
- package/src/openclaw/runtime/tool-contracts.js +0 -182
- package/src/openclaw/runtime/tool-inventory.js +4 -27
- package/src/lib/agent-profile.js +0 -74
- package/src/lib/http-auth.js +0 -151
- package/src/lib/policy.js +0 -114
- package/src/openclaw/installer/constants.js +0 -14
- package/src/product-shell/agent-cards/card-routes.js +0 -64
- package/src/product-shell/agent-cards/card-service.js +0 -287
- package/src/product-shell/agent-cards/spec-builder.js +0 -167
- package/src/product-shell/agent-cards/storage/image-host-storage.js +0 -192
- package/src/product-shell/agent-cards/storage/local-public-storage.js +0 -74
- package/src/product-shell/agent-cards/svg-renderer.js +0 -325
- package/src/product-shell/agent-cards/template-registry.js +0 -131
- package/src/product-shell/catalog/default-world-catalog.js +0 -38
- package/src/product-shell/contracts/candidate-feed.js +0 -393
- package/src/product-shell/contracts/world-manifest.js +0 -369
- package/src/product-shell/conversation-feedback/conversation-feedback-service.js +0 -261
- package/src/product-shell/feedback/feedback-contract.js +0 -13
- package/src/product-shell/feedback/feedback-routes.js +0 -98
- package/src/product-shell/feedback/feedback-service.js +0 -252
- package/src/product-shell/index.js +0 -212
- package/src/product-shell/matching/matchmaking-service.js +0 -395
- package/src/product-shell/membership/membership-service.js +0 -284
- package/src/product-shell/onboarding/onboarding-routes.js +0 -37
- package/src/product-shell/onboarding/onboarding-service.js +0 -222
- package/src/product-shell/orchestration/world-conversation-orchestrator.js +0 -28
- package/src/product-shell/profile/profile-service.js +0 -142
- package/src/product-shell/profile/public-identity-routes.js +0 -160
- package/src/product-shell/profile/public-identity-service.js +0 -192
- package/src/product-shell/search/search-service.js +0 -393
- package/src/product-shell/social/chat-request-approval-policy.js +0 -332
- package/src/product-shell/social/chat-request-routes.js +0 -130
- package/src/product-shell/social/chat-request-service.js +0 -723
- package/src/product-shell/social/friend-routes.js +0 -82
- package/src/product-shell/social/friend-service.js +0 -557
- package/src/product-shell/social/social-routes.js +0 -21
- package/src/product-shell/social/social-service.js +0 -136
- package/src/product-shell/worlds/world-admin-service.js +0 -486
- package/src/product-shell/worlds/world-authorization.js +0 -136
- package/src/product-shell/worlds/world-broadcast-service.js +0 -296
- package/src/product-shell/worlds/world-routes.js +0 -403
- package/src/product-shell/worlds/world-service.js +0 -89
- 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
|
-
}
|