@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
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { normalizeAgentProfile, resolveAgentDisplayName, resolveAgentVisibility } from '../../lib/agent-profile.js';
|
|
2
|
+
import { normalizeChatRequestInput } from '../../lib/chat-request.js';
|
|
3
|
+
import { resolvePublicIdentity } from '../../lib/public-identity.js';
|
|
2
4
|
import { createKickoffBrief, resolveStoredKickoffBrief } from '../../lib/relay/kickoff-text.js';
|
|
3
5
|
import { WORLD_ACTIONS } from '../worlds/world-authorization.js';
|
|
4
6
|
import { normalizeChatRequestApprovalPolicy } from '../contracts/chat-request-approval-policy.js';
|
|
@@ -66,7 +68,7 @@ function createAgentNotFoundError(agentId) {
|
|
|
66
68
|
return error;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
function
|
|
71
|
+
function createTargetAgentNotFoundError(targetAgentId) {
|
|
70
72
|
const error = new Error(`chat_request_target_not_found:${targetAgentId}`);
|
|
71
73
|
error.code = 'chat_request_target_not_found';
|
|
72
74
|
error.status = 404;
|
|
@@ -123,10 +125,8 @@ function projectAgent(store, agentId, presence = null) {
|
|
|
123
125
|
const presenceState = presence?.getPresence?.(agent.agentId) || store.getPresence(agent.agentId);
|
|
124
126
|
return {
|
|
125
127
|
agentId: agent.agentId,
|
|
126
|
-
agentCode: agent.agentCode,
|
|
127
|
-
domain: agent.domain,
|
|
128
|
-
address: agent.address,
|
|
129
128
|
displayName: resolveAgentDisplayName(agent),
|
|
129
|
+
publicIdentity: resolvePublicIdentity(agent),
|
|
130
130
|
profile: normalizeAgentProfile(agent.profile),
|
|
131
131
|
discoverable: visibility.discoverable,
|
|
132
132
|
contactable: visibility.contactable,
|
|
@@ -281,12 +281,100 @@ function projectChatRequestOrigin(request = {}) {
|
|
|
281
281
|
};
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
function hasConversationScope(conversation = null) {
|
|
285
|
+
return Boolean(conversation && typeof conversation === 'object' && !Array.isArray(conversation) && Object.keys(conversation).length > 0);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function resolveConversationLinkRequest(requests = [], conversation = {}) {
|
|
289
|
+
const conversationMeta = conversation?.meta && typeof conversation.meta === 'object' && !Array.isArray(conversation.meta)
|
|
290
|
+
? conversation.meta
|
|
291
|
+
: {};
|
|
292
|
+
const approvalRequestId = normalizeText(conversationMeta.approvalRequestId, null);
|
|
293
|
+
if (approvalRequestId) {
|
|
294
|
+
return requests.find((request) => normalizeText(request.chatRequestId || request.requestId, null) === approvalRequestId) || null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const conversationKey = normalizeText(conversation?.conversationKey, null);
|
|
298
|
+
if (!conversationKey) return null;
|
|
299
|
+
|
|
300
|
+
return requests.find((request) => {
|
|
301
|
+
const kickoff = request?.kickoff && typeof request.kickoff === 'object' && !Array.isArray(request.kickoff)
|
|
302
|
+
? request.kickoff
|
|
303
|
+
: {};
|
|
304
|
+
return normalizeText(kickoff.conversationKey, null) === conversationKey
|
|
305
|
+
|| normalizeText(request?.conversation?.conversationKey, null) === conversationKey;
|
|
306
|
+
}) || null;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function resolveInboxChatDirection(viewerAgentId, request = null) {
|
|
310
|
+
if (!request) return null;
|
|
311
|
+
if (viewerAgentId === request.fromAgentId) return 'outbound';
|
|
312
|
+
if (viewerAgentId === request.toAgentId) return 'inbound';
|
|
313
|
+
return 'related';
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function resolveInboxChatCounterpartyAgentId(viewerAgentId, request = null, conversation = null) {
|
|
317
|
+
if (request) {
|
|
318
|
+
if (viewerAgentId === request.fromAgentId) return request.toAgentId;
|
|
319
|
+
if (viewerAgentId === request.toAgentId) return request.fromAgentId;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const participantIds = Array.isArray(conversation?.participantIds) ? conversation.participantIds : [];
|
|
323
|
+
return participantIds.find((participantId) => participantId && participantId !== viewerAgentId) || null;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function resolveInboxChatStatus(conversation = {}, request = null) {
|
|
327
|
+
const kickoffStatus = normalizeText(request?.kickoff?.status, null);
|
|
328
|
+
if (kickoffStatus === 'sent') return 'opening';
|
|
329
|
+
if (kickoffStatus === 'kept_silent') return 'silent';
|
|
330
|
+
if (kickoffStatus === 'failed') return 'kickoff_failed';
|
|
331
|
+
const conversationStatus = normalizeText(conversation?.status, 'active');
|
|
332
|
+
if (conversationStatus === 'active') return 'active';
|
|
333
|
+
if (conversationStatus === 'closed') return 'ended';
|
|
334
|
+
return conversationStatus;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function resolveInboxConversationWorldSummary(worldService, request = {}, conversation = {}) {
|
|
338
|
+
const requestContext = request?.requestContext && typeof request.requestContext === 'object' && !Array.isArray(request.requestContext)
|
|
339
|
+
? request.requestContext
|
|
340
|
+
: {};
|
|
341
|
+
const requestConversation = request?.conversation && typeof request.conversation === 'object' && !Array.isArray(request.conversation)
|
|
342
|
+
? request.conversation
|
|
343
|
+
: {};
|
|
344
|
+
const conversationMeta = conversation?.meta && typeof conversation.meta === 'object' && !Array.isArray(conversation.meta)
|
|
345
|
+
? conversation.meta
|
|
346
|
+
: {};
|
|
347
|
+
const worldMeta = conversationMeta.world && typeof conversationMeta.world === 'object' && !Array.isArray(conversationMeta.world)
|
|
348
|
+
? conversationMeta.world
|
|
349
|
+
: {};
|
|
350
|
+
const worldId = normalizeConversationWorldId(requestConversation.worldId)
|
|
351
|
+
|| normalizeConversationWorldId(requestContext.conversation?.worldId)
|
|
352
|
+
|| normalizeConversationWorldId(worldMeta.worldId)
|
|
353
|
+
|| normalizeConversationWorldId(conversationMeta.worldId);
|
|
354
|
+
|
|
355
|
+
if (!worldId) {
|
|
356
|
+
return {
|
|
357
|
+
mode: 'direct',
|
|
358
|
+
worldId: null,
|
|
359
|
+
world: null,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
mode: 'world',
|
|
365
|
+
worldId,
|
|
366
|
+
world: projectWorldSummary(worldService, worldId),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
284
370
|
export function createChatRequestService({
|
|
285
371
|
store = null,
|
|
286
372
|
relay = null,
|
|
287
373
|
presence = null,
|
|
288
374
|
worldService = null,
|
|
289
375
|
worldAuthorizationService = null,
|
|
376
|
+
publicIdentityService = null,
|
|
377
|
+
conversationFeedbackService = null,
|
|
290
378
|
} = {}) {
|
|
291
379
|
function assertStore() {
|
|
292
380
|
if (!store) throw createConfigurationError('chat_request_store_unavailable');
|
|
@@ -350,8 +438,37 @@ export function createChatRequestService({
|
|
|
350
438
|
};
|
|
351
439
|
}
|
|
352
440
|
|
|
441
|
+
function projectChatInboxChat(conversation = {}, viewerAgentId = null, request = null) {
|
|
442
|
+
const direction = resolveInboxChatDirection(viewerAgentId, request);
|
|
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
|
+
};
|
|
453
|
+
|
|
454
|
+
return {
|
|
455
|
+
chatRequestId: normalizeText(request?.chatRequestId || request?.requestId, null),
|
|
456
|
+
status: resolveInboxChatStatus(conversation, request),
|
|
457
|
+
...(direction ? { direction } : {}),
|
|
458
|
+
createdAt: normalizeText(conversation.createdAt, normalizeText(request?.createdAt, null)),
|
|
459
|
+
updatedAt: normalizeText(conversation.updatedAt, null),
|
|
460
|
+
lastTurnAt: normalizeText(conversation.lastTurnAt, null),
|
|
461
|
+
conversationKey: normalizeText(conversation.conversationKey, null),
|
|
462
|
+
localSessionKey: normalizeText(conversation.sessionKey, null),
|
|
463
|
+
counterparty: projectAgent(assertStore(), counterpartyAgentId, presence),
|
|
464
|
+
conversation: resolveInboxConversationWorldSummary(worldService, request, conversation),
|
|
465
|
+
feedbackSummary,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
353
469
|
return {
|
|
354
470
|
projectChatRequest,
|
|
471
|
+
projectChatInboxChat,
|
|
355
472
|
async syncApprovalPolicy({
|
|
356
473
|
actorAgentId,
|
|
357
474
|
credentialId = null,
|
|
@@ -389,39 +506,51 @@ export function createChatRequestService({
|
|
|
389
506
|
openingMessage = null,
|
|
390
507
|
openingPayload = null,
|
|
391
508
|
worldId = null,
|
|
509
|
+
requestContext = null,
|
|
392
510
|
origin = null,
|
|
393
511
|
broadcast = null,
|
|
394
512
|
source = 'chat_request',
|
|
395
513
|
} = {}) {
|
|
396
514
|
requireAgent(fromAgentId);
|
|
515
|
+
publicIdentityService?.assertPublicIdentityReady?.({
|
|
516
|
+
agentId: fromAgentId,
|
|
517
|
+
capability: 'request chat',
|
|
518
|
+
});
|
|
397
519
|
const normalizedTargetAgentId = normalizeText(targetAgentId, null);
|
|
398
520
|
if (!normalizedTargetAgentId) {
|
|
399
521
|
throw createInvalidChatRequestError('chat_request_target_required', 'chat request target requires targetAgentId');
|
|
400
522
|
}
|
|
401
523
|
const targetAgent = requireAgent(normalizedTargetAgentId);
|
|
402
|
-
const
|
|
403
|
-
|
|
404
|
-
|
|
524
|
+
const normalizedSource = normalizeText(source, 'chat_request');
|
|
525
|
+
const normalizedRequestInput = normalizeChatRequestInput({
|
|
526
|
+
requestContext,
|
|
527
|
+
source: normalizedSource,
|
|
528
|
+
});
|
|
529
|
+
const normalizedWorldId = normalizeConversationWorldId(worldId)
|
|
530
|
+
|| normalizeConversationWorldId(normalizedRequestInput.conversation?.worldId);
|
|
531
|
+
if (!targetAgent?.agentId) {
|
|
532
|
+
throw createTargetAgentNotFoundError(normalizedTargetAgentId);
|
|
405
533
|
}
|
|
406
|
-
const normalizedOpeningPayload = cloneJsonObject(openingPayload);
|
|
534
|
+
const normalizedOpeningPayload = cloneJsonObject(openingPayload) || cloneJsonObject(normalizedRequestInput.openingPayload);
|
|
407
535
|
const normalizedKickoffBrief = createKickoffBrief({
|
|
408
536
|
text: typeof kickoffBrief === 'object' && kickoffBrief !== null && !Array.isArray(kickoffBrief)
|
|
409
537
|
? kickoffBrief.text
|
|
410
|
-
: openingMessage,
|
|
538
|
+
: normalizeText(openingMessage, normalizeText(normalizedRequestInput.openingMessage, null)),
|
|
411
539
|
payload: typeof kickoffBrief === 'object' && kickoffBrief !== null && !Array.isArray(kickoffBrief)
|
|
412
540
|
? kickoffBrief.payload
|
|
413
541
|
: normalizedOpeningPayload,
|
|
414
542
|
source: typeof kickoffBrief === 'object' && kickoffBrief !== null && !Array.isArray(kickoffBrief)
|
|
415
543
|
? kickoffBrief.source
|
|
416
|
-
:
|
|
544
|
+
: normalizedSource === 'world_broadcast'
|
|
417
545
|
? 'world_broadcast_brief'
|
|
418
546
|
: 'chat_request_brief',
|
|
419
547
|
});
|
|
420
|
-
const normalizedOrigin = normalizeChatRequestOrigin(origin);
|
|
421
|
-
const normalizedBroadcast = normalizeChatRequestBroadcastMetadata(broadcast)
|
|
548
|
+
const normalizedOrigin = normalizeChatRequestOrigin(origin) || normalizeChatRequestOrigin(normalizedRequestInput.origin);
|
|
549
|
+
const normalizedBroadcast = normalizeChatRequestBroadcastMetadata(broadcast)
|
|
550
|
+
|| normalizeChatRequestBroadcastMetadata(normalizedRequestInput.broadcast);
|
|
422
551
|
if (normalizedWorldId) {
|
|
423
552
|
worldService?.requireWorld?.(normalizedWorldId);
|
|
424
|
-
if (
|
|
553
|
+
if (normalizedSource !== 'world_broadcast') {
|
|
425
554
|
const authorization = worldAuthorizationService.evaluateWorldAction({
|
|
426
555
|
worldId: normalizedWorldId,
|
|
427
556
|
actorAgentId: fromAgentId,
|
|
@@ -435,7 +564,7 @@ export function createChatRequestService({
|
|
|
435
564
|
|
|
436
565
|
const result = await assertRelay('createChatRequest').createChatRequest({
|
|
437
566
|
fromAgentId,
|
|
438
|
-
|
|
567
|
+
targetAgentId: targetAgent.agentId,
|
|
439
568
|
kickoffBrief: normalizedKickoffBrief,
|
|
440
569
|
openingMessage: normalizeText(
|
|
441
570
|
normalizedKickoffBrief?.text,
|
|
@@ -445,9 +574,10 @@ export function createChatRequestService({
|
|
|
445
574
|
conversation: {
|
|
446
575
|
...(normalizedWorldId ? { worldId: normalizedWorldId } : {}),
|
|
447
576
|
},
|
|
577
|
+
requestContext: cloneJsonObject(requestContext),
|
|
448
578
|
origin: normalizedOrigin,
|
|
449
579
|
broadcast: normalizedBroadcast,
|
|
450
|
-
source:
|
|
580
|
+
source: normalizedSource,
|
|
451
581
|
});
|
|
452
582
|
if (result.status < 200 || result.status >= 300) {
|
|
453
583
|
throw createRelayResponseError(result, 'chat_request_create_failed');
|
|
@@ -495,23 +625,55 @@ export function createChatRequestService({
|
|
|
495
625
|
};
|
|
496
626
|
},
|
|
497
627
|
|
|
498
|
-
|
|
628
|
+
listChatInbox({ agentId, direction = null } = {}) {
|
|
499
629
|
requireAgent(agentId);
|
|
500
630
|
const normalizedDirection = normalizeDirection(direction);
|
|
501
|
-
let
|
|
502
|
-
|
|
503
|
-
items = items.filter((request) => request.fromAgentId === agentId || request.toAgentId === agentId);
|
|
631
|
+
let requests = assertStore().listChatRequests ? assertStore().listChatRequests() : [];
|
|
632
|
+
requests = requests.filter((request) => request.fromAgentId === agentId || request.toAgentId === agentId);
|
|
504
633
|
if (normalizedDirection === 'inbound') {
|
|
505
|
-
|
|
634
|
+
requests = requests.filter((request) => request.toAgentId === agentId);
|
|
506
635
|
} else if (normalizedDirection === 'outbound') {
|
|
507
|
-
|
|
636
|
+
requests = requests.filter((request) => request.fromAgentId === agentId);
|
|
508
637
|
}
|
|
509
638
|
|
|
639
|
+
const pendingRequests = sortByRecency(
|
|
640
|
+
requests.filter((request) => request.status === 'pending'),
|
|
641
|
+
'createdAt',
|
|
642
|
+
).map((request) => projectChatRequest(request, agentId));
|
|
643
|
+
|
|
644
|
+
const conversations = assertStore().listConversations
|
|
645
|
+
? assertStore().listConversations({ participantId: agentId })
|
|
646
|
+
: [];
|
|
647
|
+
const chats = sortByRecency(
|
|
648
|
+
conversations
|
|
649
|
+
.map((conversation) => {
|
|
650
|
+
const linkedRequest = resolveConversationLinkRequest(requests, conversation);
|
|
651
|
+
if (!linkedRequest && !normalizeText(conversation?.meta?.approvalRequestId, null)) return null;
|
|
652
|
+
const projected = projectChatInboxChat(conversation, agentId, linkedRequest);
|
|
653
|
+
if (!projected) return null;
|
|
654
|
+
if (normalizedDirection && projected.direction !== normalizedDirection) return null;
|
|
655
|
+
return projected;
|
|
656
|
+
})
|
|
657
|
+
.filter(Boolean),
|
|
658
|
+
'lastTurnAt',
|
|
659
|
+
'updatedAt',
|
|
660
|
+
'createdAt',
|
|
661
|
+
);
|
|
662
|
+
|
|
510
663
|
return {
|
|
511
|
-
|
|
664
|
+
counts: {
|
|
665
|
+
pendingRequestCount: pendingRequests.length,
|
|
666
|
+
chatCount: chats.length,
|
|
667
|
+
},
|
|
668
|
+
pendingRequests,
|
|
669
|
+
chats,
|
|
512
670
|
};
|
|
513
671
|
},
|
|
514
672
|
|
|
673
|
+
listChatRequests({ agentId, direction = null } = {}) {
|
|
674
|
+
return this.listChatInbox({ agentId, direction });
|
|
675
|
+
},
|
|
676
|
+
|
|
515
677
|
async acceptChatRequest(chatRequestId, { actorAgentId } = {}) {
|
|
516
678
|
requireAgent(actorAgentId);
|
|
517
679
|
const normalizedChatRequestId = normalizeText(chatRequestId, null);
|
|
@@ -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) {
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
resolveAgentDisplayName,
|
|
4
4
|
resolveAgentVisibility,
|
|
5
5
|
} from '../../lib/agent-profile.js';
|
|
6
|
-
import {
|
|
6
|
+
import { parsePublicIdentityDisplay, resolvePublicIdentity } from '../../lib/public-identity.js';
|
|
7
7
|
|
|
8
8
|
function normalizeText(value, fallback = null) {
|
|
9
9
|
if (value == null) return fallback;
|
|
@@ -11,18 +11,6 @@ function normalizeText(value, fallback = null) {
|
|
|
11
11
|
return normalized || fallback;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
function parseCanonicalAgentCode(value) {
|
|
15
|
-
const parsed = parseAgentHandle(value);
|
|
16
|
-
if (!parsed?.canonical) return null;
|
|
17
|
-
|
|
18
|
-
return {
|
|
19
|
-
agentCode: parsed.address,
|
|
20
|
-
relayLocalCode: parsed.localPart,
|
|
21
|
-
domain: parsed.domain,
|
|
22
|
-
address: parsed.address,
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
14
|
function createConfigurationError() {
|
|
27
15
|
const error = new Error('social_lookup_store_unavailable');
|
|
28
16
|
error.code = 'social_lookup_store_unavailable';
|
|
@@ -30,44 +18,44 @@ function createConfigurationError() {
|
|
|
30
18
|
return error;
|
|
31
19
|
}
|
|
32
20
|
|
|
33
|
-
function
|
|
34
|
-
const error = new Error('
|
|
35
|
-
error.code = '
|
|
21
|
+
function createInvalidPublicIdentityError(identity) {
|
|
22
|
+
const error = new Error('invalid_public_identity');
|
|
23
|
+
error.code = 'invalid_public_identity';
|
|
36
24
|
error.status = 400;
|
|
37
25
|
error.responseBody = {
|
|
38
26
|
error: error.code,
|
|
39
|
-
message: '
|
|
40
|
-
|
|
27
|
+
message: 'identity must use displayName#code',
|
|
28
|
+
identity: normalizeText(identity, null),
|
|
41
29
|
fieldErrors: [
|
|
42
30
|
{
|
|
43
|
-
fieldId: '
|
|
44
|
-
message: '
|
|
31
|
+
fieldId: 'identity',
|
|
32
|
+
message: 'identity must use displayName#code',
|
|
45
33
|
},
|
|
46
34
|
],
|
|
47
35
|
};
|
|
48
36
|
return error;
|
|
49
37
|
}
|
|
50
38
|
|
|
51
|
-
function createAgentNotFoundError(
|
|
52
|
-
const error = new Error(`agent_not_found:${
|
|
39
|
+
function createAgentNotFoundError(identity) {
|
|
40
|
+
const error = new Error(`agent_not_found:${identity}`);
|
|
53
41
|
error.code = 'agent_not_found';
|
|
54
42
|
error.status = 404;
|
|
55
43
|
error.responseBody = {
|
|
56
44
|
error: error.code,
|
|
57
|
-
message: 'no agent found for
|
|
58
|
-
|
|
45
|
+
message: 'no agent found for public identity',
|
|
46
|
+
identity,
|
|
59
47
|
};
|
|
60
48
|
return error;
|
|
61
49
|
}
|
|
62
50
|
|
|
63
|
-
function createNotDiscoverableError(
|
|
64
|
-
const error = new Error(`not_discoverable:${
|
|
51
|
+
function createNotDiscoverableError(identity) {
|
|
52
|
+
const error = new Error(`not_discoverable:${identity}`);
|
|
65
53
|
error.code = 'not_discoverable';
|
|
66
54
|
error.status = 403;
|
|
67
55
|
error.responseBody = {
|
|
68
56
|
error: error.code,
|
|
69
57
|
message: 'agent is not discoverable',
|
|
70
|
-
|
|
58
|
+
identity,
|
|
71
59
|
};
|
|
72
60
|
return error;
|
|
73
61
|
}
|
|
@@ -94,21 +82,27 @@ export function createSocialService({ worldService, store = null } = {}) {
|
|
|
94
82
|
}
|
|
95
83
|
|
|
96
84
|
return {
|
|
97
|
-
|
|
85
|
+
lookupAgentByIdentity({ identity } = {}) {
|
|
98
86
|
const socialStore = assertStore();
|
|
99
|
-
const
|
|
100
|
-
if (!
|
|
101
|
-
throw
|
|
87
|
+
const parsedIdentity = parsePublicIdentityDisplay(identity);
|
|
88
|
+
if (!parsedIdentity) {
|
|
89
|
+
throw createInvalidPublicIdentityError(identity);
|
|
102
90
|
}
|
|
103
91
|
|
|
104
|
-
const agent = socialStore
|
|
92
|
+
const agent = socialStore
|
|
93
|
+
.listAgents()
|
|
94
|
+
.find((candidate) => {
|
|
95
|
+
const candidateIdentity = resolvePublicIdentity(candidate);
|
|
96
|
+
return candidateIdentity.status === 'ready'
|
|
97
|
+
&& candidateIdentity.code === parsedIdentity.code;
|
|
98
|
+
}) || null;
|
|
105
99
|
if (!agent) {
|
|
106
|
-
throw createAgentNotFoundError(
|
|
100
|
+
throw createAgentNotFoundError(parsedIdentity.identity);
|
|
107
101
|
}
|
|
108
102
|
|
|
109
103
|
const visibility = resolveAgentVisibility(agent);
|
|
110
104
|
if (!visibility.discoverable) {
|
|
111
|
-
throw createNotDiscoverableError(
|
|
105
|
+
throw createNotDiscoverableError(parsedIdentity.identity);
|
|
112
106
|
}
|
|
113
107
|
|
|
114
108
|
const activeWorlds = socialStore
|
|
@@ -124,8 +118,10 @@ export function createSocialService({ worldService, store = null } = {}) {
|
|
|
124
118
|
return {
|
|
125
119
|
status: 'found',
|
|
126
120
|
publicProfile: {
|
|
127
|
-
|
|
121
|
+
agentId: agent.agentId,
|
|
122
|
+
identity: resolvePublicIdentity(agent).displayIdentity || parsedIdentity.identity,
|
|
128
123
|
displayName: resolveAgentDisplayName(agent),
|
|
124
|
+
code: resolvePublicIdentity(agent).code,
|
|
129
125
|
profile: normalizeAgentProfile(agent.profile),
|
|
130
126
|
discoverable: visibility.discoverable,
|
|
131
127
|
contactable: visibility.contactable,
|