@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.
Files changed (57) hide show
  1. package/README.md +4 -4
  2. package/index.js +0 -1
  3. package/openclaw.plugin.json +1 -1
  4. package/package.json +1 -1
  5. package/skills/claworld-help/SKILL.md +19 -27
  6. package/skills/claworld-join-and-chat/SKILL.md +9 -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 +8 -253
  10. package/src/openclaw/plugin/managed-config.js +1 -7
  11. package/src/openclaw/plugin/onboarding.js +1 -1
  12. package/src/openclaw/plugin/register.js +183 -232
  13. package/src/openclaw/plugin/relay-client.js +8 -5
  14. package/src/openclaw/runtime/product-shell-helper.js +11 -364
  15. package/src/openclaw/runtime/tool-contracts.js +0 -182
  16. package/src/openclaw/runtime/tool-inventory.js +4 -27
  17. package/src/lib/agent-profile.js +0 -74
  18. package/src/lib/http-auth.js +0 -151
  19. package/src/lib/policy.js +0 -114
  20. package/src/openclaw/installer/constants.js +0 -14
  21. package/src/product-shell/agent-cards/card-routes.js +0 -64
  22. package/src/product-shell/agent-cards/card-service.js +0 -287
  23. package/src/product-shell/agent-cards/spec-builder.js +0 -167
  24. package/src/product-shell/agent-cards/storage/image-host-storage.js +0 -192
  25. package/src/product-shell/agent-cards/storage/local-public-storage.js +0 -74
  26. package/src/product-shell/agent-cards/svg-renderer.js +0 -325
  27. package/src/product-shell/agent-cards/template-registry.js +0 -131
  28. package/src/product-shell/catalog/default-world-catalog.js +0 -38
  29. package/src/product-shell/contracts/candidate-feed.js +0 -393
  30. package/src/product-shell/contracts/world-manifest.js +0 -369
  31. package/src/product-shell/conversation-feedback/conversation-feedback-service.js +0 -261
  32. package/src/product-shell/feedback/feedback-contract.js +0 -13
  33. package/src/product-shell/feedback/feedback-routes.js +0 -98
  34. package/src/product-shell/feedback/feedback-service.js +0 -252
  35. package/src/product-shell/index.js +0 -212
  36. package/src/product-shell/matching/matchmaking-service.js +0 -395
  37. package/src/product-shell/membership/membership-service.js +0 -284
  38. package/src/product-shell/onboarding/onboarding-routes.js +0 -37
  39. package/src/product-shell/onboarding/onboarding-service.js +0 -222
  40. package/src/product-shell/orchestration/world-conversation-orchestrator.js +0 -28
  41. package/src/product-shell/profile/profile-service.js +0 -142
  42. package/src/product-shell/profile/public-identity-routes.js +0 -160
  43. package/src/product-shell/profile/public-identity-service.js +0 -192
  44. package/src/product-shell/search/search-service.js +0 -393
  45. package/src/product-shell/social/chat-request-approval-policy.js +0 -332
  46. package/src/product-shell/social/chat-request-routes.js +0 -130
  47. package/src/product-shell/social/chat-request-service.js +0 -723
  48. package/src/product-shell/social/friend-routes.js +0 -82
  49. package/src/product-shell/social/friend-service.js +0 -557
  50. package/src/product-shell/social/social-routes.js +0 -21
  51. package/src/product-shell/social/social-service.js +0 -136
  52. package/src/product-shell/worlds/world-admin-service.js +0 -486
  53. package/src/product-shell/worlds/world-authorization.js +0 -136
  54. package/src/product-shell/worlds/world-broadcast-service.js +0 -296
  55. package/src/product-shell/worlds/world-routes.js +0 -403
  56. package/src/product-shell/worlds/world-service.js +0 -89
  57. 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
- }