@xfxstudio/claworld 0.2.13 → 0.2.15

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.
Files changed (58) hide show
  1. package/README.md +4 -4
  2. package/index.js +0 -1
  3. package/openclaw.plugin.json +1 -97
  4. package/package.json +1 -1
  5. package/skills/claworld-help/SKILL.md +47 -27
  6. package/skills/claworld-join-and-chat/SKILL.md +13 -9
  7. package/src/openclaw/index.js +0 -3
  8. package/src/openclaw/plugin/account-identity.js +0 -1
  9. package/src/openclaw/plugin/claworld-channel-plugin.js +73 -319
  10. package/src/openclaw/plugin/config-schema.js +1 -55
  11. package/src/openclaw/plugin/managed-config.js +1 -42
  12. package/src/openclaw/plugin/onboarding.js +1 -1
  13. package/src/openclaw/plugin/register.js +302 -233
  14. package/src/openclaw/plugin/relay-client.js +9 -6
  15. package/src/openclaw/runtime/product-shell-helper.js +11 -364
  16. package/src/openclaw/runtime/tool-contracts.js +0 -182
  17. package/src/openclaw/runtime/tool-inventory.js +4 -27
  18. package/src/lib/agent-profile.js +0 -74
  19. package/src/lib/http-auth.js +0 -151
  20. package/src/lib/policy.js +0 -114
  21. package/src/openclaw/installer/constants.js +0 -14
  22. package/src/product-shell/agent-cards/card-routes.js +0 -64
  23. package/src/product-shell/agent-cards/card-service.js +0 -287
  24. package/src/product-shell/agent-cards/spec-builder.js +0 -167
  25. package/src/product-shell/agent-cards/storage/image-host-storage.js +0 -192
  26. package/src/product-shell/agent-cards/storage/local-public-storage.js +0 -74
  27. package/src/product-shell/agent-cards/svg-renderer.js +0 -325
  28. package/src/product-shell/agent-cards/template-registry.js +0 -131
  29. package/src/product-shell/catalog/default-world-catalog.js +0 -38
  30. package/src/product-shell/contracts/candidate-feed.js +0 -393
  31. package/src/product-shell/contracts/world-manifest.js +0 -369
  32. package/src/product-shell/conversation-feedback/conversation-feedback-service.js +0 -261
  33. package/src/product-shell/feedback/feedback-contract.js +0 -13
  34. package/src/product-shell/feedback/feedback-routes.js +0 -98
  35. package/src/product-shell/feedback/feedback-service.js +0 -252
  36. package/src/product-shell/index.js +0 -212
  37. package/src/product-shell/matching/matchmaking-service.js +0 -395
  38. package/src/product-shell/membership/membership-service.js +0 -284
  39. package/src/product-shell/onboarding/onboarding-routes.js +0 -37
  40. package/src/product-shell/onboarding/onboarding-service.js +0 -222
  41. package/src/product-shell/orchestration/world-conversation-orchestrator.js +0 -28
  42. package/src/product-shell/profile/profile-service.js +0 -142
  43. package/src/product-shell/profile/public-identity-routes.js +0 -160
  44. package/src/product-shell/profile/public-identity-service.js +0 -192
  45. package/src/product-shell/search/search-service.js +0 -393
  46. package/src/product-shell/social/chat-request-approval-policy.js +0 -332
  47. package/src/product-shell/social/chat-request-routes.js +0 -130
  48. package/src/product-shell/social/chat-request-service.js +0 -723
  49. package/src/product-shell/social/friend-routes.js +0 -82
  50. package/src/product-shell/social/friend-service.js +0 -557
  51. package/src/product-shell/social/social-routes.js +0 -21
  52. package/src/product-shell/social/social-service.js +0 -136
  53. package/src/product-shell/worlds/world-admin-service.js +0 -486
  54. package/src/product-shell/worlds/world-authorization.js +0 -136
  55. package/src/product-shell/worlds/world-broadcast-service.js +0 -296
  56. package/src/product-shell/worlds/world-routes.js +0 -403
  57. package/src/product-shell/worlds/world-service.js +0 -89
  58. package/src/product-shell/worlds/world-text.js +0 -75
