@xfxstudio/claworld 0.2.9 → 0.2.10-beta.1
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 +5 -1
- package/skills/claworld-join-and-chat/SKILL.md +21 -1
- package/skills/claworld-manage-worlds/SKILL.md +81 -10
- package/src/lib/agent-profile.js +8 -3
- package/src/lib/chat-request.js +0 -1
- package/src/lib/policy.js +2 -6
- package/src/lib/public-identity.js +175 -0
- package/src/lib/relay/kickoff-text.js +1 -0
- package/src/openclaw/installer/cli.js +48 -4
- package/src/openclaw/installer/constants.js +1 -0
- package/src/openclaw/installer/core.js +247 -71
- package/src/openclaw/installer/doctor.js +31 -17
- package/src/openclaw/plugin/account-identity.js +1 -2
- package/src/openclaw/plugin/claworld-channel-plugin.js +453 -263
- package/src/openclaw/plugin/config-schema.js +9 -23
- package/src/openclaw/plugin/managed-config.js +294 -84
- package/src/openclaw/plugin/onboarding.js +37 -45
- package/src/openclaw/plugin/register.js +124 -13
- package/src/openclaw/plugin/relay-client.js +233 -17
- 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 +26 -3
- package/src/openclaw/runtime/tool-inventory.js +7 -0
- 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 +16 -26
- 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-service.js +22 -7
- 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
|
@@ -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',
|
|
@@ -175,9 +164,9 @@ export function createOnboardingService({ worldService, store = null } = {}) {
|
|
|
175
164
|
'installer validates OpenClaw availability and minimum host version',
|
|
176
165
|
'installer verifies or installs the claworld OpenClaw plugin package',
|
|
177
166
|
'installer writes or refreshes the managed claworld channel config and binds it to the local main agent by default',
|
|
178
|
-
'installer
|
|
179
|
-
'
|
|
180
|
-
'
|
|
167
|
+
'installer reloads or starts the runtime and verifies the managed channel shape without requiring backend activation',
|
|
168
|
+
'first-run readiness goes through claworld_pair_agent and then claworld_update_public_identity',
|
|
169
|
+
'claworld_update_public_identity performs activation when needed and completes the public displayName#code identity',
|
|
181
170
|
`ongoing lifecycle uses ${CLAWORLD_UPDATE_COMMAND} for tracked package updates plus managed repair, then ${CLAWORLD_DOCTOR_COMMAND} for health confirmation`,
|
|
182
171
|
],
|
|
183
172
|
recommendedWorlds,
|
|
@@ -193,8 +182,9 @@ export function createOnboardingService({ worldService, store = null } = {}) {
|
|
|
193
182
|
selectedWorld: selectedWorld ? { worldId: selectedWorld.worldId, displayName: selectedWorld.displayName } : null,
|
|
194
183
|
actions: [
|
|
195
184
|
'install the claworld plugin and write the managed config shape',
|
|
196
|
-
'
|
|
197
|
-
'
|
|
185
|
+
'run claworld_pair_agent to confirm readiness after install',
|
|
186
|
+
'if public identity is pending, call claworld_update_public_identity',
|
|
187
|
+
'claworld_update_public_identity activates the backend binding when needed and persists the returned appToken',
|
|
198
188
|
'collect required world profile fields',
|
|
199
189
|
'validate world membership eligibility',
|
|
200
190
|
'start the first A2A loop or deliver world content',
|
|
@@ -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);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { normalizeAgentProfile, resolveAgentDisplayName, resolveAgentVisibility } from '../../lib/agent-profile.js';
|
|
2
2
|
import { normalizeChatRequestInput } from '../../lib/chat-request.js';
|
|
3
|
+
import { resolvePublicIdentity } from '../../lib/public-identity.js';
|
|
3
4
|
import { createKickoffBrief, resolveStoredKickoffBrief } from '../../lib/relay/kickoff-text.js';
|
|
4
5
|
import { WORLD_ACTIONS } from '../worlds/world-authorization.js';
|
|
5
6
|
import { normalizeChatRequestApprovalPolicy } from '../contracts/chat-request-approval-policy.js';
|
|
@@ -67,7 +68,7 @@ function createAgentNotFoundError(agentId) {
|
|
|
67
68
|
return error;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
function
|
|
71
|
+
function createTargetAgentNotFoundError(targetAgentId) {
|
|
71
72
|
const error = new Error(`chat_request_target_not_found:${targetAgentId}`);
|
|
72
73
|
error.code = 'chat_request_target_not_found';
|
|
73
74
|
error.status = 404;
|
|
@@ -124,10 +125,8 @@ function projectAgent(store, agentId, presence = null) {
|
|
|
124
125
|
const presenceState = presence?.getPresence?.(agent.agentId) || store.getPresence(agent.agentId);
|
|
125
126
|
return {
|
|
126
127
|
agentId: agent.agentId,
|
|
127
|
-
agentCode: agent.agentCode,
|
|
128
|
-
domain: agent.domain,
|
|
129
|
-
address: agent.address,
|
|
130
128
|
displayName: resolveAgentDisplayName(agent),
|
|
129
|
+
publicIdentity: resolvePublicIdentity(agent),
|
|
131
130
|
profile: normalizeAgentProfile(agent.profile),
|
|
132
131
|
discoverable: visibility.discoverable,
|
|
133
132
|
contactable: visibility.contactable,
|
|
@@ -374,6 +373,8 @@ export function createChatRequestService({
|
|
|
374
373
|
presence = null,
|
|
375
374
|
worldService = null,
|
|
376
375
|
worldAuthorizationService = null,
|
|
376
|
+
publicIdentityService = null,
|
|
377
|
+
conversationFeedbackService = null,
|
|
377
378
|
} = {}) {
|
|
378
379
|
function assertStore() {
|
|
379
380
|
if (!store) throw createConfigurationError('chat_request_store_unavailable');
|
|
@@ -440,6 +441,15 @@ export function createChatRequestService({
|
|
|
440
441
|
function projectChatInboxChat(conversation = {}, viewerAgentId = null, request = null) {
|
|
441
442
|
const direction = resolveInboxChatDirection(viewerAgentId, request);
|
|
442
443
|
const counterpartyAgentId = resolveInboxChatCounterpartyAgentId(viewerAgentId, request, conversation);
|
|
444
|
+
const feedbackSummary = conversationFeedbackService?.summarizeConversation?.({
|
|
445
|
+
conversationKey: conversation.conversationKey,
|
|
446
|
+
viewerAgentId,
|
|
447
|
+
}) || {
|
|
448
|
+
likeCount: 0,
|
|
449
|
+
dislikeCount: 0,
|
|
450
|
+
viewerGave: null,
|
|
451
|
+
viewerReceived: null,
|
|
452
|
+
};
|
|
443
453
|
|
|
444
454
|
return {
|
|
445
455
|
chatRequestId: normalizeText(request?.chatRequestId || request?.requestId, null),
|
|
@@ -452,6 +462,7 @@ export function createChatRequestService({
|
|
|
452
462
|
localSessionKey: normalizeText(conversation.sessionKey, null),
|
|
453
463
|
counterparty: projectAgent(assertStore(), counterpartyAgentId, presence),
|
|
454
464
|
conversation: resolveInboxConversationWorldSummary(worldService, request, conversation),
|
|
465
|
+
feedbackSummary,
|
|
455
466
|
};
|
|
456
467
|
}
|
|
457
468
|
|
|
@@ -501,6 +512,10 @@ export function createChatRequestService({
|
|
|
501
512
|
source = 'chat_request',
|
|
502
513
|
} = {}) {
|
|
503
514
|
requireAgent(fromAgentId);
|
|
515
|
+
publicIdentityService?.assertPublicIdentityReady?.({
|
|
516
|
+
agentId: fromAgentId,
|
|
517
|
+
capability: 'request chat',
|
|
518
|
+
});
|
|
504
519
|
const normalizedTargetAgentId = normalizeText(targetAgentId, null);
|
|
505
520
|
if (!normalizedTargetAgentId) {
|
|
506
521
|
throw createInvalidChatRequestError('chat_request_target_required', 'chat request target requires targetAgentId');
|
|
@@ -513,8 +528,8 @@ export function createChatRequestService({
|
|
|
513
528
|
});
|
|
514
529
|
const normalizedWorldId = normalizeConversationWorldId(worldId)
|
|
515
530
|
|| normalizeConversationWorldId(normalizedRequestInput.conversation?.worldId);
|
|
516
|
-
if (!targetAgent?.
|
|
517
|
-
throw
|
|
531
|
+
if (!targetAgent?.agentId) {
|
|
532
|
+
throw createTargetAgentNotFoundError(normalizedTargetAgentId);
|
|
518
533
|
}
|
|
519
534
|
const normalizedOpeningPayload = cloneJsonObject(openingPayload) || cloneJsonObject(normalizedRequestInput.openingPayload);
|
|
520
535
|
const normalizedKickoffBrief = createKickoffBrief({
|
|
@@ -549,7 +564,7 @@ export function createChatRequestService({
|
|
|
549
564
|
|
|
550
565
|
const result = await assertRelay('createChatRequest').createChatRequest({
|
|
551
566
|
fromAgentId,
|
|
552
|
-
|
|
567
|
+
targetAgentId: targetAgent.agentId,
|
|
553
568
|
kickoffBrief: normalizedKickoffBrief,
|
|
554
569
|
openingMessage: normalizeText(
|
|
555
570
|
normalizedKickoffBrief?.text,
|
|
@@ -12,7 +12,7 @@ export function registerFriendRoutes(app, { friendService }) {
|
|
|
12
12
|
try {
|
|
13
13
|
const result = await friendService.createFriendRequest({
|
|
14
14
|
fromAgentId: req.body?.fromAgentId,
|
|
15
|
-
|
|
15
|
+
targetAgentId: req.body?.targetAgentId,
|
|
16
16
|
message: req.body?.message,
|
|
17
17
|
metadata: req.body?.metadata,
|
|
18
18
|
});
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { normalizeAgentProfile, resolveAgentDisplayName, resolveAgentVisibility } from '../../lib/agent-profile.js';
|
|
2
2
|
import { createRelayPolicyHooks, evaluatePolicyHook } from '../../lib/policy.js';
|
|
3
|
+
import { resolvePublicIdentity } from '../../lib/public-identity.js';
|
|
3
4
|
|
|
4
5
|
function normalizeAgentId(agentId) {
|
|
5
6
|
const normalized = String(agentId || '').trim();
|
|
6
7
|
return normalized || null;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
function normalizeAddress(address) {
|
|
10
|
-
const normalized = String(address || '').trim().toLowerCase();
|
|
11
|
-
return normalized || null;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
10
|
function normalizeDirection(direction) {
|
|
15
11
|
const normalized = String(direction || '').trim().toLowerCase();
|
|
16
12
|
return normalized === 'inbound' || normalized === 'outbound' ? normalized : null;
|
|
@@ -54,8 +50,8 @@ function createAgentNotFoundError(agentId) {
|
|
|
54
50
|
return error;
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
function createTargetNotFoundError(
|
|
58
|
-
const error = new Error(`friend_target_not_found:${
|
|
53
|
+
function createTargetNotFoundError(targetAgentId) {
|
|
54
|
+
const error = new Error(`friend_target_not_found:${targetAgentId}`);
|
|
59
55
|
error.code = 'friend_target_not_found';
|
|
60
56
|
error.status = 404;
|
|
61
57
|
return error;
|
|
@@ -131,10 +127,8 @@ function projectAgent(store, agentId) {
|
|
|
131
127
|
const visibility = resolveAgentVisibility(agent);
|
|
132
128
|
return {
|
|
133
129
|
agentId: agent.agentId,
|
|
134
|
-
agentCode: agent.agentCode,
|
|
135
|
-
domain: agent.domain,
|
|
136
|
-
address: agent.address,
|
|
137
130
|
displayName: resolveAgentDisplayName(agent),
|
|
131
|
+
publicIdentity: resolvePublicIdentity(agent),
|
|
138
132
|
profile: normalizeAgentProfile(agent.profile),
|
|
139
133
|
discoverable: visibility.discoverable,
|
|
140
134
|
contactable: visibility.contactable,
|
|
@@ -149,7 +143,7 @@ function resolvePeerAgentId(friendship, viewerAgentId) {
|
|
|
149
143
|
return null;
|
|
150
144
|
}
|
|
151
145
|
|
|
152
|
-
export function createFriendService({ store = null, policy = null } = {}) {
|
|
146
|
+
export function createFriendService({ store = null, policy = null, publicIdentityService = null } = {}) {
|
|
153
147
|
const relayPolicy = createRelayPolicyHooks(policy);
|
|
154
148
|
|
|
155
149
|
function assertStore() {
|
|
@@ -307,23 +301,27 @@ export function createFriendService({ store = null, policy = null } = {}) {
|
|
|
307
301
|
}
|
|
308
302
|
|
|
309
303
|
return {
|
|
310
|
-
async createFriendRequest({ fromAgentId,
|
|
304
|
+
async createFriendRequest({ fromAgentId, targetAgentId, message = null, metadata = {} } = {}) {
|
|
311
305
|
const friendshipStore = assertStore();
|
|
312
306
|
const fromAgent = requireAgent(fromAgentId);
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
307
|
+
publicIdentityService?.assertPublicIdentityReady?.({
|
|
308
|
+
agentId: fromAgent.agentId,
|
|
309
|
+
capability: 'send friend request',
|
|
310
|
+
});
|
|
311
|
+
const normalizedTargetAgentId = normalizeAgentId(targetAgentId);
|
|
312
|
+
if (!normalizedTargetAgentId) {
|
|
313
|
+
throw createTargetNotFoundError(targetAgentId);
|
|
316
314
|
}
|
|
317
315
|
|
|
318
|
-
const toAgent = friendshipStore.
|
|
319
|
-
if (!toAgent) throw createTargetNotFoundError(
|
|
316
|
+
const toAgent = friendshipStore.getAgent(normalizedTargetAgentId);
|
|
317
|
+
if (!toAgent) throw createTargetNotFoundError(normalizedTargetAgentId);
|
|
320
318
|
|
|
321
319
|
const canRequest = evaluatePolicyHook({
|
|
322
320
|
hook: relayPolicy.canRequest,
|
|
323
321
|
context: {
|
|
324
322
|
fromAgentId: fromAgent.agentId,
|
|
325
323
|
fromAgent,
|
|
326
|
-
|
|
324
|
+
targetAgentId: normalizedTargetAgentId,
|
|
327
325
|
toAgent,
|
|
328
326
|
requestContext: {
|
|
329
327
|
type: 'friend_request',
|
|
@@ -382,7 +380,6 @@ export function createFriendService({ store = null, policy = null } = {}) {
|
|
|
382
380
|
const request = await friendshipStore.createFriendRequest({
|
|
383
381
|
fromAgentId: fromAgent.agentId,
|
|
384
382
|
toAgentId: toAgent.agentId,
|
|
385
|
-
toAddress: toAgent.address,
|
|
386
383
|
message: normalizeMessage(message),
|
|
387
384
|
metadata,
|
|
388
385
|
});
|
|
@@ -10,8 +10,8 @@ function sendSocialError(res, error) {
|
|
|
10
10
|
export function registerSocialRoutes(app, { socialService }) {
|
|
11
11
|
app.get('/v1/social/agents/lookup', (req, res) => {
|
|
12
12
|
try {
|
|
13
|
-
const result = socialService.
|
|
14
|
-
|
|
13
|
+
const result = socialService.lookupAgentByIdentity({
|
|
14
|
+
identity: req.query.identity,
|
|
15
15
|
});
|
|
16
16
|
res.json(result);
|
|
17
17
|
} catch (error) {
|