@xfxstudio/claworld 0.2.13 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/index.js +0 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/skills/claworld-help/SKILL.md +19 -27
- package/skills/claworld-join-and-chat/SKILL.md +9 -9
- package/src/openclaw/index.js +0 -3
- package/src/openclaw/plugin/account-identity.js +0 -1
- package/src/openclaw/plugin/claworld-channel-plugin.js +8 -253
- package/src/openclaw/plugin/managed-config.js +1 -7
- package/src/openclaw/plugin/onboarding.js +1 -1
- package/src/openclaw/plugin/register.js +183 -232
- package/src/openclaw/plugin/relay-client.js +8 -5
- package/src/openclaw/runtime/product-shell-helper.js +11 -364
- package/src/openclaw/runtime/tool-contracts.js +0 -182
- package/src/openclaw/runtime/tool-inventory.js +4 -27
- package/src/lib/agent-profile.js +0 -74
- package/src/lib/http-auth.js +0 -151
- package/src/lib/policy.js +0 -114
- package/src/openclaw/installer/constants.js +0 -14
- package/src/product-shell/agent-cards/card-routes.js +0 -64
- package/src/product-shell/agent-cards/card-service.js +0 -287
- package/src/product-shell/agent-cards/spec-builder.js +0 -167
- package/src/product-shell/agent-cards/storage/image-host-storage.js +0 -192
- package/src/product-shell/agent-cards/storage/local-public-storage.js +0 -74
- package/src/product-shell/agent-cards/svg-renderer.js +0 -325
- package/src/product-shell/agent-cards/template-registry.js +0 -131
- package/src/product-shell/catalog/default-world-catalog.js +0 -38
- package/src/product-shell/contracts/candidate-feed.js +0 -393
- package/src/product-shell/contracts/world-manifest.js +0 -369
- package/src/product-shell/conversation-feedback/conversation-feedback-service.js +0 -261
- package/src/product-shell/feedback/feedback-contract.js +0 -13
- package/src/product-shell/feedback/feedback-routes.js +0 -98
- package/src/product-shell/feedback/feedback-service.js +0 -252
- package/src/product-shell/index.js +0 -212
- package/src/product-shell/matching/matchmaking-service.js +0 -395
- package/src/product-shell/membership/membership-service.js +0 -284
- package/src/product-shell/onboarding/onboarding-routes.js +0 -37
- package/src/product-shell/onboarding/onboarding-service.js +0 -222
- package/src/product-shell/orchestration/world-conversation-orchestrator.js +0 -28
- package/src/product-shell/profile/profile-service.js +0 -142
- package/src/product-shell/profile/public-identity-routes.js +0 -160
- package/src/product-shell/profile/public-identity-service.js +0 -192
- package/src/product-shell/search/search-service.js +0 -393
- package/src/product-shell/social/chat-request-approval-policy.js +0 -332
- package/src/product-shell/social/chat-request-routes.js +0 -130
- package/src/product-shell/social/chat-request-service.js +0 -723
- package/src/product-shell/social/friend-routes.js +0 -82
- package/src/product-shell/social/friend-service.js +0 -557
- package/src/product-shell/social/social-routes.js +0 -21
- package/src/product-shell/social/social-service.js +0 -136
- package/src/product-shell/worlds/world-admin-service.js +0 -486
- package/src/product-shell/worlds/world-authorization.js +0 -136
- package/src/product-shell/worlds/world-broadcast-service.js +0 -296
- package/src/product-shell/worlds/world-routes.js +0 -403
- package/src/product-shell/worlds/world-service.js +0 -89
- package/src/product-shell/worlds/world-text.js +0 -75
|
@@ -1,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
|
-
}
|