@xfxstudio/claworld 0.2.8 → 0.2.10-beta.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.
- package/README.md +1 -1
- package/openclaw.plugin.json +7 -63
- package/package.json +6 -2
- package/skills/claworld-help/SKILL.md +7 -3
- package/skills/claworld-join-and-chat/SKILL.md +38 -9
- package/skills/claworld-manage-worlds/SKILL.md +81 -10
- package/src/lib/agent-profile.js +8 -3
- package/src/lib/chat-request.js +19 -1
- package/src/lib/policy.js +2 -6
- package/src/lib/public-identity.js +175 -0
- package/src/lib/relay/kickoff-text.js +7 -1
- package/src/openclaw/installer/cli.js +46 -1
- package/src/openclaw/installer/constants.js +1 -0
- package/src/openclaw/installer/core.js +234 -3
- package/src/openclaw/installer/doctor.js +2 -2
- package/src/openclaw/plugin/account-identity.js +1 -2
- package/src/openclaw/plugin/claworld-channel-plugin.js +302 -266
- package/src/openclaw/plugin/config-schema.js +9 -23
- package/src/openclaw/plugin/managed-config.js +284 -79
- package/src/openclaw/plugin/onboarding.js +22 -42
- package/src/openclaw/plugin/register.js +144 -25
- package/src/openclaw/plugin/relay-client.js +237 -18
- package/src/openclaw/runtime/backend-error-context.js +91 -0
- package/src/openclaw/runtime/feedback-helper.js +1 -2
- package/src/openclaw/runtime/product-shell-helper.js +43 -9
- package/src/openclaw/runtime/tool-contracts.js +65 -3
- package/src/openclaw/runtime/tool-inventory.js +8 -1
- package/src/openclaw/runtime/world-moderation-helper.js +3 -19
- package/src/product-shell/contracts/candidate-feed.js +7 -0
- package/src/product-shell/contracts/world-manifest.js +0 -1
- package/src/product-shell/contracts/world-orchestration.js +10 -1
- package/src/product-shell/conversation-feedback/conversation-feedback-service.js +261 -0
- package/src/product-shell/feedback/feedback-routes.js +0 -1
- package/src/product-shell/feedback/feedback-service.js +4 -9
- package/src/product-shell/index.js +40 -7
- package/src/product-shell/matching/matchmaking-service.js +22 -1
- package/src/product-shell/membership/membership-service.js +5 -1
- package/src/product-shell/onboarding/onboarding-service.js +10 -21
- package/src/product-shell/profile/public-identity-routes.js +60 -0
- package/src/product-shell/profile/public-identity-service.js +190 -0
- package/src/product-shell/search/search-service.js +9 -2
- package/src/product-shell/social/chat-request-routes.js +4 -1
- package/src/product-shell/social/chat-request-service.js +184 -22
- package/src/product-shell/social/friend-routes.js +1 -1
- package/src/product-shell/social/friend-service.js +16 -19
- package/src/product-shell/social/social-routes.js +2 -2
- package/src/product-shell/social/social-service.js +31 -35
- package/src/product-shell/worlds/world-admin-service.js +31 -10
- package/src/product-shell/worlds/world-broadcast-service.js +2 -2
- package/src/lib/agent-address.js +0 -46
|
@@ -6,6 +6,8 @@ import { createWorldBroadcastService } from './worlds/world-broadcast-service.js
|
|
|
6
6
|
import { DEFAULT_WORLD_MANIFESTS } from './catalog/default-world-catalog.js';
|
|
7
7
|
import { createOnboardingService } from './onboarding/onboarding-service.js';
|
|
8
8
|
import { registerOnboardingRoutes } from './onboarding/onboarding-routes.js';
|
|
9
|
+
import { createPublicIdentityService } from './profile/public-identity-service.js';
|
|
10
|
+
import { registerPublicIdentityRoutes } from './profile/public-identity-routes.js';
|
|
9
11
|
import { createMembershipService } from './membership/membership-service.js';
|
|
10
12
|
import { createMatchmakingService } from './matching/matchmaking-service.js';
|
|
11
13
|
import { createWorldSearchService } from './search/search-service.js';
|
|
@@ -18,6 +20,7 @@ import { createChatRequestService } from './social/chat-request-service.js';
|
|
|
18
20
|
import { registerChatRequestRoutes } from './social/chat-request-routes.js';
|
|
19
21
|
import { createFeedbackService } from './feedback/feedback-service.js';
|
|
20
22
|
import { registerFeedbackRoutes } from './feedback/feedback-routes.js';
|
|
23
|
+
import { createConversationFeedbackService } from './conversation-feedback/conversation-feedback-service.js';
|
|
21
24
|
|
|
22
25
|
export function createClaworldProductShell({
|
|
23
26
|
worldCatalog = DEFAULT_WORLD_MANIFESTS,
|
|
@@ -27,20 +30,42 @@ export function createClaworldProductShell({
|
|
|
27
30
|
} = {}) {
|
|
28
31
|
const worldService = createWorldService({ worldCatalog, store });
|
|
29
32
|
const onboardingService = createOnboardingService({ worldService, store });
|
|
30
|
-
const
|
|
33
|
+
const publicIdentityService = createPublicIdentityService({ store });
|
|
34
|
+
const membershipService = createMembershipService({ worldService, store, publicIdentityService });
|
|
31
35
|
const worldAuthorizationService = createWorldAuthorizationService({
|
|
32
36
|
worldService,
|
|
33
37
|
membershipService,
|
|
34
38
|
});
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
39
|
+
const conversationFeedbackService = createConversationFeedbackService({ store });
|
|
40
|
+
const matchmakingService = createMatchmakingService({
|
|
41
|
+
worldService,
|
|
42
|
+
worldAuthorizationService,
|
|
43
|
+
store,
|
|
44
|
+
presence,
|
|
45
|
+
conversationFeedbackService,
|
|
46
|
+
});
|
|
47
|
+
const searchService = createWorldSearchService({
|
|
48
|
+
worldService,
|
|
49
|
+
worldAuthorizationService,
|
|
50
|
+
store,
|
|
51
|
+
presence,
|
|
52
|
+
conversationFeedbackService,
|
|
53
|
+
});
|
|
54
|
+
const worldAdminService = createWorldAdminService({
|
|
55
|
+
worldService,
|
|
56
|
+
worldAuthorizationService,
|
|
57
|
+
store,
|
|
58
|
+
publicIdentityService,
|
|
59
|
+
conversationFeedbackService,
|
|
60
|
+
});
|
|
38
61
|
const chatRequestService = createChatRequestService({
|
|
39
62
|
store,
|
|
40
63
|
relay,
|
|
41
64
|
presence,
|
|
42
65
|
worldService,
|
|
43
66
|
worldAuthorizationService,
|
|
67
|
+
publicIdentityService,
|
|
68
|
+
conversationFeedbackService,
|
|
44
69
|
});
|
|
45
70
|
const worldBroadcastService = createWorldBroadcastService({
|
|
46
71
|
worldService,
|
|
@@ -49,13 +74,14 @@ export function createClaworldProductShell({
|
|
|
49
74
|
chatRequestService,
|
|
50
75
|
store,
|
|
51
76
|
});
|
|
52
|
-
const friendService = createFriendService({ store, policy: relay?.policy });
|
|
77
|
+
const friendService = createFriendService({ store, policy: relay?.policy, publicIdentityService });
|
|
53
78
|
const socialLookupService = createSocialService({ worldService, store });
|
|
54
79
|
const feedbackService = createFeedbackService({ store });
|
|
80
|
+
const profileService = createPublicIdentityService({ store, conversationFeedbackService });
|
|
55
81
|
const socialService = {
|
|
56
82
|
...friendService,
|
|
57
|
-
|
|
58
|
-
return socialLookupService.
|
|
83
|
+
lookupAgentByIdentity(input) {
|
|
84
|
+
return socialLookupService.lookupAgentByIdentity(input);
|
|
59
85
|
},
|
|
60
86
|
};
|
|
61
87
|
const worldConversationOrchestrator = createWorldConversationOrchestrator({
|
|
@@ -65,6 +91,7 @@ export function createClaworldProductShell({
|
|
|
65
91
|
const productShell = {
|
|
66
92
|
contexts: {
|
|
67
93
|
onboarding: onboardingService,
|
|
94
|
+
profile: profileService,
|
|
68
95
|
worlds: worldService,
|
|
69
96
|
worldAuthorization: worldAuthorizationService,
|
|
70
97
|
membership: membershipService,
|
|
@@ -75,10 +102,12 @@ export function createClaworldProductShell({
|
|
|
75
102
|
chatRequests: chatRequestService,
|
|
76
103
|
moderation: worldAdminService,
|
|
77
104
|
feedback: feedbackService,
|
|
105
|
+
conversationFeedback: conversationFeedbackService,
|
|
78
106
|
orchestration: worldConversationOrchestrator,
|
|
79
107
|
},
|
|
80
108
|
registerRoutes(app) {
|
|
81
109
|
registerOnboardingRoutes(app, { onboardingService, store });
|
|
110
|
+
registerPublicIdentityRoutes(app, { publicIdentityService: profileService, store });
|
|
82
111
|
registerFriendRoutes(app, { friendService });
|
|
83
112
|
registerChatRequestRoutes(app, { chatRequestService, store });
|
|
84
113
|
registerWorldRoutes(app, {
|
|
@@ -101,6 +130,7 @@ export function createClaworldProductShell({
|
|
|
101
130
|
compatibleWith: ['relay_backend', 'openclaw_channel_plugin'],
|
|
102
131
|
boundedContexts: [
|
|
103
132
|
'onboarding',
|
|
133
|
+
'profile',
|
|
104
134
|
'worlds',
|
|
105
135
|
'membership',
|
|
106
136
|
'matchmaking',
|
|
@@ -110,6 +140,7 @@ export function createClaworldProductShell({
|
|
|
110
140
|
'chatRequests',
|
|
111
141
|
'moderation',
|
|
112
142
|
'feedback',
|
|
143
|
+
'conversationFeedback',
|
|
113
144
|
'orchestration',
|
|
114
145
|
],
|
|
115
146
|
routes: [
|
|
@@ -117,6 +148,8 @@ export function createClaworldProductShell({
|
|
|
117
148
|
'GET /v1/meta/install',
|
|
118
149
|
'GET /v1/onboarding/plan',
|
|
119
150
|
'POST /v1/onboarding/activate',
|
|
151
|
+
'GET /v1/profile/public-identity',
|
|
152
|
+
'PUT /v1/profile/public-identity',
|
|
120
153
|
'POST /v1/friend-requests',
|
|
121
154
|
'GET /v1/friend-requests',
|
|
122
155
|
'POST /v1/friend-requests/:friendRequestId/accept',
|
|
@@ -263,6 +263,7 @@ function buildDatingDemoFeed({
|
|
|
263
263
|
limit,
|
|
264
264
|
worldAuthorizationService,
|
|
265
265
|
resolvePresence,
|
|
266
|
+
conversationFeedbackService = null,
|
|
266
267
|
}) {
|
|
267
268
|
const { viewerMembership, activeMemberships, normalizedLimit, baseFeed } = buildBaseFeed({
|
|
268
269
|
world,
|
|
@@ -278,6 +279,13 @@ function buildDatingDemoFeed({
|
|
|
278
279
|
const candidateMembership = membershipById.get(candidate.sourceMembershipId);
|
|
279
280
|
return {
|
|
280
281
|
...candidate,
|
|
282
|
+
worldFeedbackSummary: conversationFeedbackService?.summarizeWorldAgent?.({
|
|
283
|
+
worldId: world.worldId,
|
|
284
|
+
agentId: candidate.targetAgentId,
|
|
285
|
+
}) || {
|
|
286
|
+
likesReceived: 0,
|
|
287
|
+
dislikesReceived: 0,
|
|
288
|
+
},
|
|
281
289
|
...buildDatingDemoScoreDetails(viewerMembership, candidateMembership),
|
|
282
290
|
};
|
|
283
291
|
})
|
|
@@ -306,6 +314,7 @@ export function createMatchmakingService({
|
|
|
306
314
|
worldAuthorizationService,
|
|
307
315
|
store = null,
|
|
308
316
|
presence = null,
|
|
317
|
+
conversationFeedbackService = null,
|
|
309
318
|
} = {}) {
|
|
310
319
|
function assertStore() {
|
|
311
320
|
if (!store) throw createConfigurationError();
|
|
@@ -345,6 +354,7 @@ export function createMatchmakingService({
|
|
|
345
354
|
limit,
|
|
346
355
|
worldAuthorizationService,
|
|
347
356
|
resolvePresence,
|
|
357
|
+
conversationFeedbackService,
|
|
348
358
|
});
|
|
349
359
|
}
|
|
350
360
|
|
|
@@ -364,7 +374,18 @@ export function createMatchmakingService({
|
|
|
364
374
|
candidateSource: 'active_memberships_online',
|
|
365
375
|
strategy: buildStrategy(world),
|
|
366
376
|
totalCandidates: baseFeed.candidates.length,
|
|
367
|
-
candidates: baseFeed.candidates
|
|
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
|
+
})),
|
|
368
389
|
};
|
|
369
390
|
},
|
|
370
391
|
listCandidates(options = {}) {
|
|
@@ -65,7 +65,7 @@ function buildNextStageSummary() {
|
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export function createMembershipService({ worldService, store = null } = {}) {
|
|
68
|
+
export function createMembershipService({ worldService, store = null, publicIdentityService = null } = {}) {
|
|
69
69
|
function assertStore() {
|
|
70
70
|
if (!store) throw createConfigurationError();
|
|
71
71
|
return store;
|
|
@@ -193,6 +193,10 @@ export function createMembershipService({ worldService, store = null } = {}) {
|
|
|
193
193
|
|
|
194
194
|
const agent = membershipStore.getAgent(normalizedAgentId);
|
|
195
195
|
if (!agent) throw createAgentNotFoundError(normalizedAgentId);
|
|
196
|
+
publicIdentityService?.assertPublicIdentityReady?.({
|
|
197
|
+
agentId: normalizedAgentId,
|
|
198
|
+
capability: 'join world',
|
|
199
|
+
});
|
|
196
200
|
|
|
197
201
|
const normalizedParticipantContextText = resolveNormalizedParticipantContextText({
|
|
198
202
|
world,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { randomUUID } from 'crypto';
|
|
2
1
|
import {
|
|
3
2
|
CLAWORLD_DOCTOR_COMMAND,
|
|
4
3
|
CLAWORLD_INSTALLER_COMMAND,
|
|
5
4
|
CLAWORLD_INSTALLER_PACKAGE_NAME,
|
|
6
5
|
CLAWORLD_OPENCLAW_MIN_HOST_VERSION,
|
|
6
|
+
CLAWORLD_UNINSTALL_COMMAND,
|
|
7
7
|
CLAWORLD_UPDATE_COMMAND,
|
|
8
8
|
} from '../../openclaw/installer/constants.js';
|
|
9
9
|
|
|
@@ -55,10 +55,6 @@ function createInvalidActivationRequestError(fieldId, message) {
|
|
|
55
55
|
return error;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
function createHiddenActivationAgentCode() {
|
|
59
|
-
return `claworld_install_${randomUUID().replace(/-/g, '').slice(0, 20)}`;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
58
|
function createActivatedResponse({ agent, appToken, created, bindingSource }) {
|
|
63
59
|
return {
|
|
64
60
|
status: 'activated',
|
|
@@ -66,7 +62,6 @@ function createActivatedResponse({ agent, appToken, created, bindingSource }) {
|
|
|
66
62
|
bindingSource: normalizeText(bindingSource, null),
|
|
67
63
|
agentId: normalizeText(agent?.agentId, null),
|
|
68
64
|
appToken: normalizeText(appToken, null),
|
|
69
|
-
agentCode: null,
|
|
70
65
|
};
|
|
71
66
|
}
|
|
72
67
|
|
|
@@ -74,7 +69,6 @@ function validateActivationInput(input = {}) {
|
|
|
74
69
|
const normalizedInput = input && typeof input === 'object' && !Array.isArray(input) ? input : {};
|
|
75
70
|
const blockedFields = [
|
|
76
71
|
['agentId', 'dialog-first activation must not receive a user-provided agentId'],
|
|
77
|
-
['agentCode', 'dialog-first activation must not receive a user-provided agentCode'],
|
|
78
72
|
['code', 'dialog-first activation must not receive a user-provided code'],
|
|
79
73
|
['appToken', 'dialog-first activation must not receive a user-provided appToken in the request body'],
|
|
80
74
|
];
|
|
@@ -91,19 +85,13 @@ function validateActivationInput(input = {}) {
|
|
|
91
85
|
}
|
|
92
86
|
|
|
93
87
|
function createActivatedAgent({ store, displayName }) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (error?.message === 'address already exists') continue;
|
|
102
|
-
throw error;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
throw new Error('failed_to_generate_activation_identity');
|
|
88
|
+
return store.createAgent({
|
|
89
|
+
displayName,
|
|
90
|
+
publicIdentity: {
|
|
91
|
+
displayName,
|
|
92
|
+
status: 'pending',
|
|
93
|
+
},
|
|
94
|
+
});
|
|
107
95
|
}
|
|
108
96
|
|
|
109
97
|
export function createOnboardingService({ worldService, store = null } = {}) {
|
|
@@ -127,6 +115,7 @@ export function createOnboardingService({ worldService, store = null } = {}) {
|
|
|
127
115
|
install: CLAWORLD_INSTALLER_COMMAND,
|
|
128
116
|
doctor: CLAWORLD_DOCTOR_COMMAND,
|
|
129
117
|
update: CLAWORLD_UPDATE_COMMAND,
|
|
118
|
+
uninstall: CLAWORLD_UNINSTALL_COMMAND,
|
|
130
119
|
},
|
|
131
120
|
},
|
|
132
121
|
plugin: {
|
|
@@ -163,7 +152,7 @@ export function createOnboardingService({ worldService, store = null } = {}) {
|
|
|
163
152
|
requiresUserCode: false,
|
|
164
153
|
canonicalIdentityField: 'agentId',
|
|
165
154
|
credentialField: 'appToken',
|
|
166
|
-
responseFields: ['status', 'agentId', 'appToken'
|
|
155
|
+
responseFields: ['status', 'agentId', 'appToken'],
|
|
167
156
|
},
|
|
168
157
|
verification: {
|
|
169
158
|
statusRoute: '/plugins/claworld/status',
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { resolveAuthenticatedAgentId } from '../../lib/http-auth.js';
|
|
2
|
+
|
|
3
|
+
function sendProfileError(res, error) {
|
|
4
|
+
const status = Number.isInteger(error?.status) ? error.status : 500;
|
|
5
|
+
if (error?.responseBody && typeof error.responseBody === 'object') {
|
|
6
|
+
return res.status(status).json(error.responseBody);
|
|
7
|
+
}
|
|
8
|
+
const code = typeof error?.code === 'string' ? error.code : 'internal_error';
|
|
9
|
+
return res.status(status).json({ error: code, message: error?.message || code });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function sendMissingAgentIdentity(res) {
|
|
13
|
+
return res.status(401).json({
|
|
14
|
+
error: 'not_authenticated',
|
|
15
|
+
reason: 'agent_identity_required',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function registerPublicIdentityRoutes(app, { publicIdentityService, store }) {
|
|
20
|
+
app.get('/v1/profile/public-identity', (req, res) => {
|
|
21
|
+
const authAgent = resolveAuthenticatedAgentId({
|
|
22
|
+
store,
|
|
23
|
+
req,
|
|
24
|
+
providedAgentId: req.query.agentId,
|
|
25
|
+
fieldName: 'agentId',
|
|
26
|
+
});
|
|
27
|
+
if (!authAgent.ok) return res.status(authAgent.status).json(authAgent.body);
|
|
28
|
+
if (!authAgent.agentId) return sendMissingAgentIdentity(res);
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const result = publicIdentityService.getPublicIdentityStatus({
|
|
32
|
+
agentId: authAgent.agentId,
|
|
33
|
+
});
|
|
34
|
+
return res.json(result);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return sendProfileError(res, error);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
app.put('/v1/profile/public-identity', async (req, res) => {
|
|
41
|
+
const authAgent = resolveAuthenticatedAgentId({
|
|
42
|
+
store,
|
|
43
|
+
req,
|
|
44
|
+
providedAgentId: req.body?.agentId,
|
|
45
|
+
fieldName: 'agentId',
|
|
46
|
+
});
|
|
47
|
+
if (!authAgent.ok) return res.status(authAgent.status).json(authAgent.body);
|
|
48
|
+
if (!authAgent.agentId) return sendMissingAgentIdentity(res);
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const result = await publicIdentityService.updatePublicIdentity({
|
|
52
|
+
agentId: authAgent.agentId,
|
|
53
|
+
displayName: req.body?.displayName,
|
|
54
|
+
});
|
|
55
|
+
return res.json(result);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return sendProfileError(res, error);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildPublicIdentityMissingFields,
|
|
3
|
+
formatPublicIdentityDisplay,
|
|
4
|
+
generatePublicIdentityCode,
|
|
5
|
+
PUBLIC_IDENTITY_STATUS,
|
|
6
|
+
resolvePublicIdentity,
|
|
7
|
+
validatePublicDisplayName,
|
|
8
|
+
} from '../../lib/public-identity.js';
|
|
9
|
+
|
|
10
|
+
function normalizeText(value, fallback = null) {
|
|
11
|
+
if (value == null) return fallback;
|
|
12
|
+
const normalized = String(value).trim();
|
|
13
|
+
return normalized || fallback;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function createConfigurationError() {
|
|
17
|
+
const error = new Error('public_identity_store_unavailable');
|
|
18
|
+
error.code = 'public_identity_store_unavailable';
|
|
19
|
+
error.status = 500;
|
|
20
|
+
return error;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function createAgentNotFoundError(agentId) {
|
|
24
|
+
const error = new Error(`agent_not_found:${agentId}`);
|
|
25
|
+
error.code = 'agent_not_found';
|
|
26
|
+
error.status = 404;
|
|
27
|
+
error.responseBody = {
|
|
28
|
+
error: error.code,
|
|
29
|
+
message: 'agent not found',
|
|
30
|
+
agentId: normalizeText(agentId, null),
|
|
31
|
+
};
|
|
32
|
+
return error;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function createInvalidPublicIdentityRequest(fieldId, message, code = 'invalid_public_identity') {
|
|
36
|
+
const error = new Error(code);
|
|
37
|
+
error.code = code;
|
|
38
|
+
error.status = 400;
|
|
39
|
+
error.responseBody = {
|
|
40
|
+
error: code,
|
|
41
|
+
message: 'public identity request is invalid',
|
|
42
|
+
fieldErrors: [
|
|
43
|
+
{
|
|
44
|
+
fieldId,
|
|
45
|
+
message,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
return error;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createPublicIdentityIncompleteError(agent, {
|
|
53
|
+
capability = null,
|
|
54
|
+
nextTool = 'claworld_update_public_identity',
|
|
55
|
+
} = {}) {
|
|
56
|
+
const projected = projectPublicIdentityStatus(agent, { nextTool });
|
|
57
|
+
const capabilityLabel = normalizeText(capability, 'this Claworld capability');
|
|
58
|
+
const error = new Error(`public_identity_incomplete:${agent?.agentId || 'unknown'}`);
|
|
59
|
+
error.code = 'public_identity_incomplete';
|
|
60
|
+
error.status = 409;
|
|
61
|
+
error.responseBody = {
|
|
62
|
+
status: 'blocked',
|
|
63
|
+
error: error.code,
|
|
64
|
+
message: `${capabilityLabel} requires a public Claworld identity`,
|
|
65
|
+
agentId: normalizeText(agent?.agentId, null),
|
|
66
|
+
requiredAction: projected.requiredAction,
|
|
67
|
+
nextAction: projected.nextAction,
|
|
68
|
+
nextTool: projected.nextTool,
|
|
69
|
+
missingFields: projected.missingFields,
|
|
70
|
+
publicIdentity: projected.publicIdentity,
|
|
71
|
+
};
|
|
72
|
+
return error;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function projectPublicIdentityStatus(agent, {
|
|
76
|
+
nextTool = 'claworld_update_public_identity',
|
|
77
|
+
conversationFeedbackService = null,
|
|
78
|
+
} = {}) {
|
|
79
|
+
const publicIdentity = resolvePublicIdentity(agent);
|
|
80
|
+
const ready = publicIdentity.status === PUBLIC_IDENTITY_STATUS.READY;
|
|
81
|
+
return {
|
|
82
|
+
status: ready ? 'ready' : 'pending',
|
|
83
|
+
agentId: normalizeText(agent?.agentId, null),
|
|
84
|
+
ready,
|
|
85
|
+
publicIdentity: {
|
|
86
|
+
status: publicIdentity.status,
|
|
87
|
+
displayName: publicIdentity.displayName,
|
|
88
|
+
code: publicIdentity.code,
|
|
89
|
+
displayIdentity: formatPublicIdentityDisplay(publicIdentity),
|
|
90
|
+
confirmedAt: publicIdentity.confirmedAt,
|
|
91
|
+
updatedAt: publicIdentity.updatedAt,
|
|
92
|
+
},
|
|
93
|
+
recommendedDisplayName: normalizeText(agent?.displayName, publicIdentity.displayName || null),
|
|
94
|
+
nextAction: ready ? 'continue_claworld_flow' : 'set_public_identity',
|
|
95
|
+
requiredAction: ready ? null : 'set_public_identity',
|
|
96
|
+
nextTool: ready ? null : nextTool,
|
|
97
|
+
missingFields: ready ? [] : buildPublicIdentityMissingFields(agent),
|
|
98
|
+
feedbackSummary: conversationFeedbackService?.summarizeAgent?.({ agentId: agent?.agentId }) || {
|
|
99
|
+
totalLikesReceived: 0,
|
|
100
|
+
totalDislikesReceived: 0,
|
|
101
|
+
totalLikesGiven: 0,
|
|
102
|
+
totalDislikesGiven: 0,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function createPublicIdentityService({ store = null, conversationFeedbackService = null } = {}) {
|
|
108
|
+
function assertStore() {
|
|
109
|
+
if (!store) throw createConfigurationError();
|
|
110
|
+
return store;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function requireAgent(agentId) {
|
|
114
|
+
const normalizedAgentId = normalizeText(agentId, null);
|
|
115
|
+
if (!normalizedAgentId) {
|
|
116
|
+
throw createInvalidPublicIdentityRequest('agentId', 'agentId is required');
|
|
117
|
+
}
|
|
118
|
+
const agent = assertStore().getAgent(normalizedAgentId);
|
|
119
|
+
if (!agent) throw createAgentNotFoundError(normalizedAgentId);
|
|
120
|
+
return agent;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function codeExists(code, excludeAgentId = null) {
|
|
124
|
+
return assertStore()
|
|
125
|
+
.listAgents()
|
|
126
|
+
.some((agent) => agent.agentId !== excludeAgentId && resolvePublicIdentity(agent).code === code);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function issueStableCode(agentId) {
|
|
130
|
+
for (let attempt = 0; attempt < 32; attempt += 1) {
|
|
131
|
+
const code = generatePublicIdentityCode();
|
|
132
|
+
if (!codeExists(code, agentId)) return code;
|
|
133
|
+
}
|
|
134
|
+
throw new Error('public_identity_code_generation_failed');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function isPublicIdentityCodeConflict(error) {
|
|
138
|
+
return error?.code === 'public_identity_code_conflict';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
getPublicIdentityStatus({ agentId } = {}) {
|
|
143
|
+
const agent = requireAgent(agentId);
|
|
144
|
+
return projectPublicIdentityStatus(agent, { conversationFeedbackService });
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
async updatePublicIdentity({ agentId, displayName } = {}) {
|
|
148
|
+
const agent = requireAgent(agentId);
|
|
149
|
+
const validation = validatePublicDisplayName(displayName);
|
|
150
|
+
if (!validation.ok) {
|
|
151
|
+
throw createInvalidPublicIdentityRequest('displayName', validation.message, validation.code);
|
|
152
|
+
}
|
|
153
|
+
const existingIdentity = resolvePublicIdentity(agent);
|
|
154
|
+
const stableCode = existingIdentity.code || null;
|
|
155
|
+
|
|
156
|
+
for (let attempt = 0; attempt < 32; attempt += 1) {
|
|
157
|
+
const now = assertStore().now();
|
|
158
|
+
const nextCode = stableCode || issueStableCode(agent.agentId);
|
|
159
|
+
try {
|
|
160
|
+
const updatedAgent = await assertStore().updateAgent(agent.agentId, {
|
|
161
|
+
displayName: validation.value,
|
|
162
|
+
publicIdentity: {
|
|
163
|
+
displayName: validation.value,
|
|
164
|
+
code: nextCode,
|
|
165
|
+
status: PUBLIC_IDENTITY_STATUS.READY,
|
|
166
|
+
confirmedAt: existingIdentity.confirmedAt || now,
|
|
167
|
+
updatedAt: now,
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
return projectPublicIdentityStatus(updatedAgent, { conversationFeedbackService });
|
|
171
|
+
} catch (error) {
|
|
172
|
+
if (!stableCode && isPublicIdentityCodeConflict(error)) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
throw new Error('public_identity_code_generation_failed');
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
assertPublicIdentityReady({ agentId, capability = null } = {}) {
|
|
183
|
+
const agent = requireAgent(agentId);
|
|
184
|
+
if (resolvePublicIdentity(agent).status !== PUBLIC_IDENTITY_STATUS.READY) {
|
|
185
|
+
throw createPublicIdentityIncompleteError(agent, { capability });
|
|
186
|
+
}
|
|
187
|
+
return projectPublicIdentityStatus(agent, { conversationFeedbackService });
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
@@ -255,6 +255,7 @@ export function createWorldSearchService({
|
|
|
255
255
|
worldAuthorizationService,
|
|
256
256
|
store = null,
|
|
257
257
|
presence = null,
|
|
258
|
+
conversationFeedbackService = null,
|
|
258
259
|
} = {}) {
|
|
259
260
|
function assertStore() {
|
|
260
261
|
if (!store) throw createConfigurationError();
|
|
@@ -339,9 +340,8 @@ export function createWorldSearchService({
|
|
|
339
340
|
playerId: candidateAgent.agentId,
|
|
340
341
|
membershipId: membership.membershipId,
|
|
341
342
|
worldId: world.worldId,
|
|
342
|
-
displayName: normalizeText(candidateAgent.displayName, candidateAgent.
|
|
343
|
+
displayName: normalizeText(candidateAgent.displayName, candidateAgent.agentId),
|
|
343
344
|
headline: normalizeText(membership.profileSnapshot?.headline, null),
|
|
344
|
-
address: candidateAgent.address,
|
|
345
345
|
online: presenceState.online === true,
|
|
346
346
|
connectedAt: presenceState.connectedAt,
|
|
347
347
|
lastHeartbeatAt: presenceState.lastHeartbeatAt,
|
|
@@ -352,6 +352,13 @@ export function createWorldSearchService({
|
|
|
352
352
|
reasonSummary: summarizeMatchedFields(matchedFields),
|
|
353
353
|
joinedAt: membership.joinedAt,
|
|
354
354
|
profileSummary: projectProfileSummary(world, membership.profileSnapshot || {}, candidateAgent),
|
|
355
|
+
worldFeedbackSummary: conversationFeedbackService?.summarizeWorldAgent?.({
|
|
356
|
+
worldId: world.worldId,
|
|
357
|
+
agentId: candidateAgent.agentId,
|
|
358
|
+
}) || {
|
|
359
|
+
likesReceived: 0,
|
|
360
|
+
dislikesReceived: 0,
|
|
361
|
+
},
|
|
355
362
|
};
|
|
356
363
|
})
|
|
357
364
|
.filter(Boolean);
|
|
@@ -56,7 +56,10 @@ export function registerChatRequestRoutes(app, { chatRequestService, store }) {
|
|
|
56
56
|
targetAgentId: req.body?.targetAgentId,
|
|
57
57
|
kickoffBrief: req.body?.kickoffBrief,
|
|
58
58
|
openingMessage: req.body?.openingMessage,
|
|
59
|
+
openingPayload: req.body?.openingPayload,
|
|
59
60
|
worldId: req.body?.worldId,
|
|
61
|
+
requestContext: req.body?.requestContext,
|
|
62
|
+
source: req.body?.source,
|
|
60
63
|
});
|
|
61
64
|
res.status(201).json(result);
|
|
62
65
|
} catch (error) {
|
|
@@ -75,7 +78,7 @@ export function registerChatRequestRoutes(app, { chatRequestService, store }) {
|
|
|
75
78
|
if (!authAgent.agentId) return sendMissingAgentIdentity(res);
|
|
76
79
|
|
|
77
80
|
try {
|
|
78
|
-
const result = chatRequestService.
|
|
81
|
+
const result = chatRequestService.listChatInbox({
|
|
79
82
|
agentId: authAgent.agentId,
|
|
80
83
|
direction: req.query.direction,
|
|
81
84
|
});
|