@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,82 +0,0 @@
|
|
|
1
|
-
function sendFriendError(res, error) {
|
|
2
|
-
const status = Number.isInteger(error?.status) ? error.status : 500;
|
|
3
|
-
if (error?.responseBody && typeof error.responseBody === 'object') {
|
|
4
|
-
return res.status(status).json(error.responseBody);
|
|
5
|
-
}
|
|
6
|
-
const code = typeof error?.code === 'string' ? error.code : 'internal_error';
|
|
7
|
-
return res.status(status).json({ error: code, message: error?.message || code });
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function registerFriendRoutes(app, { friendService }) {
|
|
11
|
-
app.post('/v1/friend-requests', async (req, res) => {
|
|
12
|
-
try {
|
|
13
|
-
const result = await friendService.createFriendRequest({
|
|
14
|
-
fromAgentId: req.body?.fromAgentId,
|
|
15
|
-
targetAgentId: req.body?.targetAgentId,
|
|
16
|
-
message: req.body?.message,
|
|
17
|
-
metadata: req.body?.metadata,
|
|
18
|
-
});
|
|
19
|
-
res.status(result.created ? 201 : 200).json(result);
|
|
20
|
-
} catch (error) {
|
|
21
|
-
sendFriendError(res, error);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
app.get('/v1/friend-requests', (req, res) => {
|
|
26
|
-
try {
|
|
27
|
-
const result = friendService.listFriendRequests({
|
|
28
|
-
agentId: req.query.agentId,
|
|
29
|
-
direction: req.query.direction,
|
|
30
|
-
status: req.query.status,
|
|
31
|
-
});
|
|
32
|
-
res.json(result);
|
|
33
|
-
} catch (error) {
|
|
34
|
-
sendFriendError(res, error);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
app.post('/v1/friend-requests/:friendRequestId/accept', async (req, res) => {
|
|
39
|
-
try {
|
|
40
|
-
const result = await friendService.acceptFriendRequest(req.params.friendRequestId, {
|
|
41
|
-
actorAgentId: req.body?.actorAgentId,
|
|
42
|
-
});
|
|
43
|
-
res.json(result);
|
|
44
|
-
} catch (error) {
|
|
45
|
-
sendFriendError(res, error);
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
app.post('/v1/friend-requests/:friendRequestId/reject', async (req, res) => {
|
|
50
|
-
try {
|
|
51
|
-
const result = await friendService.rejectFriendRequest(req.params.friendRequestId, {
|
|
52
|
-
actorAgentId: req.body?.actorAgentId,
|
|
53
|
-
});
|
|
54
|
-
res.json(result);
|
|
55
|
-
} catch (error) {
|
|
56
|
-
sendFriendError(res, error);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
app.get('/v1/friends', (req, res) => {
|
|
61
|
-
try {
|
|
62
|
-
const result = friendService.listFriends({
|
|
63
|
-
agentId: req.query.agentId,
|
|
64
|
-
});
|
|
65
|
-
res.json(result);
|
|
66
|
-
} catch (error) {
|
|
67
|
-
sendFriendError(res, error);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
app.delete('/v1/friends/:peerAgentId', async (req, res) => {
|
|
72
|
-
try {
|
|
73
|
-
const result = await friendService.removeFriend({
|
|
74
|
-
actorAgentId: req.body?.actorAgentId || req.query.actorAgentId,
|
|
75
|
-
peerAgentId: req.params.peerAgentId,
|
|
76
|
-
});
|
|
77
|
-
res.json(result);
|
|
78
|
-
} catch (error) {
|
|
79
|
-
sendFriendError(res, error);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
}
|
|
@@ -1,557 +0,0 @@
|
|
|
1
|
-
import { normalizeAgentProfile, resolveAgentDisplayName, resolveAgentVisibility } from '../../lib/agent-profile.js';
|
|
2
|
-
import { createRelayPolicyHooks, evaluatePolicyHook } from '../../lib/policy.js';
|
|
3
|
-
import { resolvePublicIdentity } from '../../lib/public-identity.js';
|
|
4
|
-
|
|
5
|
-
function normalizeAgentId(agentId) {
|
|
6
|
-
const normalized = String(agentId || '').trim();
|
|
7
|
-
return normalized || null;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function normalizeDirection(direction) {
|
|
11
|
-
const normalized = String(direction || '').trim().toLowerCase();
|
|
12
|
-
return normalized === 'inbound' || normalized === 'outbound' ? normalized : null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function normalizeStatus(status) {
|
|
16
|
-
const normalized = String(status || '').trim().toLowerCase();
|
|
17
|
-
return normalized || null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function normalizeMessage(message) {
|
|
21
|
-
if (typeof message !== 'string') return null;
|
|
22
|
-
const normalized = message.trim();
|
|
23
|
-
return normalized || null;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function sortByRecency(items = [], ...fieldNames) {
|
|
27
|
-
return items.slice().sort((left, right) => {
|
|
28
|
-
for (const fieldName of fieldNames) {
|
|
29
|
-
const rightTs = Date.parse(right?.[fieldName] || '');
|
|
30
|
-
const leftTs = Date.parse(left?.[fieldName] || '');
|
|
31
|
-
const normalizedRightTs = Number.isFinite(rightTs) ? rightTs : -Infinity;
|
|
32
|
-
const normalizedLeftTs = Number.isFinite(leftTs) ? leftTs : -Infinity;
|
|
33
|
-
if (normalizedRightTs !== normalizedLeftTs) return normalizedRightTs - normalizedLeftTs;
|
|
34
|
-
}
|
|
35
|
-
return 0;
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function createConfigurationError() {
|
|
40
|
-
const error = new Error('friend_store_unavailable');
|
|
41
|
-
error.code = 'friend_store_unavailable';
|
|
42
|
-
error.status = 500;
|
|
43
|
-
return error;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function createAgentNotFoundError(agentId) {
|
|
47
|
-
const error = new Error(`agent_not_found:${agentId}`);
|
|
48
|
-
error.code = 'agent_not_found';
|
|
49
|
-
error.status = 404;
|
|
50
|
-
return error;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function createTargetNotFoundError(targetAgentId) {
|
|
54
|
-
const error = new Error(`friend_target_not_found:${targetAgentId}`);
|
|
55
|
-
error.code = 'friend_target_not_found';
|
|
56
|
-
error.status = 404;
|
|
57
|
-
return error;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function createFriendRequestTargetOnlyError() {
|
|
61
|
-
const error = new Error('friend_request_target_only');
|
|
62
|
-
error.code = 'friend_request_target_only';
|
|
63
|
-
error.status = 403;
|
|
64
|
-
return error;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function createFriendRequestNotPendingError(friendRequestId, status) {
|
|
68
|
-
const error = new Error(`friend_request_not_pending:${friendRequestId}`);
|
|
69
|
-
error.code = 'friend_request_not_pending';
|
|
70
|
-
error.status = 400;
|
|
71
|
-
error.responseBody = {
|
|
72
|
-
error: error.code,
|
|
73
|
-
message: 'friend request is not pending',
|
|
74
|
-
friendRequestId,
|
|
75
|
-
status,
|
|
76
|
-
};
|
|
77
|
-
return error;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function createFriendRequestNotFoundError(friendRequestId) {
|
|
81
|
-
const error = new Error(`friend_request_not_found:${friendRequestId}`);
|
|
82
|
-
error.code = 'friend_request_not_found';
|
|
83
|
-
error.status = 404;
|
|
84
|
-
return error;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function createFriendshipNotFoundError(actorAgentId, peerAgentId) {
|
|
88
|
-
const error = new Error(`friendship_not_found:${actorAgentId}:${peerAgentId}`);
|
|
89
|
-
error.code = 'friendship_not_found';
|
|
90
|
-
error.status = 404;
|
|
91
|
-
return error;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function createSelfFriendRequestError() {
|
|
95
|
-
const error = new Error('self_friend_request_not_allowed');
|
|
96
|
-
error.code = 'self_friend_request_not_allowed';
|
|
97
|
-
error.status = 400;
|
|
98
|
-
return error;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function createReversePendingRequestError(request) {
|
|
102
|
-
const error = new Error('friend_request_pending_reverse');
|
|
103
|
-
error.code = 'friend_request_pending_reverse';
|
|
104
|
-
error.status = 409;
|
|
105
|
-
error.responseBody = {
|
|
106
|
-
error: error.code,
|
|
107
|
-
message: 'peer already has a pending friend request; accept that request instead of creating a new one',
|
|
108
|
-
pendingRequestId: request.friendRequestId,
|
|
109
|
-
};
|
|
110
|
-
return error;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function createPolicyDecisionError(decision, fallbackCode = 'friend_request_not_allowed') {
|
|
114
|
-
const errorCode = String(decision?.body?.error || fallbackCode).trim() || fallbackCode;
|
|
115
|
-
const error = new Error(errorCode);
|
|
116
|
-
error.code = errorCode;
|
|
117
|
-
error.status = Number.isInteger(decision?.status) ? decision.status : 403;
|
|
118
|
-
error.responseBody = decision?.body && typeof decision.body === 'object'
|
|
119
|
-
? decision.body
|
|
120
|
-
: { error: errorCode };
|
|
121
|
-
return error;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function projectAgent(store, agentId) {
|
|
125
|
-
const agent = store.getAgent(agentId);
|
|
126
|
-
if (!agent) return null;
|
|
127
|
-
const visibility = resolveAgentVisibility(agent);
|
|
128
|
-
return {
|
|
129
|
-
agentId: agent.agentId,
|
|
130
|
-
displayName: resolveAgentDisplayName(agent),
|
|
131
|
-
publicIdentity: resolvePublicIdentity(agent),
|
|
132
|
-
profile: normalizeAgentProfile(agent.profile),
|
|
133
|
-
discoverable: visibility.discoverable,
|
|
134
|
-
contactable: visibility.contactable,
|
|
135
|
-
createdAt: agent.createdAt,
|
|
136
|
-
...store.getPresence(agent.agentId),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function resolvePeerAgentId(friendship, viewerAgentId) {
|
|
141
|
-
if (friendship.agentAId === viewerAgentId) return friendship.agentBId;
|
|
142
|
-
if (friendship.agentBId === viewerAgentId) return friendship.agentAId;
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export function createFriendService({ store = null, policy = null, publicIdentityService = null } = {}) {
|
|
147
|
-
const relayPolicy = createRelayPolicyHooks(policy);
|
|
148
|
-
|
|
149
|
-
function assertStore() {
|
|
150
|
-
if (!store) throw createConfigurationError();
|
|
151
|
-
return store;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function requireAgent(agentId) {
|
|
155
|
-
const normalizedAgentId = normalizeAgentId(agentId);
|
|
156
|
-
if (!normalizedAgentId) throw createAgentNotFoundError(agentId);
|
|
157
|
-
const agent = assertStore().getAgent(normalizedAgentId);
|
|
158
|
-
if (!agent) throw createAgentNotFoundError(normalizedAgentId);
|
|
159
|
-
return agent;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function resolveActiveFriendship(agentAId, agentBId) {
|
|
163
|
-
return sortByRecency(
|
|
164
|
-
assertStore().listFriendships({
|
|
165
|
-
agentId: agentAId,
|
|
166
|
-
peerAgentId: agentBId,
|
|
167
|
-
status: 'active',
|
|
168
|
-
}),
|
|
169
|
-
'updatedAt',
|
|
170
|
-
'acceptedAt',
|
|
171
|
-
'createdAt',
|
|
172
|
-
)[0] || null;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function resolveLatestFriendship(agentAId, agentBId) {
|
|
176
|
-
return sortByRecency(
|
|
177
|
-
assertStore().listFriendships({
|
|
178
|
-
agentId: agentAId,
|
|
179
|
-
peerAgentId: agentBId,
|
|
180
|
-
}),
|
|
181
|
-
'updatedAt',
|
|
182
|
-
'acceptedAt',
|
|
183
|
-
'createdAt',
|
|
184
|
-
)[0] || null;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function resolveFriendshipGrants(friendshipId) {
|
|
188
|
-
return assertStore()
|
|
189
|
-
.listApprovalGrants()
|
|
190
|
-
.filter((grant) => grant.metadata?.friendshipId === friendshipId);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function resolveActiveApprovalGrant(ownerAgentId, peerAgentId) {
|
|
194
|
-
return sortByRecency(
|
|
195
|
-
assertStore().listApprovalGrants({
|
|
196
|
-
ownerAgentId,
|
|
197
|
-
peerAgentId,
|
|
198
|
-
status: 'active',
|
|
199
|
-
}),
|
|
200
|
-
'updatedAt',
|
|
201
|
-
'createdAt',
|
|
202
|
-
)[0] || null;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
function projectFriendRequest(request, viewerAgentId = null) {
|
|
206
|
-
return {
|
|
207
|
-
...request,
|
|
208
|
-
direction:
|
|
209
|
-
viewerAgentId === request.fromAgentId
|
|
210
|
-
? 'outbound'
|
|
211
|
-
: viewerAgentId === request.toAgentId
|
|
212
|
-
? 'inbound'
|
|
213
|
-
: 'related',
|
|
214
|
-
fromAgent: projectAgent(assertStore(), request.fromAgentId),
|
|
215
|
-
toAgent: projectAgent(assertStore(), request.toAgentId),
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function projectFriendship(friendship, viewerAgentId = null) {
|
|
220
|
-
const peerAgentId = viewerAgentId ? resolvePeerAgentId(friendship, viewerAgentId) : null;
|
|
221
|
-
const grants = resolveFriendshipGrants(friendship.friendshipId);
|
|
222
|
-
return {
|
|
223
|
-
...friendship,
|
|
224
|
-
peerAgentId,
|
|
225
|
-
peerAgent: peerAgentId ? projectAgent(assertStore(), peerAgentId) : null,
|
|
226
|
-
participants: [
|
|
227
|
-
projectAgent(assertStore(), friendship.agentAId),
|
|
228
|
-
projectAgent(assertStore(), friendship.agentBId),
|
|
229
|
-
],
|
|
230
|
-
approval: {
|
|
231
|
-
grantIds: grants.map((grant) => grant.grantId),
|
|
232
|
-
},
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
async function ensureFriendshipApprovalGrants(friendship, request) {
|
|
237
|
-
const friendshipStore = assertStore();
|
|
238
|
-
const now = friendshipStore.now();
|
|
239
|
-
const pairSpecs = [
|
|
240
|
-
{
|
|
241
|
-
ownerAgentId: request.fromAgentId,
|
|
242
|
-
peerAgentId: request.toAgentId,
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
ownerAgentId: request.toAgentId,
|
|
246
|
-
peerAgentId: request.fromAgentId,
|
|
247
|
-
},
|
|
248
|
-
];
|
|
249
|
-
const grants = [];
|
|
250
|
-
let existingGrantUpdated = false;
|
|
251
|
-
|
|
252
|
-
for (const pair of pairSpecs) {
|
|
253
|
-
const existingGrant = friendshipStore
|
|
254
|
-
.listApprovalGrants({ ownerAgentId: pair.ownerAgentId, peerAgentId: pair.peerAgentId })
|
|
255
|
-
.find((grant) => grant.metadata?.friendshipId === friendship.friendshipId)
|
|
256
|
-
|| null;
|
|
257
|
-
|
|
258
|
-
if (!existingGrant) {
|
|
259
|
-
grants.push(await friendshipStore.createApprovalGrant({
|
|
260
|
-
requestId: request.friendRequestId,
|
|
261
|
-
ownerAgentId: pair.ownerAgentId,
|
|
262
|
-
peerAgentId: pair.peerAgentId,
|
|
263
|
-
capabilities: {
|
|
264
|
-
directMessage: true,
|
|
265
|
-
friend: true,
|
|
266
|
-
},
|
|
267
|
-
metadata: {
|
|
268
|
-
friendshipId: friendship.friendshipId,
|
|
269
|
-
pairKey: friendship.pairKey,
|
|
270
|
-
},
|
|
271
|
-
source: 'friendship',
|
|
272
|
-
}));
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
existingGrant.requestId = request.friendRequestId;
|
|
277
|
-
existingGrant.status = 'active';
|
|
278
|
-
existingGrant.updatedAt = now;
|
|
279
|
-
existingGrant.acceptedAt = existingGrant.acceptedAt || now;
|
|
280
|
-
existingGrant.revokedAt = null;
|
|
281
|
-
existingGrant.source = 'friendship';
|
|
282
|
-
existingGrant.capabilities = {
|
|
283
|
-
...(existingGrant.capabilities && typeof existingGrant.capabilities === 'object' ? existingGrant.capabilities : {}),
|
|
284
|
-
directMessage: true,
|
|
285
|
-
friend: true,
|
|
286
|
-
};
|
|
287
|
-
existingGrant.metadata = {
|
|
288
|
-
...(existingGrant.metadata && typeof existingGrant.metadata === 'object' ? existingGrant.metadata : {}),
|
|
289
|
-
friendshipId: friendship.friendshipId,
|
|
290
|
-
pairKey: friendship.pairKey,
|
|
291
|
-
};
|
|
292
|
-
existingGrantUpdated = true;
|
|
293
|
-
grants.push(existingGrant);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
if (existingGrantUpdated) {
|
|
297
|
-
await friendshipStore.markApprovalGrantUpdated();
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return grants;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return {
|
|
304
|
-
async createFriendRequest({ fromAgentId, targetAgentId, message = null, metadata = {} } = {}) {
|
|
305
|
-
const friendshipStore = assertStore();
|
|
306
|
-
const fromAgent = requireAgent(fromAgentId);
|
|
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);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const toAgent = friendshipStore.getAgent(normalizedTargetAgentId);
|
|
317
|
-
if (!toAgent) throw createTargetNotFoundError(normalizedTargetAgentId);
|
|
318
|
-
|
|
319
|
-
const canRequest = evaluatePolicyHook({
|
|
320
|
-
hook: relayPolicy.canRequest,
|
|
321
|
-
context: {
|
|
322
|
-
fromAgentId: fromAgent.agentId,
|
|
323
|
-
fromAgent,
|
|
324
|
-
targetAgentId: normalizedTargetAgentId,
|
|
325
|
-
toAgent,
|
|
326
|
-
requestContext: {
|
|
327
|
-
type: 'friend_request',
|
|
328
|
-
message: normalizeMessage(message),
|
|
329
|
-
metadata,
|
|
330
|
-
},
|
|
331
|
-
approvalGrant: resolveActiveApprovalGrant(toAgent.agentId, fromAgent.agentId),
|
|
332
|
-
store: friendshipStore,
|
|
333
|
-
},
|
|
334
|
-
hookName: 'canRequest',
|
|
335
|
-
defaultStatus: 403,
|
|
336
|
-
defaultError: 'friend_request_not_allowed',
|
|
337
|
-
});
|
|
338
|
-
if (!canRequest.allowed) {
|
|
339
|
-
if (canRequest.body?.error === 'self_request_not_allowed') throw createSelfFriendRequestError();
|
|
340
|
-
throw createPolicyDecisionError(canRequest, 'friend_request_not_allowed');
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
const existingFriendship = resolveActiveFriendship(fromAgent.agentId, toAgent.agentId);
|
|
344
|
-
if (existingFriendship) {
|
|
345
|
-
return {
|
|
346
|
-
created: false,
|
|
347
|
-
alreadyFriends: true,
|
|
348
|
-
request: null,
|
|
349
|
-
friendship: projectFriendship(existingFriendship, fromAgent.agentId),
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const pendingReverseRequest = sortByRecency(
|
|
354
|
-
friendshipStore.listFriendRequests({
|
|
355
|
-
fromAgentId: toAgent.agentId,
|
|
356
|
-
toAgentId: fromAgent.agentId,
|
|
357
|
-
status: 'pending',
|
|
358
|
-
}),
|
|
359
|
-
'createdAt',
|
|
360
|
-
)[0] || null;
|
|
361
|
-
if (pendingReverseRequest) throw createReversePendingRequestError(pendingReverseRequest);
|
|
362
|
-
|
|
363
|
-
const pendingSameDirection = sortByRecency(
|
|
364
|
-
friendshipStore.listFriendRequests({
|
|
365
|
-
fromAgentId: fromAgent.agentId,
|
|
366
|
-
toAgentId: toAgent.agentId,
|
|
367
|
-
status: 'pending',
|
|
368
|
-
}),
|
|
369
|
-
'createdAt',
|
|
370
|
-
)[0] || null;
|
|
371
|
-
if (pendingSameDirection) {
|
|
372
|
-
return {
|
|
373
|
-
created: false,
|
|
374
|
-
alreadyFriends: false,
|
|
375
|
-
request: projectFriendRequest(pendingSameDirection, fromAgent.agentId),
|
|
376
|
-
friendship: null,
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const request = await friendshipStore.createFriendRequest({
|
|
381
|
-
fromAgentId: fromAgent.agentId,
|
|
382
|
-
toAgentId: toAgent.agentId,
|
|
383
|
-
message: normalizeMessage(message),
|
|
384
|
-
metadata,
|
|
385
|
-
});
|
|
386
|
-
return {
|
|
387
|
-
created: true,
|
|
388
|
-
alreadyFriends: false,
|
|
389
|
-
request: projectFriendRequest(request, fromAgent.agentId),
|
|
390
|
-
friendship: null,
|
|
391
|
-
};
|
|
392
|
-
},
|
|
393
|
-
|
|
394
|
-
listFriendRequests({ agentId, direction = null, status = null } = {}) {
|
|
395
|
-
const normalizedAgentId = normalizeAgentId(agentId);
|
|
396
|
-
requireAgent(normalizedAgentId);
|
|
397
|
-
const normalizedDirection = normalizeDirection(direction);
|
|
398
|
-
const normalizedStatus = normalizeStatus(status);
|
|
399
|
-
const friendshipStore = assertStore();
|
|
400
|
-
|
|
401
|
-
let items = [];
|
|
402
|
-
if (normalizedDirection === 'outbound') {
|
|
403
|
-
items = friendshipStore.listFriendRequests({ fromAgentId: normalizedAgentId, status: normalizedStatus });
|
|
404
|
-
} else if (normalizedDirection === 'inbound') {
|
|
405
|
-
items = friendshipStore.listFriendRequests({ toAgentId: normalizedAgentId, status: normalizedStatus });
|
|
406
|
-
} else {
|
|
407
|
-
items = friendshipStore
|
|
408
|
-
.listFriendRequests({ status: normalizedStatus })
|
|
409
|
-
.filter((request) => request.fromAgentId === normalizedAgentId || request.toAgentId === normalizedAgentId);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return {
|
|
413
|
-
items: sortByRecency(items, 'respondedAt', 'createdAt').map((request) => projectFriendRequest(request, normalizedAgentId)),
|
|
414
|
-
};
|
|
415
|
-
},
|
|
416
|
-
|
|
417
|
-
async acceptFriendRequest(friendRequestId, { actorAgentId } = {}) {
|
|
418
|
-
const friendshipStore = assertStore();
|
|
419
|
-
const request = friendshipStore.getFriendRequest(friendRequestId);
|
|
420
|
-
if (!request) throw createFriendRequestNotFoundError(friendRequestId);
|
|
421
|
-
requireAgent(actorAgentId);
|
|
422
|
-
if (request.toAgentId !== actorAgentId) throw createFriendRequestTargetOnlyError();
|
|
423
|
-
|
|
424
|
-
const existingAcceptedFriendship = request.friendshipId ? friendshipStore.getFriendship(request.friendshipId) : null;
|
|
425
|
-
if (request.status === 'accepted' && existingAcceptedFriendship) {
|
|
426
|
-
return {
|
|
427
|
-
created: false,
|
|
428
|
-
request: projectFriendRequest(request, actorAgentId),
|
|
429
|
-
friendship: projectFriendship(existingAcceptedFriendship, actorAgentId),
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
if (request.status !== 'pending') {
|
|
434
|
-
throw createFriendRequestNotPendingError(friendRequestId, request.status);
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
const now = friendshipStore.now();
|
|
438
|
-
const latestFriendship = resolveLatestFriendship(request.fromAgentId, request.toAgentId);
|
|
439
|
-
let friendship = resolveActiveFriendship(request.fromAgentId, request.toAgentId);
|
|
440
|
-
let created = false;
|
|
441
|
-
|
|
442
|
-
if (!friendship && latestFriendship) {
|
|
443
|
-
friendship = latestFriendship;
|
|
444
|
-
friendship.requestId = request.friendRequestId;
|
|
445
|
-
friendship.status = 'active';
|
|
446
|
-
friendship.updatedAt = now;
|
|
447
|
-
friendship.acceptedAt = now;
|
|
448
|
-
friendship.removedAt = null;
|
|
449
|
-
friendship.removedByAgentId = null;
|
|
450
|
-
friendship.source = 'friend_request';
|
|
451
|
-
await friendshipStore.markFriendshipUpdated();
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
if (!friendship) {
|
|
455
|
-
friendship = await friendshipStore.createFriendship({
|
|
456
|
-
requestId: request.friendRequestId,
|
|
457
|
-
agentAId: request.fromAgentId,
|
|
458
|
-
agentBId: request.toAgentId,
|
|
459
|
-
metadata: {
|
|
460
|
-
requestedByAgentId: request.fromAgentId,
|
|
461
|
-
},
|
|
462
|
-
source: 'friend_request',
|
|
463
|
-
});
|
|
464
|
-
created = true;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const grants = await ensureFriendshipApprovalGrants(friendship, request);
|
|
468
|
-
friendship.requestId = request.friendRequestId;
|
|
469
|
-
friendship.updatedAt = now;
|
|
470
|
-
friendship.acceptedAt = friendship.acceptedAt || now;
|
|
471
|
-
friendship.approvalGrantIds = grants.map((grant) => grant.grantId);
|
|
472
|
-
if (!created) {
|
|
473
|
-
await friendshipStore.markFriendshipUpdated();
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
request.status = 'accepted';
|
|
477
|
-
request.respondedAt = now;
|
|
478
|
-
request.acceptedByAgentId = actorAgentId;
|
|
479
|
-
request.rejectedByAgentId = null;
|
|
480
|
-
request.friendshipId = friendship.friendshipId;
|
|
481
|
-
request.approvalGrantIds = grants.map((grant) => grant.grantId);
|
|
482
|
-
await friendshipStore.markFriendRequestUpdated();
|
|
483
|
-
|
|
484
|
-
return {
|
|
485
|
-
created,
|
|
486
|
-
request: projectFriendRequest(request, actorAgentId),
|
|
487
|
-
friendship: projectFriendship(friendship, actorAgentId),
|
|
488
|
-
};
|
|
489
|
-
},
|
|
490
|
-
|
|
491
|
-
async rejectFriendRequest(friendRequestId, { actorAgentId } = {}) {
|
|
492
|
-
const friendshipStore = assertStore();
|
|
493
|
-
const request = friendshipStore.getFriendRequest(friendRequestId);
|
|
494
|
-
if (!request) throw createFriendRequestNotFoundError(friendRequestId);
|
|
495
|
-
requireAgent(actorAgentId);
|
|
496
|
-
if (request.toAgentId !== actorAgentId) throw createFriendRequestTargetOnlyError();
|
|
497
|
-
if (request.status !== 'pending') {
|
|
498
|
-
throw createFriendRequestNotPendingError(friendRequestId, request.status);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
request.status = 'rejected';
|
|
502
|
-
request.respondedAt = friendshipStore.now();
|
|
503
|
-
request.rejectedByAgentId = actorAgentId;
|
|
504
|
-
request.acceptedByAgentId = null;
|
|
505
|
-
await friendshipStore.markFriendRequestUpdated();
|
|
506
|
-
|
|
507
|
-
return {
|
|
508
|
-
request: projectFriendRequest(request, actorAgentId),
|
|
509
|
-
};
|
|
510
|
-
},
|
|
511
|
-
|
|
512
|
-
listFriends({ agentId } = {}) {
|
|
513
|
-
const normalizedAgentId = normalizeAgentId(agentId);
|
|
514
|
-
requireAgent(normalizedAgentId);
|
|
515
|
-
const friendshipStore = assertStore();
|
|
516
|
-
return {
|
|
517
|
-
items: sortByRecency(
|
|
518
|
-
friendshipStore.listFriendships({ agentId: normalizedAgentId, status: 'active' }),
|
|
519
|
-
'updatedAt',
|
|
520
|
-
'acceptedAt',
|
|
521
|
-
'createdAt',
|
|
522
|
-
).map((friendship) => projectFriendship(friendship, normalizedAgentId)),
|
|
523
|
-
};
|
|
524
|
-
},
|
|
525
|
-
|
|
526
|
-
async removeFriend({ actorAgentId, peerAgentId } = {}) {
|
|
527
|
-
const friendshipStore = assertStore();
|
|
528
|
-
requireAgent(actorAgentId);
|
|
529
|
-
requireAgent(peerAgentId);
|
|
530
|
-
const friendship = resolveActiveFriendship(actorAgentId, peerAgentId);
|
|
531
|
-
if (!friendship) throw createFriendshipNotFoundError(actorAgentId, peerAgentId);
|
|
532
|
-
|
|
533
|
-
const now = friendshipStore.now();
|
|
534
|
-
friendship.status = 'removed';
|
|
535
|
-
friendship.updatedAt = now;
|
|
536
|
-
friendship.removedAt = now;
|
|
537
|
-
friendship.removedByAgentId = actorAgentId;
|
|
538
|
-
await friendshipStore.markFriendshipUpdated();
|
|
539
|
-
|
|
540
|
-
let grantsUpdated = false;
|
|
541
|
-
for (const grant of resolveFriendshipGrants(friendship.friendshipId)) {
|
|
542
|
-
if (grant.status === 'revoked') continue;
|
|
543
|
-
grant.status = 'revoked';
|
|
544
|
-
grant.revokedAt = now;
|
|
545
|
-
grant.updatedAt = now;
|
|
546
|
-
grantsUpdated = true;
|
|
547
|
-
}
|
|
548
|
-
if (grantsUpdated) {
|
|
549
|
-
await friendshipStore.markApprovalGrantUpdated();
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return {
|
|
553
|
-
friendship: projectFriendship(friendship, actorAgentId),
|
|
554
|
-
};
|
|
555
|
-
},
|
|
556
|
-
};
|
|
557
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
function sendSocialError(res, error) {
|
|
2
|
-
const status = Number.isInteger(error?.status) ? error.status : 500;
|
|
3
|
-
if (error?.responseBody && typeof error.responseBody === 'object') {
|
|
4
|
-
return res.status(status).json(error.responseBody);
|
|
5
|
-
}
|
|
6
|
-
const code = typeof error?.code === 'string' ? error.code : 'internal_error';
|
|
7
|
-
return res.status(status).json({ error: code, message: error?.message || code });
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function registerSocialRoutes(app, { socialService }) {
|
|
11
|
-
app.get('/v1/social/agents/lookup', (req, res) => {
|
|
12
|
-
try {
|
|
13
|
-
const result = socialService.lookupAgentByIdentity({
|
|
14
|
-
identity: req.query.identity,
|
|
15
|
-
});
|
|
16
|
-
res.json(result);
|
|
17
|
-
} catch (error) {
|
|
18
|
-
sendSocialError(res, error);
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
}
|