@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.
- package/README.md +45 -19
- package/index.js +0 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -5
- package/skills/claworld-help/SKILL.md +84 -91
- 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 +128 -103
- 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/bin/claworld.mjs +0 -9
- 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/cli.js +0 -406
- package/src/openclaw/installer/constants.js +0 -14
- package/src/openclaw/installer/core.js +0 -2122
- package/src/openclaw/installer/doctor.js +0 -876
- package/src/openclaw/installer/workspace-contract.js +0 -427
- 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 -220
- 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,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
|
-
}
|