@@ -1,723 +0,0 @@
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';
4
- import { createKickoffBrief, resolveStoredKickoffBrief } from '../../lib/relay/kickoff-text.js';
5
- import { WORLD_ACTIONS } from '../worlds/world-authorization.js';
6
- import { normalizeChatRequestApprovalPolicy } from '../contracts/chat-request-approval-policy.js';
7
- import {
8
- createStoredChatRequestApprovalPolicy,
9
- evaluateChatRequestApprovalPolicy,
10
- } from './chat-request-approval-policy.js';
11
-
12
- function normalizeText(value, fallback = null) {
13
- if (value == null) return fallback;
14
- const normalized = String(value).trim();
15
- return normalized || fallback;
16
- }
17
-
18
- function normalizeDirection(direction) {
19
- const normalized = normalizeText(direction, null);
20
- return normalized === 'inbound' || normalized === 'outbound' ? normalized : null;
21
- }
22
-
23
- function normalizePositiveInteger(value, fallback = null) {
24
- const normalized = Number(value);
25
- if (!Number.isFinite(normalized) || normalized <= 0) return fallback;
26
- return Math.max(1, Math.trunc(normalized));
27
- }
28
-
29
- function cloneJsonObject(value) {
30
- if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
31
- try {
32
- const cloned = JSON.parse(JSON.stringify(value));
33
- if (!cloned || typeof cloned !== 'object' || Array.isArray(cloned)) return null;
34
- return cloned;
35
- } catch {
36
- return null;
37
- }
38
- }
39
-
40
- function normalizeConversationWorldId(value) {
41
- return normalizeText(value, null);
42
- }
43
-
44
- function sortByRecency(items = [], ...fieldNames) {
45
- return items.slice().sort((left, right) => {
46
- for (const fieldName of fieldNames) {
47
- const rightTs = Date.parse(right?.[fieldName] || '');
48
- const leftTs = Date.parse(left?.[fieldName] || '');
49
- const normalizedRightTs = Number.isFinite(rightTs) ? rightTs : -Infinity;
50
- const normalizedLeftTs = Number.isFinite(leftTs) ? leftTs : -Infinity;
51
- if (normalizedRightTs !== normalizedLeftTs) return normalizedRightTs - normalizedLeftTs;
52
- }
53
- return String(right?.requestId || '').localeCompare(String(left?.requestId || ''));
54
- });
55
- }
56
-
57
- function createConfigurationError(code = 'chat_request_service_unavailable') {
58
- const error = new Error(code);
59
- error.code = code;
60
- error.status = 500;
61
- return error;
62
- }
63
-
64
- function createAgentNotFoundError(agentId) {
65
- const error = new Error(`agent_not_found:${agentId}`);
66
- error.code = 'agent_not_found';
67
- error.status = 404;
68
- return error;
69
- }
70
-
71
- function createTargetAgentNotFoundError(targetAgentId) {
72
- const error = new Error(`chat_request_target_not_found:${targetAgentId}`);
73
- error.code = 'chat_request_target_not_found';
74
- error.status = 404;
75
- error.responseBody = {
76
- error: error.code,
77
- message: 'chat request target is not available',
78
- targetAgentId: normalizeText(targetAgentId, null),
79
- };
80
- return error;
81
- }
82
-
83
- function createInvalidChatRequestError(code, message, extra = {}) {
84
- const error = new Error(code);
85
- error.code = code;
86
- error.status = 400;
87
- error.responseBody = {
88
- error: code,
89
- message,
90
- ...extra,
91
- };
92
- return error;
93
- }
94
-
95
- function createWorldChatRequestMembershipNotActiveError(worldId, agentId) {
96
- const error = new Error(`world_chat_request_membership_not_active:${worldId}:${agentId}`);
97
- error.code = 'world_chat_request_membership_not_active';
98
- error.status = 409;
99
- error.responseBody = {
100
- error: error.code,
101
- message: 'agent must have an active world membership before creating a world-scoped chat request',
102
- worldId: normalizeText(worldId, null),
103
- agentId: normalizeText(agentId, null),
104
- requiredMembershipStatus: 'active',
105
- };
106
- return error;
107
- }
108
-
109
- function createRelayResponseError(result = {}, fallbackCode = 'chat_request_failed') {
110
- const code = normalizeText(result?.body?.error, fallbackCode);
111
- const error = new Error(code);
112
- error.code = code;
113
- error.status = Number.isInteger(result?.status) ? result.status : 500;
114
- error.responseBody = result?.body && typeof result.body === 'object'
115
- ? result.body
116
- : { error: code };
117
- return error;
118
- }
119
-
120
- function projectAgent(store, agentId, presence = null) {
121
- if (!store || !agentId) return null;
122
- const agent = store.getAgent(agentId);
123
- if (!agent) return null;
124
- const visibility = resolveAgentVisibility(agent);
125
- const presenceState = presence?.getPresence?.(agent.agentId) || store.getPresence(agent.agentId);
126
- return {
127
- agentId: agent.agentId,
128
- displayName: resolveAgentDisplayName(agent),
129
- publicIdentity: resolvePublicIdentity(agent),
130
- profile: normalizeAgentProfile(agent.profile),
131
- discoverable: visibility.discoverable,
132
- contactable: visibility.contactable,
133
- createdAt: agent.createdAt,
134
- ...presenceState,
135
- };
136
- }
137
-
138
- function projectWorldSummary(worldService, worldId) {
139
- const normalizedWorldId = normalizeText(worldId, null);
140
- if (!normalizedWorldId) return null;
141
- const world = worldService?.getWorld?.(normalizedWorldId) || null;
142
- if (!world) {
143
- return {
144
- worldId: normalizedWorldId,
145
- slug: null,
146
- displayName: null,
147
- summary: null,
148
- };
149
- }
150
- return {
151
- worldId: world.worldId,
152
- slug: world.slug || null,
153
- displayName: world.displayName || null,
154
- summary: world.summary || null,
155
- };
156
- }
157
-
158
- function projectKickoffBrief(brief = null) {
159
- if (!brief || typeof brief !== 'object' || Array.isArray(brief)) return null;
160
- return {
161
- text: normalizeText(brief.text, null),
162
- payload: cloneJsonObject(brief.payload),
163
- source: normalizeText(brief.source, 'chat_request_brief'),
164
- };
165
- }
166
-
167
- function resolveKickoffBrief(request = {}, requestContext = {}) {
168
- const directBrief = request.kickoffBrief && typeof request.kickoffBrief === 'object' && !Array.isArray(request.kickoffBrief)
169
- ? request.kickoffBrief
170
- : null;
171
- return directBrief || resolveStoredKickoffBrief(requestContext);
172
- }
173
-
174
- function resolveOpeningMessage(request = {}, requestContext = {}) {
175
- return normalizeText(
176
- resolveKickoffBrief(request, requestContext)?.text,
177
- normalizeText(
178
- request?.openingMessage,
179
- normalizeText(requestContext.openingPayload?.text, normalizeText(requestContext.message, null)),
180
- ),
181
- );
182
- }
183
-
184
- function projectKickoff(kickoff = {}) {
185
- if (!kickoff || typeof kickoff !== 'object') return null;
186
- return {
187
- status: normalizeText(kickoff.status, 'skipped'),
188
- deliveredAt: normalizeText(kickoff.deliveredAt, null),
189
- senderKickoffDeliveredAt: normalizeText(kickoff.senderKickoffDeliveredAt, normalizeText(kickoff.deliveredAt, null)),
190
- openerAcceptedAt: normalizeText(kickoff.openerAcceptedAt, null),
191
- openerDeliveredAt: normalizeText(kickoff.openerDeliveredAt, null),
192
- liveChatEstablishedAt: normalizeText(kickoff.liveChatEstablishedAt, null),
193
- reason: normalizeText(kickoff.reason, null),
194
- };
195
- }
196
-
197
- function resolveAcceptNextAction(kickoff) {
198
- const kickoffStatus = kickoff?.status || 'skipped';
199
- if (kickoffStatus === 'established') return 'runtime_owns_live_conversation';
200
- if (kickoffStatus === 'sent') return 'wait_for_sender_opener_delivery';
201
- if (kickoffStatus === 'kept_silent') return 'sender_kept_silent';
202
- if (kickoffStatus === 'failed') return 'backend_kickoff_failed';
203
- return 'chat_request_accepted_without_opening_message';
204
- }
205
-
206
- function resolveCreateNextAction({ verdict = 'pending', kickoff = null } = {}) {
207
- if (verdict === 'auto_accept') return resolveAcceptNextAction(kickoff);
208
- if (verdict === 'reject') return 'request_rejected_by_policy';
209
- return 'wait_for_peer_acceptance';
210
- }
211
-
212
- function normalizeChatRequestOrigin(origin = {}) {
213
- if (!origin || typeof origin !== 'object' || Array.isArray(origin)) return null;
214
- const type = normalizeText(origin.type, null);
215
- const broadcastId = normalizeText(origin.broadcastId, null);
216
- if (!type && !broadcastId) return null;
217
- return {
218
- ...(type ? { type } : {}),
219
- ...(broadcastId ? { broadcastId } : {}),
220
- };
221
- }
222
-
223
- function normalizeChatRequestBroadcastMetadata(broadcast = {}) {
224
- if (!broadcast || typeof broadcast !== 'object' || Array.isArray(broadcast)) return null;
225
- const broadcastId = normalizeText(broadcast.broadcastId, null);
226
- if (!broadcastId) return null;
227
- return {
228
- broadcastId,
229
- ...(normalizeText(broadcast.worldId, null) ? { worldId: normalizeText(broadcast.worldId, null) } : {}),
230
- ...(normalizeText(broadcast.audience, null) ? { audience: normalizeText(broadcast.audience, null) } : {}),
231
- ...(normalizeText(broadcast.senderRole, null) ? { senderRole: normalizeText(broadcast.senderRole, null) } : {}),
232
- ...(normalizeText(broadcast.eligibility, null) ? { eligibility: normalizeText(broadcast.eligibility, null) } : {}),
233
- ...(typeof broadcast.excludeSelf === 'boolean' ? { excludeSelf: broadcast.excludeSelf } : {}),
234
- };
235
- }
236
-
237
- function projectStoredApprovalPolicy(record = {}) {
238
- if (!record || typeof record !== 'object' || Array.isArray(record)) return null;
239
- return {
240
- agentId: normalizeText(record.agentId, null),
241
- schemaVersion: Number.isInteger(record.schemaVersion) ? record.schemaVersion : null,
242
- syncedAt: normalizeText(record.syncedAt, null),
243
- credentialId: normalizeText(record.credentialId, null),
244
- source: record.source && typeof record.source === 'object' && !Array.isArray(record.source)
245
- ? {
246
- channel: normalizeText(record.source.channel, null),
247
- integration: normalizeText(record.source.integration, null),
248
- accountId: normalizeText(record.source.accountId, null),
249
- }
250
- : {},
251
- policy: normalizeChatRequestApprovalPolicy(record.policy || {}),
252
- };
253
- }
254
-
255
- function projectChatRequestOrigin(request = {}) {
256
- const directOrigin = request.origin && typeof request.origin === 'object' && !Array.isArray(request.origin)
257
- ? request.origin
258
- : null;
259
- if (directOrigin) {
260
- return {
261
- type: normalizeText(directOrigin.type, normalizeText(request.source, 'chat_request')),
262
- broadcastId: normalizeText(directOrigin.broadcastId, null),
263
- };
264
- }
265
-
266
- const requestContext = request.requestContext && typeof request.requestContext === 'object' && !Array.isArray(request.requestContext)
267
- ? request.requestContext
268
- : {};
269
- const origin = requestContext.origin && typeof requestContext.origin === 'object' && !Array.isArray(requestContext.origin)
270
- ? requestContext.origin
271
- : {};
272
- const broadcast = requestContext.broadcast && typeof requestContext.broadcast === 'object' && !Array.isArray(requestContext.broadcast)
273
- ? requestContext.broadcast
274
- : {};
275
- const broadcastId = normalizeText(origin.broadcastId, normalizeText(broadcast.broadcastId, null));
276
- const source = normalizeText(request.source, null);
277
- const type = normalizeText(origin.type, source === 'world_broadcast' ? 'world_broadcast' : 'chat_request');
278
- return {
279
- type,
280
- broadcastId,
281
- };
282
- }
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
-
370
- export function createChatRequestService({
371
- store = null,
372
- relay = null,
373
- presence = null,
374
- worldService = null,
375
- worldAuthorizationService = null,
376
- publicIdentityService = null,
377
- conversationFeedbackService = null,
378
- } = {}) {
379
- function assertStore() {
380
- if (!store) throw createConfigurationError('chat_request_store_unavailable');
381
- return store;
382
- }
383
-
384
- function assertRelay(methodName) {
385
- if (!relay || typeof relay[methodName] !== 'function') {
386
- throw createConfigurationError('chat_request_relay_unavailable');
387
- }
388
- return relay;
389
- }
390
-
391
- function requireAgent(agentId) {
392
- const normalizedAgentId = normalizeText(agentId, null);
393
- if (!normalizedAgentId) throw createAgentNotFoundError(agentId);
394
- const agent = assertStore().getAgent(normalizedAgentId);
395
- if (!agent) throw createAgentNotFoundError(normalizedAgentId);
396
- return agent;
397
- }
398
-
399
- function projectChatRequest(request = {}, viewerAgentId = null) {
400
- const requestContext = request.requestContext && typeof request.requestContext === 'object' && !Array.isArray(request.requestContext)
401
- ? request.requestContext
402
- : {};
403
- const conversation = request.conversation && typeof request.conversation === 'object' && !Array.isArray(request.conversation)
404
- ? request.conversation
405
- : {};
406
- const worldId = normalizeConversationWorldId(conversation.worldId)
407
- || normalizeConversationWorldId(requestContext.conversation?.worldId);
408
- const counterpartyAgentId =
409
- viewerAgentId === request.fromAgentId
410
- ? request.toAgentId
411
- : viewerAgentId === request.toAgentId
412
- ? request.fromAgentId
413
- : null;
414
-
415
- return {
416
- chatRequestId: normalizeText(request.chatRequestId || request.requestId, null),
417
- status: normalizeText(request.status, 'pending'),
418
- direction:
419
- viewerAgentId === request.fromAgentId
420
- ? 'outbound'
421
- : viewerAgentId === request.toAgentId
422
- ? 'inbound'
423
- : 'related',
424
- openingMessage: resolveOpeningMessage(request, requestContext),
425
- kickoffBrief: projectKickoffBrief(resolveKickoffBrief(request, requestContext)),
426
- createdAt: normalizeText(request.createdAt, null),
427
- respondedAt: normalizeText(request.respondedAt, null),
428
- expiresAt: normalizeText(request.expiresAt, null),
429
- origin: projectChatRequestOrigin(request),
430
- fromAgent: projectAgent(assertStore(), request.fromAgentId, presence),
431
- toAgent: projectAgent(assertStore(), request.toAgentId, presence),
432
- counterparty: projectAgent(assertStore(), counterpartyAgentId, presence),
433
- conversation: {
434
- mode: worldId ? 'world' : 'direct',
435
- worldId,
436
- world: projectWorldSummary(worldService, worldId),
437
- },
438
- };
439
- }
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
-
469
- return {
470
- projectChatRequest,
471
- projectChatInboxChat,
472
- async syncApprovalPolicy({
473
- actorAgentId,
474
- credentialId = null,
475
- accountId = null,
476
- approval = null,
477
- } = {}) {
478
- requireAgent(actorAgentId);
479
- if (!assertStore().upsertChatRequestApprovalPolicy) {
480
- throw createConfigurationError('chat_request_approval_policy_store_unavailable');
481
- }
482
-
483
- const record = await assertStore().upsertChatRequestApprovalPolicy(
484
- createStoredChatRequestApprovalPolicy({
485
- agentId: actorAgentId,
486
- credentialId,
487
- syncedAt: assertStore().now(),
488
- policy: normalizeChatRequestApprovalPolicy(approval || {}),
489
- source: {
490
- channel: 'claworld',
491
- integration: 'openclaw_plugin',
492
- accountId,
493
- },
494
- }),
495
- );
496
-
497
- return {
498
- status: 'synced',
499
- approvalPolicy: projectStoredApprovalPolicy(record),
500
- };
501
- },
502
- async createChatRequest({
503
- fromAgentId,
504
- targetAgentId,
505
- kickoffBrief = null,
506
- openingMessage = null,
507
- openingPayload = null,
508
- worldId = null,
509
- requestContext = null,
510
- origin = null,
511
- broadcast = null,
512
- source = 'chat_request',
513
- } = {}) {
514
- requireAgent(fromAgentId);
515
- publicIdentityService?.assertPublicIdentityReady?.({
516
- agentId: fromAgentId,
517
- capability: 'request chat',
518
- });
519
- const normalizedTargetAgentId = normalizeText(targetAgentId, null);
520
- if (!normalizedTargetAgentId) {
521
- throw createInvalidChatRequestError('chat_request_target_required', 'chat request target requires targetAgentId');
522
- }
523
- const targetAgent = requireAgent(normalizedTargetAgentId);
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);
533
- }
534
- const normalizedOpeningPayload = cloneJsonObject(openingPayload) || cloneJsonObject(normalizedRequestInput.openingPayload);
535
- const normalizedKickoffBrief = createKickoffBrief({
536
- text: typeof kickoffBrief === 'object' && kickoffBrief !== null && !Array.isArray(kickoffBrief)
537
- ? kickoffBrief.text
538
- : normalizeText(openingMessage, normalizeText(normalizedRequestInput.openingMessage, null)),
539
- payload: typeof kickoffBrief === 'object' && kickoffBrief !== null && !Array.isArray(kickoffBrief)
540
- ? kickoffBrief.payload
541
- : normalizedOpeningPayload,
542
- source: typeof kickoffBrief === 'object' && kickoffBrief !== null && !Array.isArray(kickoffBrief)
543
- ? kickoffBrief.source
544
- : normalizedSource === 'world_broadcast'
545
- ? 'world_broadcast_brief'
546
- : 'chat_request_brief',
547
- });
548
- const normalizedOrigin = normalizeChatRequestOrigin(origin) || normalizeChatRequestOrigin(normalizedRequestInput.origin);
549
- const normalizedBroadcast = normalizeChatRequestBroadcastMetadata(broadcast)
550
- || normalizeChatRequestBroadcastMetadata(normalizedRequestInput.broadcast);
551
- if (normalizedWorldId) {
552
- worldService?.requireWorld?.(normalizedWorldId);
553
- if (normalizedSource !== 'world_broadcast') {
554
- const authorization = worldAuthorizationService.evaluateWorldAction({
555
- worldId: normalizedWorldId,
556
- actorAgentId: fromAgentId,
557
- action: WORLD_ACTIONS.CREATE_CHAT_REQUEST,
558
- });
559
- if (!authorization.allowed || authorization.membership?.status !== 'active') {
560
- throw createWorldChatRequestMembershipNotActiveError(normalizedWorldId, fromAgentId);
561
- }
562
- }
563
- }
564
-
565
- const result = await assertRelay('createChatRequest').createChatRequest({
566
- fromAgentId,
567
- targetAgentId: targetAgent.agentId,
568
- kickoffBrief: normalizedKickoffBrief,
569
- openingMessage: normalizeText(
570
- normalizedKickoffBrief?.text,
571
- normalizeText(openingMessage, null),
572
- ),
573
- openingPayload: cloneJsonObject(normalizedKickoffBrief?.payload) || normalizedOpeningPayload,
574
- conversation: {
575
- ...(normalizedWorldId ? { worldId: normalizedWorldId } : {}),
576
- },
577
- requestContext: cloneJsonObject(requestContext),
578
- origin: normalizedOrigin,
579
- broadcast: normalizedBroadcast,
580
- source: normalizedSource,
581
- });
582
- if (result.status < 200 || result.status >= 300) {
583
- throw createRelayResponseError(result, 'chat_request_create_failed');
584
- }
585
-
586
- let storedRequest = result.body || {};
587
- let kickoff = null;
588
- const evaluation = evaluateChatRequestApprovalPolicy({
589
- policyRecord: assertStore().getChatRequestApprovalPolicy?.(storedRequest.toAgentId) || null,
590
- request: storedRequest,
591
- store: assertStore(),
592
- presence,
593
- worldService,
594
- });
595
-
596
- if (evaluation.verdict === 'auto_accept') {
597
- const accepted = await assertRelay('acceptChatRequest').acceptChatRequest(storedRequest.chatRequestId || storedRequest.requestId, {
598
- actorAgentId: storedRequest.toAgentId,
599
- });
600
- if (accepted.status < 200 || accepted.status >= 300) {
601
- throw createRelayResponseError(accepted, 'chat_request_auto_accept_failed');
602
- }
603
- storedRequest = accepted.body?.request || storedRequest;
604
- kickoff = projectKickoff(accepted.body?.kickoff || storedRequest.kickoff);
605
- } else if (evaluation.verdict === 'reject') {
606
- const rejected = await assertRelay('rejectChatRequest').rejectChatRequest(storedRequest.chatRequestId || storedRequest.requestId, {
607
- actorAgentId: storedRequest.toAgentId,
608
- });
609
- if (rejected.status < 200 || rejected.status >= 300) {
610
- throw createRelayResponseError(rejected, 'chat_request_policy_reject_failed');
611
- }
612
- storedRequest = rejected.body || storedRequest;
613
- }
614
-
615
- return {
616
- status: normalizeText(storedRequest.status, 'pending'),
617
- verdict: evaluation.verdict,
618
- reason: evaluation.reason,
619
- chatRequest: projectChatRequest(storedRequest, fromAgentId),
620
- ...(kickoff ? { kickoff } : {}),
621
- nextAction: resolveCreateNextAction({
622
- verdict: evaluation.verdict,
623
- kickoff,
624
- }),
625
- };
626
- },
627
-
628
- listChatInbox({ agentId, direction = null } = {}) {
629
- requireAgent(agentId);
630
- const normalizedDirection = normalizeDirection(direction);
631
- let requests = assertStore().listChatRequests ? assertStore().listChatRequests() : [];
632
- requests = requests.filter((request) => request.fromAgentId === agentId || request.toAgentId === agentId);
633
- if (normalizedDirection === 'inbound') {
634
- requests = requests.filter((request) => request.toAgentId === agentId);
635
- } else if (normalizedDirection === 'outbound') {
636
- requests = requests.filter((request) => request.fromAgentId === agentId);
637
- }
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
-
663
- return {
664
- counts: {
665
- pendingRequestCount: pendingRequests.length,
666
- chatCount: chats.length,
667
- },
668
- pendingRequests,
669
- chats,
670
- };
671
- },
672
-
673
- listChatRequests({ agentId, direction = null } = {}) {
674
- return this.listChatInbox({ agentId, direction });
675
- },
676
-
677
- async acceptChatRequest(chatRequestId, { actorAgentId } = {}) {
678
- requireAgent(actorAgentId);
679
- const normalizedChatRequestId = normalizeText(chatRequestId, null);
680
- if (!normalizedChatRequestId) {
681
- throw createInvalidChatRequestError('chat_request_id_required', 'chatRequestId is required');
682
- }
683
-
684
- const result = await assertRelay('acceptChatRequest').acceptChatRequest(normalizedChatRequestId, {
685
- actorAgentId,
686
- });
687
- if (result.status < 200 || result.status >= 300) {
688
- throw createRelayResponseError(result, 'chat_request_accept_failed');
689
- }
690
-
691
- const kickoff = projectKickoff(result.body?.kickoff || result.body?.request?.kickoff);
692
- return {
693
- status: 'accepted',
694
- verdict: 'manual_accept',
695
- chatRequest: projectChatRequest(result.body?.request || {}, actorAgentId),
696
- kickoff,
697
- nextAction: resolveAcceptNextAction(kickoff),
698
- };
699
- },
700
-
701
- async rejectChatRequest(chatRequestId, { actorAgentId } = {}) {
702
- requireAgent(actorAgentId);
703
- const normalizedChatRequestId = normalizeText(chatRequestId, null);
704
- if (!normalizedChatRequestId) {
705
- throw createInvalidChatRequestError('chat_request_id_required', 'chatRequestId is required');
706
- }
707
-
708
- const result = await assertRelay('rejectChatRequest').rejectChatRequest(normalizedChatRequestId, {
709
- actorAgentId,
710
- });
711
- if (result.status < 200 || result.status >= 300) {
712
- throw createRelayResponseError(result, 'chat_request_reject_failed');
713
- }
714
-
715
- return {
716
- status: 'rejected',
717
- verdict: 'manual_reject',
718
- chatRequest: projectChatRequest(result.body || {}, actorAgentId),
719
- nextAction: 'request_rejected_by_peer',
720
- };
721
- },
722
- };
723
- }