@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,403 +0,0 @@
1
- import { authenticateAppTokenRequest, resolveAuthenticatedAgentId } from '../../lib/http-auth.js';
2
- import {
3
- buildCandidateDeliverySummary,
4
- buildResolvedWorldJoinOrchestration,
5
- buildWorldSelectionPrompt,
6
- } from '../contracts/world-orchestration.js';
7
-
8
- function sendWorldError(res, error) {
9
- const status = Number.isInteger(error?.status) ? error.status : 500;
10
- if (error?.responseBody && typeof error.responseBody === 'object') {
11
- return res.status(status).json(error.responseBody);
12
- }
13
- const code = typeof error?.code === 'string' ? error.code : 'internal_error';
14
- return res.status(status).json({ error: code, message: error?.message || code });
15
- }
16
-
17
- function normalizePositiveInteger(value, fallback) {
18
- const parsed = Number(value);
19
- if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
20
- return Math.max(1, Math.trunc(parsed));
21
- }
22
-
23
- function normalizeWorldListSort(value) {
24
- const normalized = String(value || '').trim().toLowerCase();
25
- if (normalized === 'latest') return 'latest';
26
- return 'hot';
27
- }
28
-
29
- function resolveWorldTimestamp(item = {}) {
30
- const value = Date.parse(item.updatedAt || item.createdAt || '');
31
- return Number.isFinite(value) ? value : 0;
32
- }
33
-
34
- function buildWorldDirectoryPayload(worldService, membershipService, query = {}) {
35
- const requestedLimit = normalizePositiveInteger(query.limit, 10);
36
- const limit = Math.min(requestedLimit, 50);
37
- const requestedPage = normalizePositiveInteger(query.page, 1);
38
- const sort = normalizeWorldListSort(query.sort);
39
- const activeMembershipCounts = new Map();
40
-
41
- for (const membership of membershipService.listMembershipsAcrossWorlds({ status: 'active' })) {
42
- activeMembershipCounts.set(
43
- membership.worldId,
44
- (activeMembershipCounts.get(membership.worldId) || 0) + 1,
45
- );
46
- }
47
-
48
- const items = worldService.listWorlds().map((world) => {
49
- const hotness = activeMembershipCounts.get(world.worldId) || 0;
50
- return {
51
- ...world,
52
- hotness,
53
- activatedMemberCount: hotness,
54
- };
55
- });
56
-
57
- const sortedItems = [...items].sort((left, right) => {
58
- if (sort === 'latest') {
59
- const rightTs = resolveWorldTimestamp(right);
60
- const leftTs = resolveWorldTimestamp(left);
61
- if (rightTs !== leftTs) return rightTs - leftTs;
62
- return String(left.displayName || '').localeCompare(String(right.displayName || ''));
63
- }
64
-
65
- if (right.hotness !== left.hotness) return right.hotness - left.hotness;
66
- return String(left.displayName || '').localeCompare(String(right.displayName || ''));
67
- });
68
-
69
- const totalCount = sortedItems.length;
70
- const totalPages = totalCount === 0 ? 0 : Math.ceil(totalCount / limit);
71
- const page = totalPages === 0 ? 1 : Math.min(requestedPage, totalPages);
72
- const startIndex = (page - 1) * limit;
73
- const pagedItems = sortedItems.slice(startIndex, startIndex + limit);
74
-
75
- const payload = {
76
- items: pagedItems,
77
- pagination: {
78
- page,
79
- totalPages,
80
- totalCount,
81
- },
82
- sort,
83
- };
84
-
85
- return {
86
- ...payload,
87
- orchestration: buildWorldSelectionPrompt({
88
- ...payload,
89
- recommendedWorldId: pagedItems[0]?.worldId || null,
90
- }),
91
- };
92
- }
93
-
94
- export function registerWorldRoutes(
95
- app,
96
- {
97
- productShell,
98
- store,
99
- worldService,
100
- membershipService,
101
- matchmakingService,
102
- searchService,
103
- worldBroadcastService,
104
- worldAdminService,
105
- worldConversationOrchestrator,
106
- },
107
- ) {
108
- function resolveAgentIdentity(req, res, { providedAgentId = null, fieldName = 'agentId', required = true } = {}) {
109
- const result = resolveAuthenticatedAgentId({
110
- store,
111
- req,
112
- providedAgentId,
113
- fieldName,
114
- });
115
- if (!result.ok) {
116
- res.status(result.status).json(result.body);
117
- return null;
118
- }
119
- if (!result.agentId) {
120
- if (!required) return null;
121
- res.status(400).json({
122
- error: 'invalid_agent',
123
- field: fieldName,
124
- message: `${fieldName} is required`,
125
- });
126
- return null;
127
- }
128
- return result.agentId;
129
- }
130
-
131
- function resolveBroadcastSender(req, res) {
132
- const auth = authenticateAppTokenRequest({ store, req });
133
- if (!auth.present) {
134
- res.status(401).json({
135
- error: 'not_authenticated',
136
- reason: 'credential_required',
137
- });
138
- return null;
139
- }
140
- if (!auth.ok) {
141
- res.status(401).json(auth.error);
142
- return null;
143
- }
144
-
145
- const explicitAgentId = String(req.body?.agentId || req.body?.senderAgentId || '').trim() || null;
146
- if (explicitAgentId && explicitAgentId !== auth.agent.agentId) {
147
- res.status(403).json({
148
- error: 'forbidden',
149
- reason: 'agent_identity_mismatch',
150
- field: 'agentId',
151
- authenticatedAgentId: auth.agent.agentId,
152
- providedAgentId: explicitAgentId,
153
- });
154
- return null;
155
- }
156
-
157
- return auth.agent.agentId;
158
- }
159
-
160
- app.get('/v1/meta/product-shell', (_req, res) => {
161
- res.json(productShell.describe());
162
- });
163
-
164
- app.get('/v1/worlds', (req, res) => {
165
- res.json(buildWorldDirectoryPayload(worldService, membershipService, req.query));
166
- });
167
-
168
- app.post('/v1/worlds', async (req, res) => {
169
- const ownerAgentId = resolveAgentIdentity(req, res, {
170
- providedAgentId: req.body?.agentId || req.body?.creatorAgentId,
171
- fieldName: 'agentId',
172
- });
173
- if (!ownerAgentId) return;
174
- try {
175
- const result = await worldAdminService.createWorld({
176
- ownerAgentId,
177
- displayName: req.body?.displayName,
178
- worldContextText: req.body?.worldContextText,
179
- enabled: req.body?.enabled,
180
- });
181
- res.status(201).json(result);
182
- } catch (error) {
183
- sendWorldError(res, error);
184
- }
185
- });
186
-
187
- app.get('/v1/worlds/:worldId', (req, res) => {
188
- try {
189
- const detail = worldService.describeWorldDetail(req.params.worldId);
190
- res.json(detail);
191
- } catch (error) {
192
- sendWorldError(res, error);
193
- }
194
- });
195
-
196
- app.post('/v1/worlds/:worldId/search', (req, res) => {
197
- const agentId = resolveAgentIdentity(req, res, {
198
- providedAgentId: req.body?.agentId,
199
- fieldName: 'agentId',
200
- });
201
- if (!agentId) return;
202
- try {
203
- const result = searchService.searchWorld({
204
- worldId: req.params.worldId,
205
- agentId,
206
- query: req.body?.query || {},
207
- limit: req.body?.limit || null,
208
- });
209
- res.json(result);
210
- } catch (error) {
211
- sendWorldError(res, error);
212
- }
213
- });
214
-
215
- app.post('/v1/worlds/:worldId/broadcast', async (req, res) => {
216
- const senderAgentId = resolveBroadcastSender(req, res);
217
- if (!senderAgentId) return;
218
- try {
219
- const result = await worldBroadcastService.broadcastWorld({
220
- worldId: req.params.worldId,
221
- senderAgentId,
222
- payload: req.body?.payload,
223
- audience: req.body?.audience,
224
- excludeSelf: req.body?.excludeSelf,
225
- });
226
- res.json(result);
227
- } catch (error) {
228
- sendWorldError(res, error);
229
- }
230
- });
231
-
232
- app.get('/v1/worlds/:worldId/candidates', (req, res) => {
233
- const agentId = resolveAgentIdentity(req, res, {
234
- providedAgentId: req.query.agentId || null,
235
- fieldName: 'agentId',
236
- });
237
- if (!agentId) return;
238
- try {
239
- const result = matchmakingService.listCandidateFeed({
240
- worldId: req.params.worldId,
241
- agentId,
242
- limit: req.query.limit || null,
243
- });
244
- const worldDetail = worldService.describeWorldDetail(req.params.worldId);
245
- res.json({
246
- ...result,
247
- candidateDelivery: buildCandidateDeliverySummary(result, {
248
- worldDetail,
249
- limit: req.query.limit || result.limit || null,
250
- }),
251
- });
252
- } catch (error) {
253
- sendWorldError(res, error);
254
- }
255
- });
256
-
257
- app.post('/v1/worlds/:worldId/join', async (req, res) => {
258
- const agentId = resolveAgentIdentity(req, res, {
259
- providedAgentId: req.body?.agentId,
260
- fieldName: 'agentId',
261
- });
262
- if (!agentId) return;
263
- try {
264
- const result = await membershipService.joinWorld({
265
- worldId: req.params.worldId,
266
- agentId,
267
- participantContextText: req.body?.participantContextText,
268
- });
269
- const worldDetail = worldService.describeWorldDetail(req.params.worldId);
270
- const candidateFeed = matchmakingService.listCandidateFeed({
271
- worldId: req.params.worldId,
272
- agentId,
273
- limit: req.body?.candidateLimit || null,
274
- });
275
- const candidateDelivery = buildCandidateDeliverySummary(candidateFeed, {
276
- worldDetail,
277
- limit: req.body?.candidateLimit || candidateFeed.limit || null,
278
- });
279
- res.status(result.created ? 201 : 200).json({
280
- status: result.status,
281
- worldId: req.params.worldId,
282
- membershipStatus: result.membershipStatus,
283
- normalizedProfile: result.normalizedProfile,
284
- membership: result.membership,
285
- nextAction: result.nextAction,
286
- nextStageSummary: result.nextStageSummary,
287
- candidateFeed: {
288
- ...candidateFeed,
289
- candidateDelivery,
290
- },
291
- candidateDelivery,
292
- orchestration: buildResolvedWorldJoinOrchestration({
293
- joinResult: result,
294
- candidateDelivery,
295
- }) || result.orchestration || null,
296
- });
297
- } catch (error) {
298
- sendWorldError(res, error);
299
- }
300
- });
301
-
302
- app.get('/v1/worlds/:worldId/memberships', (req, res) => {
303
- const agentId = resolveAgentIdentity(req, res, {
304
- providedAgentId: req.query.agentId || null,
305
- fieldName: 'agentId',
306
- required: false,
307
- });
308
- try {
309
- const items = membershipService.listMemberships({
310
- worldId: req.params.worldId,
311
- agentId,
312
- status: req.query.status || null,
313
- });
314
- res.json({ items });
315
- } catch (error) {
316
- sendWorldError(res, error);
317
- }
318
- });
319
-
320
- app.post('/v1/worlds/:worldId/memberships', async (req, res) => {
321
- const agentId = resolveAgentIdentity(req, res, {
322
- providedAgentId: req.body?.agentId,
323
- fieldName: 'agentId',
324
- });
325
- if (!agentId) return;
326
- try {
327
- const result = await membershipService.createMembership({
328
- worldId: req.params.worldId,
329
- agentId,
330
- participantContextText: req.body?.participantContextText,
331
- });
332
- res.status(result.created ? 201 : 200).json(result.membership);
333
- } catch (error) {
334
- sendWorldError(res, error);
335
- }
336
- });
337
-
338
- app.post('/v1/worlds/:worldId/conversation-preview', (req, res) => {
339
- try {
340
- const result = worldConversationOrchestrator.previewConversation({
341
- worldId: req.params.worldId,
342
- conversationKey: req.body?.conversationKey,
343
- });
344
- res.json(result);
345
- } catch (error) {
346
- sendWorldError(res, error);
347
- }
348
- });
349
-
350
- app.get('/v1/moderation/worlds', (req, res) => {
351
- const actorAgentId = resolveAgentIdentity(req, res, {
352
- providedAgentId: req.query.agentId || null,
353
- fieldName: 'agentId',
354
- });
355
- if (!actorAgentId) return;
356
- try {
357
- const items = worldAdminService.listManagedWorlds({
358
- actorAgentId,
359
- includeDisabled: req.query.includeDisabled !== 'false',
360
- });
361
- res.json({ items });
362
- } catch (error) {
363
- sendWorldError(res, error);
364
- }
365
- });
366
-
367
- app.get('/v1/moderation/worlds/:worldId', (req, res) => {
368
- const actorAgentId = resolveAgentIdentity(req, res, {
369
- providedAgentId: req.query.agentId || null,
370
- fieldName: 'agentId',
371
- });
372
- if (!actorAgentId) return;
373
- try {
374
- const result = worldAdminService.getManagedWorld({
375
- actorAgentId,
376
- worldId: req.params.worldId,
377
- });
378
- res.json(result);
379
- } catch (error) {
380
- sendWorldError(res, error);
381
- }
382
- });
383
-
384
- app.patch('/v1/moderation/worlds/:worldId', async (req, res) => {
385
- const actorAgentId = resolveAgentIdentity(req, res, {
386
- providedAgentId: req.body?.agentId || null,
387
- fieldName: 'agentId',
388
- });
389
- if (!actorAgentId) return;
390
- try {
391
- const result = await worldAdminService.manageWorld({
392
- actorAgentId,
393
- worldId: req.params.worldId,
394
- changes: req.body?.changes || null,
395
- enabled: Object.prototype.hasOwnProperty.call(req.body || {}, 'enabled') ? req.body.enabled : null,
396
- status: req.body?.status || null,
397
- });
398
- res.json(result);
399
- } catch (error) {
400
- sendWorldError(res, error);
401
- }
402
- });
403
- }
@@ -1,89 +0,0 @@
1
- import {
2
- normalizeWorldManifest,
3
- projectWorldCard,
4
- projectWorldDetail,
5
- projectJoinPlan,
6
- projectConversationWorldContext,
7
- } from '../contracts/world-manifest.js';
8
- import { DEFAULT_WORLD_MANIFESTS } from '../catalog/default-world-catalog.js';
9
-
10
- function createWorldNotFoundError(worldId) {
11
- const error = new Error(`world_not_found:${worldId}`);
12
- error.code = 'world_not_found';
13
- error.status = 404;
14
- return error;
15
- }
16
-
17
- export function createWorldService({ worldCatalog = DEFAULT_WORLD_MANIFESTS, store = null } = {}) {
18
- const seededWorlds = worldCatalog.map((manifest, index) => normalizeWorldManifest(manifest, index));
19
- const seededWorldMap = new Map(seededWorlds.map((world) => [world.worldId, world]));
20
-
21
- function loadCustomWorlds({ includeDisabled = false, creatorAgentId = null } = {}) {
22
- if (!store || typeof store.listWorldConfigs !== 'function') return [];
23
-
24
- return store
25
- .listWorldConfigs({ creatorAgentId })
26
- .filter((world) => includeDisabled || world.enabled === true)
27
- .map((world, index) => normalizeWorldManifest(world, seededWorlds.length + index));
28
- }
29
-
30
- function loadWorldMap(options = {}) {
31
- return new Map(
32
- [...seededWorlds, ...loadCustomWorlds(options)].map((world) => [world.worldId, world]),
33
- );
34
- }
35
-
36
- return {
37
- listWorlds() {
38
- return [...seededWorlds, ...loadCustomWorlds()].map((world) => projectWorldCard(world));
39
- },
40
- listCustomWorlds(options = {}) {
41
- return loadCustomWorlds(options);
42
- },
43
- listWorldIds() {
44
- return [...seededWorlds, ...loadCustomWorlds({ includeDisabled: true })].map((world) => world.worldId);
45
- },
46
- listOwnedWorlds({ creatorAgentId, includeDisabled = true } = {}) {
47
- return loadCustomWorlds({ includeDisabled, creatorAgentId });
48
- },
49
- getWorld(worldId, options = {}) {
50
- const normalizedWorldId = String(worldId || '').trim();
51
- if (!normalizedWorldId) return null;
52
- if (seededWorldMap.has(normalizedWorldId)) return seededWorldMap.get(normalizedWorldId) || null;
53
- return loadWorldMap(options).get(normalizedWorldId) || null;
54
- },
55
- getOwnedWorld(worldId, creatorAgentId) {
56
- const normalizedWorldId = String(worldId || '').trim();
57
- if (!normalizedWorldId) return null;
58
- const world = loadCustomWorlds({ includeDisabled: true, creatorAgentId })
59
- .find((entry) => entry.worldId === normalizedWorldId) || null;
60
- return world;
61
- },
62
- requireWorld(worldId, options = {}) {
63
- const world = this.getWorld(worldId, options);
64
- if (!world) throw createWorldNotFoundError(worldId);
65
- return world;
66
- },
67
- requireOwnedWorld(worldId, creatorAgentId) {
68
- const world = this.getOwnedWorld(worldId, creatorAgentId);
69
- if (!world) throw createWorldNotFoundError(worldId);
70
- return world;
71
- },
72
- describeWorldDetail(worldId) {
73
- return projectWorldDetail(this.requireWorld(worldId));
74
- },
75
- buildJoinPlan(worldId) {
76
- return projectJoinPlan(this.requireWorld(worldId));
77
- },
78
- describeConversationWorldContext(worldId) {
79
- return projectConversationWorldContext(this.requireWorld(worldId));
80
- },
81
- describeCatalog() {
82
- return {
83
- worldCount: this.listWorldIds().length,
84
- worldIds: this.listWorldIds(),
85
- status: store ? 'seeded_plus_store' : 'seeded_catalog',
86
- };
87
- },
88
- };
89
- }
@@ -1,75 +0,0 @@
1
- function normalizeText(value, fallback = null) {
2
- if (value == null) return fallback;
3
- const normalized = String(value).trim();
4
- return normalized || fallback;
5
- }
6
-
7
- function normalizeStringList(values = []) {
8
- if (!Array.isArray(values)) return [];
9
- return [...new Set(values.map((value) => normalizeText(value, null)).filter(Boolean))];
10
- }
11
-
12
- function formatProfileValue(value) {
13
- if (value == null) return null;
14
- if (typeof value === 'string') return normalizeText(value, null);
15
- if (typeof value === 'number' || typeof value === 'boolean') return String(value);
16
- if (Array.isArray(value)) {
17
- const normalized = normalizeStringList(value);
18
- return normalized.length > 0 ? normalized.join(', ') : null;
19
- }
20
- return null;
21
- }
22
-
23
- export function buildWorldContextText({
24
- worldId = null,
25
- displayName = null,
26
- summary = null,
27
- worldContextText = null,
28
- interactionRules = null,
29
- prohibitedRules = null,
30
- } = {}) {
31
- const explicitWorldContextText = normalizeText(worldContextText, null);
32
- if (explicitWorldContextText) return explicitWorldContextText;
33
-
34
- const normalizedWorldId = normalizeText(worldId, null);
35
- const normalizedDisplayName = normalizeText(displayName, normalizedWorldId);
36
- const lines = [
37
- normalizedDisplayName
38
- ? `世界:${normalizedDisplayName}${normalizedWorldId ? ` [${normalizedWorldId}]` : ''}`
39
- : (normalizedWorldId ? `世界:${normalizedWorldId}` : null),
40
- normalizeText(summary, null) ? `简介:${normalizeText(summary, null)}` : null,
41
- normalizeText(interactionRules, null) ? `互动规则:${normalizeText(interactionRules, null)}` : null,
42
- normalizeText(prohibitedRules, null) ? `禁止事项:${normalizeText(prohibitedRules, null)}` : null,
43
- ].filter(Boolean);
44
-
45
- return lines.length > 0 ? lines.join('\n') : null;
46
- }
47
-
48
- export function buildParticipantContextText({
49
- world = null,
50
- agent = null,
51
- participantContextText = null,
52
- profileSnapshot = null,
53
- } = {}) {
54
- const explicitParticipantContextText = normalizeText(participantContextText, null);
55
- if (explicitParticipantContextText) return explicitParticipantContextText;
56
-
57
- const normalizedProfileSnapshot = profileSnapshot && typeof profileSnapshot === 'object' && !Array.isArray(profileSnapshot)
58
- ? profileSnapshot
59
- : {};
60
- const normalizedProfileText = normalizeText(normalizedProfileSnapshot.participantContextText, null);
61
- if (normalizedProfileText) return normalizedProfileText;
62
-
63
- const lines = [
64
- normalizeText(agent?.displayName, null) ? `名称:${normalizeText(agent.displayName, null)}` : null,
65
- normalizeText(normalizedProfileSnapshot.headline, null) ? `简介:${normalizeText(normalizedProfileSnapshot.headline, null)}` : null,
66
- ...Object.entries(normalizedProfileSnapshot).map(([fieldId, rawValue]) => {
67
- if (fieldId === 'headline' || fieldId === 'participantContextText') return null;
68
- const value = formatProfileValue(rawValue);
69
- if (!value) return null;
70
- return `${fieldId}:${value}`;
71
- }),
72
- ].filter(Boolean);
73
-
74
- return lines.length > 0 ? lines.join('\n') : null;
75
- }