@xfxstudio/claworld 0.1.4 → 0.2.0
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 +12 -29
- package/openclaw.plugin.json +9 -33
- package/package.json +2 -10
- package/skills/claworld-help/SKILL.md +86 -160
- package/skills/claworld-join-and-chat/SKILL.md +107 -203
- package/skills/claworld-manage-worlds/SKILL.md +75 -392
- package/src/lib/chat-request.js +347 -0
- package/src/lib/{accepted-chat-kickoff.js → relay/kickoff-text.js} +67 -26
- package/src/openclaw/index.js +0 -5
- package/src/openclaw/installer/cli.js +14 -16
- package/src/openclaw/installer/core.js +13 -14
- package/src/openclaw/installer/doctor.js +69 -31
- package/src/openclaw/installer/workspace-contract.js +33 -9
- package/src/openclaw/plugin/claworld-channel-plugin.js +156 -625
- package/src/openclaw/plugin/config-schema.js +4 -16
- package/src/openclaw/plugin/managed-config.js +127 -75
- package/src/openclaw/plugin/onboarding.js +7 -3
- package/src/openclaw/plugin/register.js +40 -339
- package/src/openclaw/plugin/relay-client.js +112 -102
- package/src/openclaw/protocol/relay-event-protocol.js +34 -22
- package/src/openclaw/runtime/canonical-result-builder.js +15 -5
- package/src/openclaw/runtime/demo-session-bootstrap.js +0 -4
- package/src/openclaw/runtime/feedback-helper.js +3 -2
- package/src/openclaw/runtime/inbound-session-router.js +28 -20
- package/src/openclaw/runtime/outbound-session-bridge.js +21 -9
- package/src/openclaw/runtime/product-shell-helper.js +45 -637
- package/src/openclaw/runtime/runtime-path.js +2 -2
- package/src/openclaw/runtime/system-message-orchestrator.js +1 -1
- package/src/openclaw/runtime/tool-contracts.js +36 -258
- package/src/openclaw/runtime/world-moderation-helper.js +11 -65
- package/src/product-shell/catalog/default-world-catalog.js +15 -33
- package/src/product-shell/contracts/candidate-feed.js +40 -5
- package/src/product-shell/contracts/chat-request-approval-policy.js +3 -3
- package/src/product-shell/contracts/world-manifest.js +134 -161
- package/src/product-shell/contracts/world-orchestration.js +55 -326
- package/src/product-shell/feedback/feedback-routes.js +4 -3
- package/src/product-shell/feedback/feedback-service.js +11 -8
- package/src/product-shell/index.js +6 -7
- package/src/product-shell/matching/matchmaking-service.js +39 -5
- package/src/product-shell/membership/membership-service.js +125 -147
- package/src/product-shell/onboarding/onboarding-service.js +2 -2
- package/src/product-shell/orchestration/world-conversation-orchestrator.js +30 -0
- package/src/product-shell/orchestration/world-conversation-text.js +231 -0
- package/src/product-shell/results/result-service.js +9 -3
- package/src/product-shell/search/search-service.js +28 -1
- package/src/product-shell/social/chat-request-routes.js +0 -1
- package/src/product-shell/social/chat-request-service.js +1 -102
- package/src/product-shell/worlds/world-admin-service.js +86 -277
- package/src/product-shell/worlds/world-authorization.js +3 -5
- package/src/product-shell/worlds/world-routes.js +8 -38
- package/src/product-shell/worlds/world-service.js +3 -3
- package/src/product-shell/worlds/world-text.js +77 -0
- package/src/lib/runtime-guidance.js +0 -457
- package/src/openclaw/runtime/world-session-startup.js +0 -1
- package/src/product-shell/orchestration/session-orchestrator.js +0 -38
|
@@ -68,13 +68,6 @@ export function normalizeProfile(profile = {}) {
|
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
export function mergeProfileState(profile = {}, profileUpdate = {}) {
|
|
72
|
-
return {
|
|
73
|
-
...normalizeProfile(profile),
|
|
74
|
-
...normalizeProfile(profileUpdate),
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
71
|
function normalizeLookupText(value) {
|
|
79
72
|
return normalizeText(value, '')?.toLowerCase() || '';
|
|
80
73
|
}
|
|
@@ -108,12 +101,9 @@ function normalizeWorldSummary(world = {}) {
|
|
|
108
101
|
return {
|
|
109
102
|
worldId: normalizeText(rawWorldId, 'unknown-world'),
|
|
110
103
|
displayName: normalizeText(summary.displayName || world.displayName, normalizeText(rawWorldId, 'Unknown World')),
|
|
111
|
-
|
|
112
|
-
category: normalizeText(summary.category || world.category, 'general'),
|
|
104
|
+
worldContextText: normalizeText(summary.worldContextText || world.worldContextText, ''),
|
|
113
105
|
hotness: normalizeInteger(summary.hotness || world.hotness || world.activatedMemberCount, 0),
|
|
114
106
|
requiredFieldCount: normalizeInteger(summary.requiredFieldCount || world.requiredFieldCount, 0),
|
|
115
|
-
matchingMode: normalizeText(summary.matchingMode || world.matchingMode, 'manual_review'),
|
|
116
|
-
sessionMode: normalizeText(summary.sessionMode || world.sessionMode, 'a2a'),
|
|
117
107
|
};
|
|
118
108
|
}
|
|
119
109
|
|
|
@@ -180,7 +170,7 @@ function normalizeWorldDetail(payload = {}) {
|
|
|
180
170
|
requiredFieldCount: normalizeInteger(payload.requiredFieldCount, requiredFields.length) || requiredFields.length,
|
|
181
171
|
optionalFieldCount: normalizeInteger(payload.optionalFieldCount, optionalFields.length) || optionalFields.length,
|
|
182
172
|
matchingMode: normalizeText(payload.matchingMode, 'manual_review'),
|
|
183
|
-
|
|
173
|
+
conversationMode: normalizeText(payload.conversationMode, 'a2a'),
|
|
184
174
|
interactionRules: normalizeText(payload.interactionRules, null),
|
|
185
175
|
prohibitedRules: normalizeText(payload.prohibitedRules, null),
|
|
186
176
|
ratingRules: normalizeText(payload.ratingRules, null),
|
|
@@ -191,7 +181,10 @@ function normalizeWorldDetail(payload = {}) {
|
|
|
191
181
|
optionalFields,
|
|
192
182
|
hints: normalizeStringList(payload.hints),
|
|
193
183
|
nextAction: normalizeText(payload.nextAction, 'call_join_world'),
|
|
194
|
-
|
|
184
|
+
conversationOverview:
|
|
185
|
+
payload.conversationOverview && typeof payload.conversationOverview === 'object'
|
|
186
|
+
? payload.conversationOverview
|
|
187
|
+
: {},
|
|
195
188
|
matchingOverview: payload.matchingOverview && typeof payload.matchingOverview === 'object' ? payload.matchingOverview : {},
|
|
196
189
|
searchSchema: normalizeSearchSchema(payload.searchSchema || {}, {
|
|
197
190
|
worldId: normalizedWorldId,
|
|
@@ -204,8 +197,8 @@ function normalizeWorldDetail(payload = {}) {
|
|
|
204
197
|
const agentSummary = payload.agentSummary && typeof payload.agentSummary === 'object' ? payload.agentSummary : {};
|
|
205
198
|
const joinSchema = payload.joinSchema && typeof payload.joinSchema === 'object' ? payload.joinSchema : {};
|
|
206
199
|
const fieldGuide = payload.fieldGuide && typeof payload.fieldGuide === 'object' ? payload.fieldGuide : {};
|
|
207
|
-
const
|
|
208
|
-
? payload.
|
|
200
|
+
const conversationOverview = payload.conversationOverview && typeof payload.conversationOverview === 'object'
|
|
201
|
+
? payload.conversationOverview
|
|
209
202
|
: {};
|
|
210
203
|
const matchingOverview = payload.matchingOverview && typeof payload.matchingOverview === 'object'
|
|
211
204
|
? payload.matchingOverview
|
|
@@ -237,7 +230,10 @@ function normalizeWorldDetail(payload = {}) {
|
|
|
237
230
|
requiredFieldCount: normalizeInteger(joinSchema.requiredFieldCount, requiredFields.length) || requiredFields.length,
|
|
238
231
|
optionalFieldCount: normalizeInteger(joinSchema.optionalFieldCount, optionalFields.length) || optionalFields.length,
|
|
239
232
|
matchingMode: normalizeText(payload.matchingMode || agentSummary.matchingMode || matchingOverview.mode || world.matching?.mode, 'manual_review'),
|
|
240
|
-
|
|
233
|
+
conversationMode: normalizeText(
|
|
234
|
+
payload.conversationMode || agentSummary.conversationMode || conversationOverview.mode || world.conversationTemplate?.mode,
|
|
235
|
+
'a2a',
|
|
236
|
+
),
|
|
241
237
|
interactionRules: normalizeText(payload.interactionRules || world.interactionRules, null),
|
|
242
238
|
prohibitedRules: normalizeText(payload.prohibitedRules || world.prohibitedRules, null),
|
|
243
239
|
ratingRules: normalizeText(payload.ratingRules || world.ratingRules, null),
|
|
@@ -248,7 +244,7 @@ function normalizeWorldDetail(payload = {}) {
|
|
|
248
244
|
optionalFields,
|
|
249
245
|
hints: normalizeStringList(payload.hints || joinSchema.hints),
|
|
250
246
|
nextAction: normalizeText(payload.nextAction || joinSchema.nextAction, 'call_join_world'),
|
|
251
|
-
|
|
247
|
+
conversationOverview,
|
|
252
248
|
matchingOverview,
|
|
253
249
|
searchSchema: normalizeSearchSchema(searchOverview, {
|
|
254
250
|
worldId,
|
|
@@ -257,55 +253,6 @@ function normalizeWorldDetail(payload = {}) {
|
|
|
257
253
|
};
|
|
258
254
|
}
|
|
259
255
|
|
|
260
|
-
function normalizeJoinCheckField(field = {}, index = 0) {
|
|
261
|
-
return normalizeField(field, index, { required: true });
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function normalizeJoinCheckResponse(payload = {}, { worldId = null, profile = {} } = {}) {
|
|
265
|
-
const missingFields = Array.isArray(payload.missingFields)
|
|
266
|
-
? payload.missingFields.map((field, index) => normalizeJoinCheckField(field, index))
|
|
267
|
-
: [];
|
|
268
|
-
const guidance = payload.missingFieldGuidance && typeof payload.missingFieldGuidance === 'object'
|
|
269
|
-
? payload.missingFieldGuidance
|
|
270
|
-
: {};
|
|
271
|
-
const orderedMissingFields = Array.isArray(guidance.orderedMissingFields)
|
|
272
|
-
? guidance.orderedMissingFields.map((field, index) => normalizeJoinCheckField(field, index))
|
|
273
|
-
: missingFields;
|
|
274
|
-
const fallbackNextMissingField = orderedMissingFields[0] || missingFields[0] || null;
|
|
275
|
-
const nextMissingField = payload.nextMissingField
|
|
276
|
-
? normalizeJoinCheckField(payload.nextMissingField)
|
|
277
|
-
: (guidance.nextMissingField ? normalizeJoinCheckField(guidance.nextMissingField) : fallbackNextMissingField);
|
|
278
|
-
const accepted = payload.accepted === true || orderedMissingFields.length === 0;
|
|
279
|
-
|
|
280
|
-
return {
|
|
281
|
-
worldId: normalizeText(payload.worldId, worldId || 'unknown-world'),
|
|
282
|
-
accepted,
|
|
283
|
-
status: normalizeText(payload.status, accepted ? 'eligible' : 'needs_profile'),
|
|
284
|
-
missingFields,
|
|
285
|
-
nextMissingField,
|
|
286
|
-
missingFieldGuidance: {
|
|
287
|
-
mode: normalizeText(guidance.mode, nextMissingField ? 'ordered_required_fields' : 'complete'),
|
|
288
|
-
orderedMissingFields,
|
|
289
|
-
orderedMissingFieldIds: normalizeStringList(
|
|
290
|
-
Array.isArray(guidance.orderedMissingFieldIds)
|
|
291
|
-
? guidance.orderedMissingFieldIds
|
|
292
|
-
: orderedMissingFields.map((field) => field.fieldId),
|
|
293
|
-
),
|
|
294
|
-
nextMissingField,
|
|
295
|
-
remainingRequiredFieldCount: normalizeInteger(guidance.remainingRequiredFieldCount, orderedMissingFields.length),
|
|
296
|
-
},
|
|
297
|
-
normalizedProfile: normalizeProfile(
|
|
298
|
-
payload.normalizedProfile && typeof payload.normalizedProfile === 'object'
|
|
299
|
-
? payload.normalizedProfile
|
|
300
|
-
: profile,
|
|
301
|
-
),
|
|
302
|
-
nextAction: normalizeText(
|
|
303
|
-
payload.nextAction,
|
|
304
|
-
accepted ? 'call_join_world' : 'retry_join_world_after_profile_update',
|
|
305
|
-
),
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
|
|
309
256
|
function normalizeProfileSummaryField(field = {}, index = 0) {
|
|
310
257
|
const fieldId = normalizeText(field.fieldId || field.id, `field_${index + 1}`);
|
|
311
258
|
const value = Array.isArray(field.value)
|
|
@@ -388,6 +335,7 @@ function normalizeCandidate(candidate = {}, index = 0) {
|
|
|
388
335
|
candidateId: normalizeText(candidate.candidateId, `candidate_${index + 1}`),
|
|
389
336
|
worldId: normalizeText(candidate.worldId, 'unknown-world'),
|
|
390
337
|
sourceMembershipId: normalizeText(candidate.sourceMembershipId, null),
|
|
338
|
+
online: candidate.online === true,
|
|
391
339
|
targetAgentId,
|
|
392
340
|
requestChat,
|
|
393
341
|
profileSummary: normalizeCandidateProfileSummary(candidate.profileSummary),
|
|
@@ -420,7 +368,7 @@ function normalizeCandidateFeedResponse(payload = {}, { worldId = null, agentId
|
|
|
420
368
|
payload.nextAction,
|
|
421
369
|
candidates.length > 0 ? 'review_candidates_then_request_chat' : 'wait_for_more_candidates',
|
|
422
370
|
),
|
|
423
|
-
candidateSource: normalizeText(payload.candidateSource, '
|
|
371
|
+
candidateSource: normalizeText(payload.candidateSource, 'active_memberships_online'),
|
|
424
372
|
candidateModel: payload.candidateModel && typeof payload.candidateModel === 'object' ? payload.candidateModel : {},
|
|
425
373
|
strategy: payload.strategy && typeof payload.strategy === 'object' ? payload.strategy : {},
|
|
426
374
|
limit: normalizeInteger(payload.limit, candidates.length),
|
|
@@ -449,31 +397,18 @@ function buildCandidateDeliverySummaryLine(candidateSummary = {}, index = 0) {
|
|
|
449
397
|
return `${index + 1}. ${candidateSummary.summary}`;
|
|
450
398
|
}
|
|
451
399
|
|
|
452
|
-
function
|
|
453
|
-
const
|
|
454
|
-
? detail.
|
|
400
|
+
function formatConversationOverview(detail = {}) {
|
|
401
|
+
const conversationOverview = detail.conversationOverview && typeof detail.conversationOverview === 'object'
|
|
402
|
+
? detail.conversationOverview
|
|
455
403
|
: {};
|
|
456
|
-
const mode = normalizeText(detail.
|
|
457
|
-
const maxTurns = normalizeInteger(sessionOverview.maxTurns, null);
|
|
404
|
+
const mode = normalizeText(detail.conversationMode || conversationOverview.mode, null);
|
|
458
405
|
const parts = [];
|
|
459
406
|
|
|
460
407
|
if (mode) parts.push(`${mode} mode`);
|
|
461
|
-
if (maxTurns != null) parts.push(`max ${maxTurns} turns`);
|
|
462
408
|
|
|
463
409
|
return parts.length > 0 ? parts.join(', ') : null;
|
|
464
410
|
}
|
|
465
411
|
|
|
466
|
-
function buildRaiseHandDirective(sessionOverview = {}) {
|
|
467
|
-
const mode = normalizeText(sessionOverview.raiseHandPolicy?.mode, null);
|
|
468
|
-
if (mode === 'dual_raise_hand') {
|
|
469
|
-
return 'When you are ready to conclude, include [[CLAWORLD_RAISE_HAND]] in your reply. The round closes once both agents raise hand.';
|
|
470
|
-
}
|
|
471
|
-
if (mode === 'single_raise_hand' || mode === 'either_raise_hand') {
|
|
472
|
-
return 'When you are ready to conclude, include [[CLAWORLD_RAISE_HAND]] in your reply to close the round.';
|
|
473
|
-
}
|
|
474
|
-
return null;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
412
|
export function buildWorldSessionStartupText(detail = {}) {
|
|
478
413
|
const normalizedDetail = normalizeWorldDetail(detail);
|
|
479
414
|
const worldId = normalizeText(normalizedDetail.worldId, null);
|
|
@@ -481,33 +416,29 @@ export function buildWorldSessionStartupText(detail = {}) {
|
|
|
481
416
|
|
|
482
417
|
const displayName = normalizeText(normalizedDetail.displayName, worldId);
|
|
483
418
|
const summary = normalizeText(normalizedDetail.summary, null);
|
|
484
|
-
const sessionSummary =
|
|
485
|
-
const
|
|
486
|
-
? normalizedDetail.
|
|
419
|
+
const sessionSummary = formatConversationOverview(normalizedDetail);
|
|
420
|
+
const conversationOverview = normalizedDetail.conversationOverview && typeof normalizedDetail.conversationOverview === 'object'
|
|
421
|
+
? normalizedDetail.conversationOverview
|
|
487
422
|
: {};
|
|
488
|
-
const
|
|
489
|
-
const
|
|
490
|
-
const convergenceText = normalizeText(sessionOverview.convergence?.text, null);
|
|
491
|
-
const raiseHandDirective = buildRaiseHandDirective(sessionOverview);
|
|
423
|
+
const openingText = normalizeText(conversationOverview.openingText, null);
|
|
424
|
+
const convergenceText = normalizeText(conversationOverview.convergence?.text, null);
|
|
492
425
|
const interactionRules = normalizeText(normalizedDetail.interactionRules, null);
|
|
493
426
|
const prohibitedRules = normalizeText(normalizedDetail.prohibitedRules, null);
|
|
494
427
|
const ratingRules = normalizeText(normalizedDetail.ratingRules, null);
|
|
495
428
|
|
|
496
429
|
const lines = [
|
|
497
|
-
'Internal Claworld world context for this
|
|
430
|
+
'Internal Claworld world context for this conversation.',
|
|
498
431
|
'Do not acknowledge, paraphrase, or announce this setup to the peer unless it is directly relevant to their message.',
|
|
499
432
|
`World: ${displayName} [${worldId}]`,
|
|
500
433
|
summary ? `Summary: ${summary}` : null,
|
|
501
434
|
sessionSummary ? `Session overview: ${sessionSummary}` : null,
|
|
502
|
-
raiseHandSummary ? `Completion rule: ${raiseHandSummary}` : null,
|
|
503
435
|
'Interruption handling: prefer reconnect/resume. Temporary silence or reconnect churn is not the normal way to close a round.',
|
|
504
436
|
openingText ? `Opening focus: ${openingText}` : null,
|
|
505
437
|
interactionRules ? `Interaction rules: ${interactionRules}` : null,
|
|
506
438
|
prohibitedRules ? `Prohibited rules: ${prohibitedRules}` : null,
|
|
507
439
|
ratingRules ? `Rating rules: ${ratingRules}` : null,
|
|
508
440
|
convergenceText ? `Convergence rule: ${convergenceText}` : null,
|
|
509
|
-
|
|
510
|
-
'Apply these world rules symmetrically when responding in this session.',
|
|
441
|
+
'Apply these world rules symmetrically when responding in this conversation.',
|
|
511
442
|
].filter(Boolean);
|
|
512
443
|
|
|
513
444
|
return lines.join('\n');
|
|
@@ -583,9 +514,8 @@ function buildSelectionRetryContract(status, selection, items = [], matches = []
|
|
|
583
514
|
export function buildWorldSelectionPrompt(worldDirectory = {}) {
|
|
584
515
|
const worldLines = Array.isArray(worldDirectory.items)
|
|
585
516
|
? worldDirectory.items.map((world, index) => (
|
|
586
|
-
`${index + 1}. ${world.displayName} [${world.worldId}]
|
|
587
|
-
+ ` (
|
|
588
|
-
+ ` matching: ${world.matchingMode}; session: ${world.sessionMode})`
|
|
517
|
+
`${index + 1}. ${world.displayName} [${world.worldId}]`
|
|
518
|
+
+ ` (required fields: ${world.requiredFieldCount}; hotness: ${normalizeInteger(world.hotness, 0)})`
|
|
589
519
|
))
|
|
590
520
|
: [];
|
|
591
521
|
|
|
@@ -661,10 +591,10 @@ export function resolveWorldSelection(worldDirectory = {}, selection = null) {
|
|
|
661
591
|
candidateWorlds: items,
|
|
662
592
|
orchestration: {
|
|
663
593
|
stage: 'post_setup_world_selected',
|
|
664
|
-
system: 'Confirm the resolved world choice before fetching detail and
|
|
594
|
+
system: 'Confirm the resolved world choice before fetching detail and collecting participantContextText for join_world.',
|
|
665
595
|
user: `I matched the user choice to ${selectedWorld.displayName} [${selectedWorld.worldId}]. Confirm that this is the world we will use next.`,
|
|
666
596
|
confirmation: `Confirmed world: ${selectedWorld.displayName} [${selectedWorld.worldId}].`,
|
|
667
|
-
followUp: 'Fetch the selected world detail, explain the
|
|
597
|
+
followUp: 'Fetch the selected world detail, explain the participant context requirement, and use join_world once participantContextText is available.',
|
|
668
598
|
},
|
|
669
599
|
};
|
|
670
600
|
}
|
|
@@ -683,246 +613,42 @@ function buildFieldStepPrompt(field = {}, index = 0, total = 1) {
|
|
|
683
613
|
|
|
684
614
|
export function buildRequiredFieldExplanation(worldDetail = {}) {
|
|
685
615
|
const detail = normalizeWorldDetail(worldDetail);
|
|
686
|
-
const
|
|
687
|
-
const
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
constraints: field.constraints,
|
|
701
|
-
}));
|
|
702
|
-
const optionalContextSummary = optionalFieldLabels.length > 0
|
|
703
|
-
? `Optional context you can add later: ${joinAsNaturalLanguage(optionalFieldLabels)}.`
|
|
704
|
-
: null;
|
|
705
|
-
const nextInstruction = steps[0]?.prompt || 'All required fields are already explained. You can move to join_world.';
|
|
616
|
+
const field = detail.requiredFields[0];
|
|
617
|
+
const summary = `To join ${detail.displayName}, I need one ${field.label} text.`;
|
|
618
|
+
const steps = [
|
|
619
|
+
{
|
|
620
|
+
step: 1,
|
|
621
|
+
fieldId: field.fieldId,
|
|
622
|
+
label: field.label,
|
|
623
|
+
prompt: buildFieldStepPrompt(field, 0, 1),
|
|
624
|
+
description: field.description,
|
|
625
|
+
examples: field.examples,
|
|
626
|
+
constraints: field.constraints,
|
|
627
|
+
},
|
|
628
|
+
];
|
|
629
|
+
const nextInstruction = steps[0].prompt;
|
|
706
630
|
|
|
707
631
|
return {
|
|
708
632
|
status: 'ready',
|
|
709
633
|
stage: 'post_setup_world_requirements',
|
|
710
634
|
worldId: detail.worldId,
|
|
711
635
|
displayName: detail.displayName,
|
|
712
|
-
requiredFieldCount:
|
|
713
|
-
optionalFieldCount:
|
|
636
|
+
requiredFieldCount: 1,
|
|
637
|
+
optionalFieldCount: 0,
|
|
714
638
|
summary,
|
|
715
639
|
steps,
|
|
716
640
|
hints: detail.hints,
|
|
717
|
-
optionalContextSummary,
|
|
718
641
|
nextAction: detail.nextAction,
|
|
719
642
|
orchestration: {
|
|
720
643
|
stage: 'post_setup_world_requirements',
|
|
721
|
-
system: 'Confirm the selected world, explain
|
|
644
|
+
system: 'Confirm the selected world, explain the participant context requirement in plain language, and use join_world once that text is available.',
|
|
722
645
|
confirmation: `Confirmed world: ${detail.displayName} [${detail.worldId}].`,
|
|
723
|
-
user: [summary, nextInstruction
|
|
724
|
-
followUp:
|
|
725
|
-
? `After the user answers ${steps[0].label}, continue with the remaining required fields in order, then call join_world.`
|
|
726
|
-
: (requiredFields.length === 1
|
|
727
|
-
? 'After the user answers the required field, call join_world.'
|
|
728
|
-
: 'No required fields remain. Call join_world now.'),
|
|
646
|
+
user: [summary, nextInstruction].filter(Boolean).join('\n\n'),
|
|
647
|
+
followUp: 'After the user provides participantContextText, call join_world.',
|
|
729
648
|
},
|
|
730
649
|
};
|
|
731
650
|
}
|
|
732
651
|
|
|
733
|
-
function buildFieldLookup(worldDetail = {}) {
|
|
734
|
-
const detail = normalizeWorldDetail(worldDetail);
|
|
735
|
-
return new Map(
|
|
736
|
-
[...detail.requiredFields, ...detail.optionalFields].map((field) => [field.fieldId, field]),
|
|
737
|
-
);
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
function selectPromptFields(joinCheck = {}, worldDetail = {}, maxFieldsPerStep = 1) {
|
|
741
|
-
if (joinCheck.accepted) return [];
|
|
742
|
-
|
|
743
|
-
const fieldLookup = buildFieldLookup(worldDetail);
|
|
744
|
-
const orderedMissingFields = Array.isArray(joinCheck.missingFieldGuidance?.orderedMissingFields)
|
|
745
|
-
&& joinCheck.missingFieldGuidance.orderedMissingFields.length > 0
|
|
746
|
-
? joinCheck.missingFieldGuidance.orderedMissingFields
|
|
747
|
-
: (Array.isArray(joinCheck.missingFields) ? joinCheck.missingFields : []);
|
|
748
|
-
|
|
749
|
-
return orderedMissingFields.slice(0, Math.max(1, maxFieldsPerStep)).map((field, index) => {
|
|
750
|
-
const detailField = fieldLookup.get(field.fieldId) || {};
|
|
751
|
-
return normalizeField(
|
|
752
|
-
{
|
|
753
|
-
...detailField,
|
|
754
|
-
...field,
|
|
755
|
-
description: normalizeText(field.description, detailField.description || null),
|
|
756
|
-
examples: Array.isArray(detailField.examples) ? detailField.examples : field.examples,
|
|
757
|
-
constraints: detailField.constraints || field.constraints,
|
|
758
|
-
},
|
|
759
|
-
index,
|
|
760
|
-
{ required: true },
|
|
761
|
-
);
|
|
762
|
-
});
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
function buildProfileFieldPrompt(field = {}, index = 0, total = 1) {
|
|
766
|
-
const examples = Array.isArray(field.examples) && field.examples.length > 0
|
|
767
|
-
? ` Example: ${field.examples.map((example) => quoteExample(example)).join(' or ')}.`
|
|
768
|
-
: '';
|
|
769
|
-
const description = sentenceCase(
|
|
770
|
-
field.description || `Provide ${field.label} so the world can evaluate the profile`,
|
|
771
|
-
'Provide this field so the world can evaluate the profile.',
|
|
772
|
-
);
|
|
773
|
-
const prefix = total > 1 ? `${index + 1}. ` : '';
|
|
774
|
-
|
|
775
|
-
return `${prefix}${field.label}. ${description}${examples}`;
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
function listProvidedRequiredFieldIds(worldDetail = {}, profile = {}, missingFieldIds = []) {
|
|
779
|
-
const missingSet = new Set(normalizeStringList(missingFieldIds));
|
|
780
|
-
return normalizeWorldDetail(worldDetail).requiredFields
|
|
781
|
-
.filter((field) => !missingSet.has(field.fieldId) && !isEmptyProfileValue(profile[field.fieldId]))
|
|
782
|
-
.map((field) => field.fieldId);
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
function buildProfileCollectionFollowUp(promptFields = []) {
|
|
786
|
-
if (promptFields.length === 0) {
|
|
787
|
-
return 'The current profile is already eligible. Continue with the next world step without restarting profile collection.';
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
const labels = promptFields.map((field) => field.label);
|
|
791
|
-
if (promptFields.length === 1) {
|
|
792
|
-
return `After the user answers ${labels[0]}, merge it into the saved profile draft and retry join_world before asking anything else.`;
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
return `After the user answers ${joinAsNaturalLanguage(labels)}, merge those fields into the saved profile draft and retry join_world before asking anything else.`;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
export function buildWorldProfileCollectionFlow({
|
|
799
|
-
worldDetail = {},
|
|
800
|
-
joinCheck = {},
|
|
801
|
-
profile = {},
|
|
802
|
-
maxFieldsPerStep = 1,
|
|
803
|
-
} = {}) {
|
|
804
|
-
const detail = normalizeWorldDetail(worldDetail);
|
|
805
|
-
const normalizedJoinCheck = normalizeJoinCheckResponse(joinCheck, {
|
|
806
|
-
worldId: detail.worldId,
|
|
807
|
-
profile,
|
|
808
|
-
});
|
|
809
|
-
const promptLimit = Math.max(1, normalizeInteger(maxFieldsPerStep, 1));
|
|
810
|
-
const promptFields = selectPromptFields(normalizedJoinCheck, detail, promptLimit);
|
|
811
|
-
const promptFieldIds = promptFields.map((field) => field.fieldId);
|
|
812
|
-
const providedRequiredFieldIds = listProvidedRequiredFieldIds(
|
|
813
|
-
detail,
|
|
814
|
-
normalizedJoinCheck.normalizedProfile,
|
|
815
|
-
normalizedJoinCheck.missingFieldGuidance.orderedMissingFieldIds,
|
|
816
|
-
);
|
|
817
|
-
const providedRequiredLabels = detail.requiredFields
|
|
818
|
-
.filter((field) => providedRequiredFieldIds.includes(field.fieldId))
|
|
819
|
-
.map((field) => field.label);
|
|
820
|
-
const optionalFieldLabels = detail.optionalFields.map((field) => field.label);
|
|
821
|
-
const remainingCount = normalizedJoinCheck.missingFieldGuidance.remainingRequiredFieldCount;
|
|
822
|
-
const summary = normalizedJoinCheck.accepted
|
|
823
|
-
? `All required fields for ${detail.displayName} are currently present.`
|
|
824
|
-
: `${remainingCount} required field${remainingCount === 1 ? '' : 's'} still need to be collected for ${detail.displayName}.`;
|
|
825
|
-
const savedSummary = providedRequiredLabels.length > 0
|
|
826
|
-
? `Already saved required fields: ${joinAsNaturalLanguage(providedRequiredLabels)}.`
|
|
827
|
-
: 'No required fields are saved yet.';
|
|
828
|
-
const promptSummary = promptFields.length === 0
|
|
829
|
-
? null
|
|
830
|
-
: (promptFields.length === 1
|
|
831
|
-
? 'Ask the user for this required field next:'
|
|
832
|
-
: `Ask the user for these ${promptFields.length} required fields next, and accept them in one reply if convenient:`);
|
|
833
|
-
const promptBody = promptFields.length === 0
|
|
834
|
-
? null
|
|
835
|
-
: promptFields.map((field, index) => buildProfileFieldPrompt(field, index, promptFields.length)).join('\n');
|
|
836
|
-
const optionalContextSummary = optionalFieldLabels.length > 0
|
|
837
|
-
? `Optional context the user can add or edit later: ${joinAsNaturalLanguage(optionalFieldLabels)}.`
|
|
838
|
-
: null;
|
|
839
|
-
const revalidationMode = normalizedJoinCheck.accepted
|
|
840
|
-
? 'complete'
|
|
841
|
-
: (promptFields.length > 1 ? 'after_current_batch' : 'after_each_reply');
|
|
842
|
-
|
|
843
|
-
return {
|
|
844
|
-
accepted: normalizedJoinCheck.accepted,
|
|
845
|
-
status: normalizedJoinCheck.status,
|
|
846
|
-
source: 'product_shell',
|
|
847
|
-
worldId: detail.worldId,
|
|
848
|
-
displayName: detail.displayName,
|
|
849
|
-
profile: normalizedJoinCheck.normalizedProfile,
|
|
850
|
-
promptFields,
|
|
851
|
-
promptFieldIds,
|
|
852
|
-
providedRequiredFieldIds,
|
|
853
|
-
remainingRequiredFieldCount: remainingCount,
|
|
854
|
-
nextAction: normalizedJoinCheck.nextAction,
|
|
855
|
-
revalidationCheckpoint: {
|
|
856
|
-
mode: revalidationMode,
|
|
857
|
-
promptFieldIds,
|
|
858
|
-
},
|
|
859
|
-
orchestration: {
|
|
860
|
-
stage: 'post_setup_world_profile_collection',
|
|
861
|
-
system:
|
|
862
|
-
'Use the backend join_world unmet-requirement guidance as the source of truth for the next required field prompt. After every user reply or the current batch checkpoint, merge the updates into the saved profile draft and retry join_world.',
|
|
863
|
-
confirmation: `Confirmed world: ${detail.displayName} [${detail.worldId}].`,
|
|
864
|
-
user: [summary, savedSummary, promptSummary, promptBody, optionalContextSummary].filter(Boolean).join('\n\n'),
|
|
865
|
-
followUp: buildProfileCollectionFollowUp(promptFields),
|
|
866
|
-
},
|
|
867
|
-
};
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
export function buildWorldJoinOutcomeOrchestration({ worldDetail = {}, joinResult = {} } = {}) {
|
|
871
|
-
const detail = normalizeWorldDetail(worldDetail);
|
|
872
|
-
const membershipStatus = normalizeText(
|
|
873
|
-
joinResult.membershipStatus || joinResult.membership?.status,
|
|
874
|
-
'unknown',
|
|
875
|
-
);
|
|
876
|
-
const joinSummary = joinResult.nextStageSummary?.summary
|
|
877
|
-
? sentenceCase(joinResult.nextStageSummary.summary, '')
|
|
878
|
-
: null;
|
|
879
|
-
|
|
880
|
-
return {
|
|
881
|
-
stage: 'world_join_result',
|
|
882
|
-
status: membershipStatus,
|
|
883
|
-
system:
|
|
884
|
-
'The backend already resolved the world join result. Reflect the authoritative membership outcome before any candidate-feed review, request_chat, or live-session follow-up.',
|
|
885
|
-
confirmation: `World membership in ${detail.displayName} [${detail.worldId}] is ${membershipStatus}.`,
|
|
886
|
-
user: membershipStatus === 'active'
|
|
887
|
-
? ['Joined ' + detail.displayName + ' successfully. World membership is active.', joinSummary].filter(Boolean).join(' ')
|
|
888
|
-
: `The join result for ${detail.displayName} is ${membershipStatus}.`,
|
|
889
|
-
followUp: membershipStatus === 'active'
|
|
890
|
-
? 'Use the backend-authored candidate-feed and candidate-delivery payloads for the next world step, and keep request_chat as the canonical conversation-start action.'
|
|
891
|
-
: 'Do not continue to world-member-only follow-up until membership becomes active.',
|
|
892
|
-
};
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
export function buildResolvedWorldSelectionOrchestration({
|
|
896
|
-
selection = null,
|
|
897
|
-
profileCollectionFlow = null,
|
|
898
|
-
} = {}) {
|
|
899
|
-
const selectionOrchestration = selection?.orchestration && typeof selection.orchestration === 'object' && !Array.isArray(selection.orchestration)
|
|
900
|
-
? selection.orchestration
|
|
901
|
-
: null;
|
|
902
|
-
const profileOrchestration = profileCollectionFlow?.orchestration && typeof profileCollectionFlow.orchestration === 'object' && !Array.isArray(profileCollectionFlow.orchestration)
|
|
903
|
-
? profileCollectionFlow.orchestration
|
|
904
|
-
: null;
|
|
905
|
-
|
|
906
|
-
if (!profileOrchestration) return selectionOrchestration;
|
|
907
|
-
|
|
908
|
-
return {
|
|
909
|
-
stage: normalizeText(profileOrchestration.stage, normalizeText(selectionOrchestration?.stage, null)),
|
|
910
|
-
system: normalizeText(profileOrchestration.system, normalizeText(selectionOrchestration?.system, null)),
|
|
911
|
-
confirmation: normalizeText(
|
|
912
|
-
profileOrchestration.confirmation,
|
|
913
|
-
normalizeText(selectionOrchestration?.confirmation, null),
|
|
914
|
-
),
|
|
915
|
-
user: [
|
|
916
|
-
normalizeText(profileOrchestration.confirmation, null),
|
|
917
|
-
normalizeText(profileOrchestration.user, null),
|
|
918
|
-
].filter(Boolean).join('\n\n'),
|
|
919
|
-
followUp: normalizeText(
|
|
920
|
-
profileOrchestration.followUp,
|
|
921
|
-
normalizeText(selectionOrchestration?.followUp, null),
|
|
922
|
-
),
|
|
923
|
-
};
|
|
924
|
-
}
|
|
925
|
-
|
|
926
652
|
export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail = null, limit = null } = {}) {
|
|
927
653
|
const detail = worldDetail ? normalizeWorldDetail(worldDetail) : null;
|
|
928
654
|
const normalizedFeed = normalizeCandidateFeedResponse(candidateFeed, {
|
|
@@ -949,6 +675,7 @@ export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail
|
|
|
949
675
|
.map((signal) => sentenceCase(signal.summary, ''))
|
|
950
676
|
.filter(Boolean);
|
|
951
677
|
const deliveryReasonSummary = sentenceCase(candidate.deliveryReason.summary, '');
|
|
678
|
+
const availabilitySummary = candidate.online === true ? 'Online now.' : 'Currently offline.';
|
|
952
679
|
const scoreSummary = candidate.score == null
|
|
953
680
|
? null
|
|
954
681
|
: `Score ${candidate.score}${candidate.rank == null ? '' : `, rank ${candidate.rank}`}.`;
|
|
@@ -958,12 +685,14 @@ export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail
|
|
|
958
685
|
optionalFieldSummary.length > 0 ? `Optional context: ${optionalFieldSummary.join('; ')}.` : null,
|
|
959
686
|
compatibilitySummary.length > 0 ? compatibilitySummary.join(' ') : null,
|
|
960
687
|
deliveryReasonSummary || null,
|
|
688
|
+
availabilitySummary,
|
|
961
689
|
scoreSummary,
|
|
962
690
|
].filter(Boolean).join(' ');
|
|
963
691
|
|
|
964
692
|
return {
|
|
965
693
|
candidateId: candidate.candidateId,
|
|
966
694
|
sourceMembershipId: candidate.sourceMembershipId,
|
|
695
|
+
online: candidate.online === true,
|
|
967
696
|
targetAgentId: candidate.targetAgentId,
|
|
968
697
|
requestChat: candidate.requestChat,
|
|
969
698
|
displayName: name,
|
|
@@ -982,11 +711,11 @@ export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail
|
|
|
982
711
|
const totalCandidateCount = Math.max(normalizedFeed.totalCandidates, deliveredCandidateCount);
|
|
983
712
|
const remainingCandidateCount = Math.max(totalCandidateCount - deliveredCandidateCount, 0);
|
|
984
713
|
const heading = deliveredCandidateCount > 0
|
|
985
|
-
? `${displayName} has ${deliveredCandidateCount} candidate profile ${deliveredCandidateCount === 1 ? 'summary' : 'summaries'} ready for review now.`
|
|
986
|
-
: `No candidate profile summaries are ready for review in ${displayName} yet.`;
|
|
714
|
+
? `${displayName} has ${deliveredCandidateCount} online candidate profile ${deliveredCandidateCount === 1 ? 'summary' : 'summaries'} ready for review now.`
|
|
715
|
+
: `No online candidate profile summaries are ready for review in ${displayName} yet.`;
|
|
987
716
|
const promptBody = deliveredCandidateCount > 0
|
|
988
717
|
? candidateSummaries.map((summary, index) => buildCandidateDeliverySummaryLine(summary, index)).join('\n\n')
|
|
989
|
-
: 'No candidates are currently available from the active-membership feed.';
|
|
718
|
+
: 'No online candidates are currently available from the active-membership feed.';
|
|
990
719
|
|
|
991
720
|
return {
|
|
992
721
|
worldId: normalizedFeed.worldId,
|
|
@@ -53,8 +53,9 @@ export function registerFeedbackRoutes(app, { store, feedbackService }) {
|
|
|
53
53
|
details: req.body?.details,
|
|
54
54
|
reproductionSteps: req.body?.reproductionSteps,
|
|
55
55
|
worldId: req.body?.worldId,
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
conversationKey: req.body?.conversationKey,
|
|
57
|
+
turnId: req.body?.turnId,
|
|
58
|
+
deliveryId: req.body?.deliveryId,
|
|
58
59
|
targetAgentId: req.body?.targetAgentId,
|
|
59
60
|
targetAgentCode: req.body?.targetAgentCode,
|
|
60
61
|
tags: req.body?.tags,
|
|
@@ -77,7 +78,7 @@ export function registerFeedbackRoutes(app, { store, feedbackService }) {
|
|
|
77
78
|
accountId: req.query.accountId,
|
|
78
79
|
reporterAgentId: req.query.reporterAgentId,
|
|
79
80
|
worldId: req.query.worldId,
|
|
80
|
-
|
|
81
|
+
conversationKey: req.query.conversationKey,
|
|
81
82
|
source: req.query.source,
|
|
82
83
|
page: req.query.page,
|
|
83
84
|
limit: req.query.limit,
|
|
@@ -122,8 +122,9 @@ function projectFeedback(feedback = {}) {
|
|
|
122
122
|
context: feedback.context && typeof feedback.context === 'object'
|
|
123
123
|
? {
|
|
124
124
|
worldId: feedback.context.worldId || null,
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
conversationKey: feedback.context.conversationKey || null,
|
|
126
|
+
turnId: feedback.context.turnId || null,
|
|
127
|
+
deliveryId: feedback.context.deliveryId || null,
|
|
127
128
|
targetAgentId: feedback.context.targetAgentId || null,
|
|
128
129
|
targetAgentCode: feedback.context.targetAgentCode || null,
|
|
129
130
|
tags: Array.isArray(feedback.context.tags) ? feedback.context.tags : [],
|
|
@@ -133,8 +134,9 @@ function projectFeedback(feedback = {}) {
|
|
|
133
134
|
}
|
|
134
135
|
: {
|
|
135
136
|
worldId: null,
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
conversationKey: null,
|
|
138
|
+
turnId: null,
|
|
139
|
+
deliveryId: null,
|
|
138
140
|
targetAgentId: null,
|
|
139
141
|
targetAgentCode: null,
|
|
140
142
|
tags: [],
|
|
@@ -181,8 +183,9 @@ export function createFeedbackService({ store } = {}) {
|
|
|
181
183
|
},
|
|
182
184
|
context: {
|
|
183
185
|
worldId: normalizeText(input.worldId, normalizeText(context.worldId, null)),
|
|
184
|
-
|
|
185
|
-
|
|
186
|
+
conversationKey: normalizeText(input.conversationKey, normalizeText(context.conversationKey, null)),
|
|
187
|
+
turnId: normalizeText(input.turnId, normalizeText(context.turnId, null)),
|
|
188
|
+
deliveryId: normalizeText(input.deliveryId, normalizeText(context.deliveryId, null)),
|
|
186
189
|
targetAgentId: normalizeText(input.targetAgentId, normalizeText(context.targetAgentId, null)),
|
|
187
190
|
targetAgentCode: normalizeText(input.targetAgentCode, normalizeText(context.targetAgentCode, null)),
|
|
188
191
|
tags: normalizeStringList(input.tags || context.tags),
|
|
@@ -223,7 +226,7 @@ export function createFeedbackService({ store } = {}) {
|
|
|
223
226
|
accountId: normalizeText(filters.accountId, null),
|
|
224
227
|
reporterAgentId: normalizeText(filters.reporterAgentId, null),
|
|
225
228
|
worldId: normalizeText(filters.worldId, null),
|
|
226
|
-
|
|
229
|
+
conversationKey: normalizeText(filters.conversationKey, null),
|
|
227
230
|
source: normalizeText(filters.source, null),
|
|
228
231
|
page,
|
|
229
232
|
limit,
|
|
@@ -245,7 +248,7 @@ export function createFeedbackService({ store } = {}) {
|
|
|
245
248
|
accountId: normalizeText(filters.accountId, null),
|
|
246
249
|
reporterAgentId: normalizeText(filters.reporterAgentId, null),
|
|
247
250
|
worldId: normalizeText(filters.worldId, null),
|
|
248
|
-
|
|
251
|
+
conversationKey: normalizeText(filters.conversationKey, null),
|
|
249
252
|
source: normalizeText(filters.source, null),
|
|
250
253
|
},
|
|
251
254
|
};
|