@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,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
- ]);