@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,369 +0,0 @@
1
- import { projectCandidateFeedModel } from './candidate-feed.js';
2
- import { buildWorldSessionStartupText } from './world-orchestration.js';
3
- import { buildWorldContextText } from '../worlds/world-text.js';
4
-
5
- function normalizeText(value, fallback = null) {
6
- if (value == null) return fallback;
7
- const normalized = String(value).trim();
8
- return normalized || fallback;
9
- }
10
-
11
- function normalizeStringList(values = []) {
12
- if (!Array.isArray(values)) return [];
13
- return [...new Set(values.map((value) => normalizeText(value, null)).filter(Boolean))];
14
- }
15
-
16
- function normalizeCandidateSource(value) {
17
- const normalized = normalizeText(value, null);
18
- if (!normalized) return null;
19
- if (
20
- normalized === 'active_memberships'
21
- || normalized === 'world_members'
22
- || normalized === 'search_results'
23
- ) {
24
- return 'active_memberships_online';
25
- }
26
- return normalized;
27
- }
28
-
29
- function normalizeCandidateSources(values = []) {
30
- if (!Array.isArray(values)) return [];
31
- return [...new Set(values.map((value) => normalizeCandidateSource(value)).filter(Boolean))];
32
- }
33
-
34
- function normalizeWorldEligibility(value, fallback = 'active') {
35
- const normalized = normalizeText(value, fallback);
36
- if (normalized === 'joined') return 'joined';
37
- return 'active';
38
- }
39
-
40
- function normalizeBroadcastAudience(value, fallback = 'members') {
41
- const normalized = normalizeText(value, fallback);
42
- if (normalized === 'admins') return 'admins';
43
- if (normalized === 'admins_and_owner') return 'admins_and_owner';
44
- return 'members';
45
- }
46
-
47
- function normalizeBroadcastReplyPolicy(value, fallback = 'zero') {
48
- const normalized = normalizeText(value, fallback);
49
- if (normalized === 'at_most_one') return 'at_most_one';
50
- return 'zero';
51
- }
52
-
53
- function normalizeBroadcastConfig(broadcast = {}, { enabled = false } = {}) {
54
- const normalized = broadcast && typeof broadcast === 'object' && !Array.isArray(broadcast)
55
- ? broadcast
56
- : {};
57
-
58
- return {
59
- enabled: normalized.enabled === true || enabled === true,
60
- audience: normalizeBroadcastAudience(normalized.audience, 'members'),
61
- replyPolicy: normalizeBroadcastReplyPolicy(normalized.replyPolicy, 'zero'),
62
- excludeSelf: normalized.excludeSelf !== false,
63
- };
64
- }
65
-
66
- function normalizeField(field = {}, index = 0, { required = false } = {}) {
67
- const fieldId = normalizeText(field.fieldId || field.id, `field_${index + 1}`);
68
- return {
69
- fieldId,
70
- label: normalizeText(field.label, fieldId),
71
- type: normalizeText(field.type, 'string'),
72
- source: normalizeText(field.source, 'profile'),
73
- required: field.required === true || required,
74
- description: normalizeText(field.description, null),
75
- examples: normalizeStringList(field.examples),
76
- constraints: field.constraints && typeof field.constraints === 'object' ? field.constraints : {},
77
- };
78
- }
79
-
80
- function normalizeJoinSchema(joinSchema = {}) {
81
- return {
82
- requiredFields: [
83
- {
84
- fieldId: 'participantContextText',
85
- label: 'Entry Profile',
86
- type: 'string',
87
- source: 'membership',
88
- required: true,
89
- description: 'Free-form participant profile/context text used for world join, search, and candidate review.',
90
- examples: [],
91
- constraints: {},
92
- },
93
- ],
94
- optionalFields: [],
95
- hints: [],
96
- };
97
- }
98
-
99
- function normalizeSearchSchema(searchSchema = {}, joinSchema = {}) {
100
- const fieldLookup = new Map(
101
- [...joinSchema.requiredFields, ...joinSchema.optionalFields].map((field) => [field.fieldId, field]),
102
- );
103
- const rawInputFieldIds = Array.isArray(searchSchema.inputFieldIds)
104
- ? searchSchema.inputFieldIds
105
- : (Array.isArray(searchSchema.inputFields)
106
- ? searchSchema.inputFields.map((field) => (typeof field === 'string' ? field : field.fieldId || field.id))
107
- : joinSchema.requiredFields.map((field) => field.fieldId));
108
- const inputFieldIds = normalizeStringList(rawInputFieldIds);
109
- const inputFields = inputFieldIds.map((fieldId, index) => {
110
- const referenced = fieldLookup.get(fieldId);
111
- if (referenced) return { ...referenced };
112
- return normalizeField({ fieldId, label: fieldId }, index, { required: false });
113
- });
114
-
115
- return {
116
- mode: normalizeText(searchSchema.mode, 'membership_profile_search'),
117
- inputFieldIds,
118
- inputFields,
119
- resultFields: normalizeStringList(
120
- searchSchema.resultFields || [
121
- 'agentId',
122
- 'playerId',
123
- 'membershipId',
124
- 'worldId',
125
- 'displayName',
126
- 'headline',
127
- 'online',
128
- 'matchedFieldIds',
129
- 'score',
130
- 'profileSummary',
131
- ],
132
- ),
133
- onlineOnly: searchSchema.onlineOnly !== false,
134
- defaultLimit: Number.isFinite(Number(searchSchema.defaultLimit))
135
- ? Math.max(1, Math.trunc(Number(searchSchema.defaultLimit)))
136
- : 10,
137
- summary: normalizeText(
138
- searchSchema.summary,
139
- 'Compatibility-only manual search over active online world members. Candidate feed review is the canonical path before request_chat.',
140
- ),
141
- hints: normalizeStringList(searchSchema.hints),
142
- };
143
- }
144
-
145
- function resolveConversationTemplateInput(manifest = {}) {
146
- if (manifest.conversationTemplate && typeof manifest.conversationTemplate === 'object' && !Array.isArray(manifest.conversationTemplate)) {
147
- return manifest.conversationTemplate;
148
- }
149
- return {};
150
- }
151
-
152
- export function normalizeWorldManifest(manifest = {}, index = 0) {
153
- const worldId = normalizeText(manifest.worldId || manifest.id, `world_${index + 1}`);
154
- const joinSchema = normalizeJoinSchema(manifest.joinSchema);
155
- const searchSchema = normalizeSearchSchema(manifest.searchSchema, joinSchema);
156
- const conversationTemplate = resolveConversationTemplateInput(manifest);
157
- const defaultInteractionRules = normalizeText(conversationTemplate?.worldRules?.openingText, null);
158
- const normalizedInteractionRules = normalizeText(manifest.interactionRules, defaultInteractionRules);
159
- const normalizedProhibitedRules = normalizeText(manifest.prohibitedRules, null);
160
-
161
- return {
162
- worldId,
163
- slug: normalizeText(manifest.slug, worldId),
164
- displayName: normalizeText(manifest.displayName, worldId),
165
- summary: normalizeText(manifest.summary, null),
166
- description: normalizeText(manifest.description, normalizeText(manifest.summary, null)),
167
- category: normalizeText(manifest.category, 'general'),
168
- lifecycle: normalizeText(manifest.lifecycle, 'scaffold'),
169
- tags: normalizeStringList(manifest.tags),
170
- interactionRules: normalizedInteractionRules,
171
- prohibitedRules: normalizedProhibitedRules,
172
- worldContextText: buildWorldContextText({
173
- worldId,
174
- displayName: normalizeText(manifest.displayName, worldId),
175
- summary: normalizeText(manifest.summary, null),
176
- worldContextText: normalizeText(
177
- manifest.worldContextText,
178
- normalizeText(manifest.runtimeWorldContext?.text, null),
179
- ),
180
- interactionRules: normalizedInteractionRules,
181
- prohibitedRules: normalizedProhibitedRules,
182
- }),
183
- creatorAgentId: normalizeText(manifest.creatorAgentId, null),
184
- eligibility: normalizeWorldEligibility(manifest.eligibility, 'active'),
185
- broadcast: normalizeBroadcastConfig(manifest.broadcast, {
186
- enabled: manifest.broadcast?.enabled === true,
187
- }),
188
- status: normalizeText(manifest.status, manifest.enabled === true ? 'enabled' : 'draft'),
189
- enabled: manifest.enabled === true,
190
- schemaVersion: Number.isFinite(Number(manifest.schemaVersion)) ? Math.max(1, Math.trunc(Number(manifest.schemaVersion))) : 1,
191
- createdAt: normalizeText(manifest.createdAt, null),
192
- updatedAt: normalizeText(manifest.updatedAt, null),
193
- metrics: manifest.metrics && typeof manifest.metrics === 'object'
194
- ? {
195
- totalConversationCount: Number.isFinite(Number(manifest.metrics.totalConversationCount))
196
- ? Math.max(0, Math.trunc(Number(manifest.metrics.totalConversationCount)))
197
- : 0,
198
- }
199
- : { totalConversationCount: 0 },
200
- joinSchema,
201
- searchSchema,
202
- matching: {
203
- mode: normalizeText(manifest.matching?.mode, 'manual_review'),
204
- cadence: normalizeText(manifest.matching?.cadence, 'on_demand'),
205
- strategySummary: normalizeText(manifest.matching?.strategySummary, null),
206
- candidateSources: normalizeCandidateSources(manifest.matching?.candidateSources),
207
- },
208
- conversationTemplate: {
209
- mode: normalizeText(conversationTemplate?.mode, 'a2a'),
210
- worldRules: {
211
- openingText: normalizeText(conversationTemplate?.worldRules?.openingText, null),
212
- turnMessageRules: Array.isArray(conversationTemplate?.worldRules?.turnMessageRules)
213
- ? conversationTemplate.worldRules.turnMessageRules
214
- : [],
215
- convergence: conversationTemplate?.worldRules?.convergence || {},
216
- stateChangeMessages: conversationTemplate?.worldRules?.stateChangeMessages || {},
217
- },
218
- },
219
- meta: {
220
- status: normalizeText(manifest.meta?.status, 'scaffold_ready'),
221
- persistence: normalizeText(manifest.meta?.persistence, 'in_memory_catalog'),
222
- },
223
- };
224
- }
225
-
226
- export function projectWorldCard(world) {
227
- return {
228
- worldId: world.worldId,
229
- slug: world.slug,
230
- displayName: world.displayName,
231
- worldContextText: world.worldContextText,
232
- createdAt: world.createdAt || null,
233
- updatedAt: world.updatedAt || null,
234
- status: world.status || world.meta.status,
235
- enabled: world.enabled === true,
236
- };
237
- }
238
-
239
- export function projectJoinPlan(world) {
240
- return {
241
- worldId: world.worldId,
242
- participantContextField: {
243
- fieldId: 'participantContextText',
244
- label: 'Entry Profile',
245
- description: 'A short text describing who you are in this world and what context you bring into it.',
246
- },
247
- nextAction: 'call_join_world',
248
- };
249
- }
250
-
251
- function projectFieldGuide(fields = [], { required = false } = {}) {
252
- return fields.map((field) => ({
253
- fieldId: field.fieldId,
254
- label: field.label,
255
- required,
256
- type: field.type,
257
- source: field.source,
258
- description: field.description,
259
- examples: field.examples,
260
- constraints: field.constraints,
261
- }));
262
- }
263
-
264
- export function projectSearchModel(world) {
265
- return {
266
- modelId: `${world.worldId}.search.v1`,
267
- worldId: world.worldId,
268
- mode: world.searchSchema.mode,
269
- previewRoute: `/v1/worlds/${world.worldId}/search`,
270
- inputFieldIds: world.searchSchema.inputFieldIds,
271
- inputFields: projectFieldGuide(world.searchSchema.inputFields, { required: false }),
272
- resultFields: world.searchSchema.resultFields,
273
- viewerRequirement: 'active_membership',
274
- onlineOnly: world.searchSchema.onlineOnly,
275
- defaultLimit: world.searchSchema.defaultLimit,
276
- summary: world.searchSchema.summary,
277
- hints: world.searchSchema.hints,
278
- status: 'compatibility_world_search',
279
- };
280
- }
281
-
282
- export function projectWorldDetail(world) {
283
- const joinPlan = projectJoinPlan(world);
284
- const worldContextText = normalizeText(
285
- world.worldContextText,
286
- buildWorldSessionStartupText({
287
- worldId: world.worldId,
288
- displayName: world.displayName,
289
- summary: world.summary,
290
- interactionRules: world.interactionRules,
291
- prohibitedRules: world.prohibitedRules,
292
- conversationMode: world.conversationTemplate.mode,
293
- conversationOverview: {
294
- worldId: world.worldId,
295
- mode: world.conversationTemplate.mode,
296
- openingText: world.conversationTemplate.worldRules.openingText,
297
- convergence: world.conversationTemplate.worldRules.convergence,
298
- },
299
- }),
300
- );
301
-
302
- return {
303
- worldId: world.worldId,
304
- world: {
305
- worldId: world.worldId,
306
- displayName: world.displayName,
307
- worldContextText,
308
- },
309
- management: {
310
- ownerAgentId: world.creatorAgentId || null,
311
- enabled: world.enabled === true,
312
- status: world.status || world.meta.status,
313
- createdAt: world.createdAt || null,
314
- updatedAt: world.updatedAt || null,
315
- memberCount: world.metrics?.memberCount || null,
316
- totalConversationCount: Number.isFinite(Number(world.metrics?.totalConversationCount))
317
- ? Math.max(0, Math.trunc(Number(world.metrics.totalConversationCount)))
318
- : 0,
319
- },
320
- participantContextField: joinPlan.participantContextField,
321
- joinPlan,
322
- worldContextText,
323
- };
324
- }
325
-
326
- export function projectConversationWorldContext(world) {
327
- const runtimeWorldContext = {
328
- worldId: world.worldId,
329
- status: 'ready',
330
- source: 'product_shell_conversation_world_context',
331
- text: normalizeText(
332
- world.worldContextText,
333
- buildWorldSessionStartupText({
334
- worldId: world.worldId,
335
- displayName: world.displayName,
336
- summary: world.summary,
337
- interactionRules: world.interactionRules,
338
- prohibitedRules: world.prohibitedRules,
339
- conversationMode: world.conversationTemplate.mode,
340
- conversationOverview: {
341
- worldId: world.worldId,
342
- mode: world.conversationTemplate.mode,
343
- openingText: world.conversationTemplate.worldRules.openingText,
344
- convergence: world.conversationTemplate.worldRules.convergence,
345
- },
346
- }),
347
- ),
348
- };
349
-
350
- return {
351
- worldId: world.worldId,
352
- displayName: world.displayName,
353
- summary: world.summary,
354
- worldContextText: runtimeWorldContext.text,
355
- interactionRules: world.interactionRules,
356
- prohibitedRules: world.prohibitedRules,
357
- conversationMode: world.conversationTemplate.mode,
358
- conversationOverview: {
359
- worldId: world.worldId,
360
- mode: world.conversationTemplate.mode,
361
- openingText: world.conversationTemplate.worldRules.openingText,
362
- convergence: world.conversationTemplate.worldRules.convergence,
363
- turnMessageRules: world.conversationTemplate.worldRules.turnMessageRules,
364
- },
365
- status: 'ready',
366
- source: 'product_shell_conversation_world_context',
367
- runtimeWorldContext,
368
- };
369
- }
@@ -1,261 +0,0 @@
1
- const FEEDBACK_TOKEN_TO_SENTIMENT = Object.freeze({
2
- '[[like]]': 'like',
3
- '[[dislike]]': 'dislike',
4
- });
5
-
6
- const SENTIMENT_VALUES = Object.freeze(['like', 'dislike']);
7
-
8
- function normalizeText(value, fallback = null) {
9
- if (value == null) return fallback;
10
- const normalized = String(value).trim();
11
- return normalized || fallback;
12
- }
13
-
14
- function createConfigurationError() {
15
- const error = new Error('conversation_feedback_store_unavailable');
16
- error.code = 'conversation_feedback_store_unavailable';
17
- error.status = 500;
18
- return error;
19
- }
20
-
21
- function createInvalidConversationFeedbackError(fieldId, message = `${fieldId} is required`) {
22
- const error = new Error(`invalid_conversation_feedback:${fieldId}`);
23
- error.code = 'invalid_conversation_feedback';
24
- error.status = 400;
25
- error.responseBody = {
26
- error: error.code,
27
- message: 'conversation feedback request is invalid',
28
- fieldErrors: [
29
- {
30
- fieldId,
31
- message,
32
- },
33
- ],
34
- };
35
- return error;
36
- }
37
-
38
- function normalizeSentiment(value, fallback = null) {
39
- const normalized = normalizeText(value, fallback);
40
- return SENTIMENT_VALUES.includes(normalized) ? normalized : fallback;
41
- }
42
-
43
- function resolveSentimentFromToken(token = null) {
44
- const normalizedToken = normalizeText(token, null);
45
- return normalizedToken ? FEEDBACK_TOKEN_TO_SENTIMENT[normalizedToken] || null : null;
46
- }
47
-
48
- export function parseConversationFeedbackTokens(text = '') {
49
- if (typeof text !== 'string' || !text.trim()) {
50
- return {
51
- tokens: [],
52
- sentiment: null,
53
- conflict: false,
54
- };
55
- }
56
-
57
- const matches = [...text.matchAll(/\[\[(like|dislike)\]\]/g)];
58
- if (matches.length === 0) {
59
- return {
60
- tokens: [],
61
- sentiment: null,
62
- conflict: false,
63
- };
64
- }
65
-
66
- const tokens = [...new Set(matches.map((match) => `[[${String(match[1] || '').trim().toLowerCase()}]]`))];
67
- const sentiments = [...new Set(tokens.map((token) => resolveSentimentFromToken(token)).filter(Boolean))];
68
- return {
69
- tokens,
70
- sentiment: sentiments.length === 1 ? sentiments[0] : null,
71
- conflict: sentiments.length > 1,
72
- };
73
- }
74
-
75
- function projectFeedbackMark(feedbackMark = {}) {
76
- return {
77
- feedbackMarkId: normalizeText(feedbackMark.feedbackMarkId, null),
78
- conversationKey: normalizeText(feedbackMark.conversationKey, null),
79
- worldId: normalizeText(feedbackMark.worldId, null),
80
- giverAgentId: normalizeText(feedbackMark.giverAgentId, null),
81
- receiverAgentId: normalizeText(feedbackMark.receiverAgentId, null),
82
- sourceTurnId: normalizeText(feedbackMark.sourceTurnId, null),
83
- sourceDeliveryId: normalizeText(feedbackMark.sourceDeliveryId, null),
84
- token: normalizeText(feedbackMark.token, null),
85
- sentiment: normalizeSentiment(feedbackMark.sentiment, null),
86
- source: normalizeText(feedbackMark.source, 'reply_control_token'),
87
- status: normalizeText(feedbackMark.status, 'active'),
88
- createdAt: normalizeText(feedbackMark.createdAt, null),
89
- updatedAt: normalizeText(feedbackMark.updatedAt, normalizeText(feedbackMark.createdAt, null)),
90
- };
91
- }
92
-
93
- function summarizeReceived(items = []) {
94
- return {
95
- likesReceived: items.filter((item) => item.sentiment === 'like').length,
96
- dislikesReceived: items.filter((item) => item.sentiment === 'dislike').length,
97
- };
98
- }
99
-
100
- export function createConversationFeedbackService({ store } = {}) {
101
- if (
102
- !store
103
- || typeof store.createConversationFeedbackMark !== 'function'
104
- || typeof store.listConversationFeedbackMarks !== 'function'
105
- || typeof store.findConversationFeedbackMarkByConversationAndActors !== 'function'
106
- ) {
107
- throw createConfigurationError();
108
- }
109
-
110
- return {
111
- parseTokens(text = '') {
112
- return parseConversationFeedbackTokens(text);
113
- },
114
-
115
- async recordFeedback({
116
- conversationKey,
117
- worldId = null,
118
- giverAgentId,
119
- receiverAgentId,
120
- sourceTurnId = null,
121
- sourceDeliveryId = null,
122
- token = null,
123
- sentiment = null,
124
- } = {}) {
125
- const normalizedConversationKey = normalizeText(conversationKey, null);
126
- if (!normalizedConversationKey) {
127
- throw createInvalidConversationFeedbackError('conversationKey');
128
- }
129
- const normalizedGiverAgentId = normalizeText(giverAgentId, null);
130
- if (!normalizedGiverAgentId) {
131
- throw createInvalidConversationFeedbackError('giverAgentId');
132
- }
133
- const normalizedReceiverAgentId = normalizeText(receiverAgentId, null);
134
- if (!normalizedReceiverAgentId) {
135
- throw createInvalidConversationFeedbackError('receiverAgentId');
136
- }
137
- const normalizedToken = normalizeText(token, null);
138
- const normalizedSentiment = normalizeSentiment(sentiment, resolveSentimentFromToken(normalizedToken));
139
- if (!normalizedSentiment) {
140
- throw createInvalidConversationFeedbackError('sentiment', 'sentiment must be like or dislike');
141
- }
142
-
143
- const existing = store.findConversationFeedbackMarkByConversationAndActors({
144
- conversationKey: normalizedConversationKey,
145
- giverAgentId: normalizedGiverAgentId,
146
- receiverAgentId: normalizedReceiverAgentId,
147
- });
148
- if (existing) {
149
- return {
150
- recorded: false,
151
- deduped: true,
152
- feedbackMark: projectFeedbackMark(existing),
153
- };
154
- }
155
-
156
- const feedbackMark = await store.createConversationFeedbackMark({
157
- conversationKey: normalizedConversationKey,
158
- worldId: normalizeText(worldId, null),
159
- giverAgentId: normalizedGiverAgentId,
160
- receiverAgentId: normalizedReceiverAgentId,
161
- sourceTurnId: normalizeText(sourceTurnId, null),
162
- sourceDeliveryId: normalizeText(sourceDeliveryId, null),
163
- token: normalizedToken || `[[${normalizedSentiment}]]`,
164
- sentiment: normalizedSentiment,
165
- source: 'reply_control_token',
166
- status: 'active',
167
- });
168
-
169
- return {
170
- recorded: true,
171
- deduped: false,
172
- feedbackMark: projectFeedbackMark(feedbackMark),
173
- };
174
- },
175
-
176
- listFeedbackMarks(filters = {}) {
177
- return store.listConversationFeedbackMarks(filters).map((item) => projectFeedbackMark(item));
178
- },
179
-
180
- summarizeConversation({ conversationKey, viewerAgentId = null } = {}) {
181
- const normalizedConversationKey = normalizeText(conversationKey, null);
182
- const normalizedViewerAgentId = normalizeText(viewerAgentId, null);
183
- const items = normalizedConversationKey
184
- ? this.listFeedbackMarks({ conversationKey: normalizedConversationKey, status: 'active' })
185
- : [];
186
- const viewerGave = normalizedViewerAgentId
187
- ? items.find((item) => item.giverAgentId === normalizedViewerAgentId)?.sentiment || null
188
- : null;
189
- const viewerReceived = normalizedViewerAgentId
190
- ? items.find((item) => item.receiverAgentId === normalizedViewerAgentId)?.sentiment || null
191
- : null;
192
- return {
193
- likeCount: items.filter((item) => item.sentiment === 'like').length,
194
- dislikeCount: items.filter((item) => item.sentiment === 'dislike').length,
195
- viewerGave,
196
- viewerReceived,
197
- };
198
- },
199
-
200
- summarizeAgent({ agentId } = {}) {
201
- const normalizedAgentId = normalizeText(agentId, null);
202
- if (!normalizedAgentId) {
203
- return {
204
- totalLikesReceived: 0,
205
- totalDislikesReceived: 0,
206
- totalLikesGiven: 0,
207
- totalDislikesGiven: 0,
208
- };
209
- }
210
- const received = this.listFeedbackMarks({
211
- receiverAgentId: normalizedAgentId,
212
- status: 'active',
213
- });
214
- const given = this.listFeedbackMarks({
215
- giverAgentId: normalizedAgentId,
216
- status: 'active',
217
- });
218
-
219
- return {
220
- totalLikesReceived: received.filter((item) => item.sentiment === 'like').length,
221
- totalDislikesReceived: received.filter((item) => item.sentiment === 'dislike').length,
222
- totalLikesGiven: given.filter((item) => item.sentiment === 'like').length,
223
- totalDislikesGiven: given.filter((item) => item.sentiment === 'dislike').length,
224
- };
225
- },
226
-
227
- summarizeWorldAgent({ worldId, agentId } = {}) {
228
- const normalizedWorldId = normalizeText(worldId, null);
229
- const normalizedAgentId = normalizeText(agentId, null);
230
- if (!normalizedWorldId || !normalizedAgentId) {
231
- return {
232
- likesReceived: 0,
233
- dislikesReceived: 0,
234
- };
235
- }
236
- return summarizeReceived(this.listFeedbackMarks({
237
- worldId: normalizedWorldId,
238
- receiverAgentId: normalizedAgentId,
239
- status: 'active',
240
- }));
241
- },
242
-
243
- summarizeWorld({ worldId } = {}) {
244
- const normalizedWorldId = normalizeText(worldId, null);
245
- if (!normalizedWorldId) {
246
- return {
247
- totalLikes: 0,
248
- totalDislikes: 0,
249
- };
250
- }
251
- const items = this.listFeedbackMarks({
252
- worldId: normalizedWorldId,
253
- status: 'active',
254
- });
255
- return {
256
- totalLikes: items.filter((item) => item.sentiment === 'like').length,
257
- totalDislikes: items.filter((item) => item.sentiment === 'dislike').length,
258
- };
259
- },
260
- };
261
- }
@@ -1,13 +0,0 @@
1
- export const FEEDBACK_CATEGORY_VALUES = Object.freeze([
2
- 'experience_issue',
3
- 'usage_issue',
4
- 'bug_report',
5
- 'feature_request',
6
- ]);
7
-
8
- export const FEEDBACK_IMPACT_VALUES = Object.freeze([
9
- 'low',
10
- 'medium',
11
- 'high',
12
- 'blocker',
13
- ]);