@xfxstudio/claworld 0.1.4 → 0.1.5
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/openclaw.plugin.json +3 -3
- package/package.json +1 -1
- package/skills/claworld-help/SKILL.md +100 -42
- package/skills/claworld-join-and-chat/SKILL.md +161 -47
- package/skills/claworld-manage-worlds/SKILL.md +88 -188
- package/src/openclaw/installer/cli.js +0 -11
- package/src/openclaw/installer/core.js +1 -8
- package/src/openclaw/plugin/claworld-channel-plugin.js +38 -2
- package/src/openclaw/plugin/config-schema.js +1 -1
- package/src/openclaw/plugin/managed-config.js +30 -29
- package/src/openclaw/plugin/register.js +4 -4
- package/src/openclaw/plugin/relay-client.js +1 -1
- package/src/openclaw/runtime/product-shell-helper.js +2 -1
- package/src/openclaw/runtime/tool-contracts.js +3 -0
- package/src/product-shell/catalog/default-world-catalog.js +6 -6
- package/src/product-shell/contracts/candidate-feed.js +14 -4
- package/src/product-shell/contracts/world-manifest.js +19 -1
- package/src/product-shell/contracts/world-orchestration.js +8 -4
- package/src/product-shell/index.js +1 -1
- package/src/product-shell/matching/matchmaking-service.js +39 -5
- package/src/product-shell/worlds/world-admin-service.js +2 -2
|
@@ -107,12 +107,21 @@ function findManagedAccountEntry(config = {}, accountId) {
|
|
|
107
107
|
return {};
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
const MANAGED_LEGACY_BUNDLED_SKILL_NAMES = Object.freeze([
|
|
111
|
+
'claworld-join-and-chat',
|
|
112
|
+
'claworld-manage-worlds',
|
|
113
|
+
'claworld-help',
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
function hasOnlyManagedBundledSkills(value) {
|
|
117
|
+
if (!Array.isArray(value) || value.length === 0) return false;
|
|
118
|
+
return value.every((skillName) => MANAGED_LEGACY_BUNDLED_SKILL_NAMES.includes(skillName));
|
|
119
|
+
}
|
|
120
|
+
|
|
110
121
|
function buildManagedAgentEntry(options = {}) {
|
|
111
|
-
const managedSkills = resolveManagedAgentSkills({ toolProfile: options.toolProfile });
|
|
112
122
|
return {
|
|
113
123
|
id: options.agentId,
|
|
114
124
|
workspace: options.workspace,
|
|
115
|
-
...(managedSkills === undefined ? {} : { skills: managedSkills }),
|
|
116
125
|
...(options.agentDirExplicit && options.agentDir ? { agentDir: options.agentDir } : {}),
|
|
117
126
|
};
|
|
118
127
|
}
|
|
@@ -123,7 +132,6 @@ function buildManagedAccountEntry(options = {}) {
|
|
|
123
132
|
serverUrl: options.serverUrl,
|
|
124
133
|
apiKey: options.apiKey,
|
|
125
134
|
accountId: options.accountId,
|
|
126
|
-
toolProfile: options.toolProfile,
|
|
127
135
|
name: normalizeText(options.name, normalizeText(options.displayName, null)),
|
|
128
136
|
approval: {
|
|
129
137
|
mode: normalizeChatRequestApprovalMode(options.approvalMode, DEFAULT_CLAWORLD_APPROVAL_MODE),
|
|
@@ -162,7 +170,6 @@ function buildMergedAccountEntry(existingAccount = {}, options = {}) {
|
|
|
162
170
|
serverUrl: options.serverUrl,
|
|
163
171
|
apiKey: options.apiKey,
|
|
164
172
|
accountId: options.accountId,
|
|
165
|
-
toolProfile: options.toolProfile,
|
|
166
173
|
name: normalizeText(options.name, normalizeText(existingAccount.name, normalizeText(options.displayName, null))),
|
|
167
174
|
approval: {
|
|
168
175
|
...existingApproval,
|
|
@@ -179,6 +186,7 @@ function buildMergedAccountEntry(existingAccount = {}, options = {}) {
|
|
|
179
186
|
? { relay: existingRelay }
|
|
180
187
|
: {}),
|
|
181
188
|
};
|
|
189
|
+
delete merged.toolProfile;
|
|
182
190
|
|
|
183
191
|
if (options.appToken) {
|
|
184
192
|
return {
|
|
@@ -421,10 +429,6 @@ export function resolveClaworldManagedRuntimeOptions({
|
|
|
421
429
|
normalizeText(input.name, resolveDefaultManagedDisplayName(resolvedAccountId)),
|
|
422
430
|
);
|
|
423
431
|
const name = normalizeText(overrides.name, displayName);
|
|
424
|
-
const explicitToolProfile = normalizeText(
|
|
425
|
-
overrides.toolProfile,
|
|
426
|
-
normalizeText(input.toolProfile, null),
|
|
427
|
-
);
|
|
428
432
|
const existingRegistrationAgentCode = normalizeRegistrationAgentCode(
|
|
429
433
|
existingAccount?.registration?.agentCode,
|
|
430
434
|
normalizeRegistrationAgentCode(existingAccount?.localAgent?.agentCode, null),
|
|
@@ -461,11 +465,6 @@ export function resolveClaworldManagedRuntimeOptions({
|
|
|
461
465
|
displayName,
|
|
462
466
|
name,
|
|
463
467
|
defaultToAddress: normalizeText(overrides.defaultToAddress, null),
|
|
464
|
-
toolProfile: resolveManagedToolProfile({
|
|
465
|
-
cfg,
|
|
466
|
-
existingAccount,
|
|
467
|
-
explicitToolProfile,
|
|
468
|
-
}),
|
|
469
468
|
approvalMode,
|
|
470
469
|
sessionDmScope: normalizeText(
|
|
471
470
|
overrides.sessionDmScope,
|
|
@@ -481,8 +480,6 @@ export function resolveClaworldManagedRuntimeOptions({
|
|
|
481
480
|
|
|
482
481
|
export function applyClaworldManagedRuntimeConfig(inputConfig = {}, options = {}) {
|
|
483
482
|
const config = JSON.parse(JSON.stringify(ensureObject(inputConfig)));
|
|
484
|
-
const toolProfile = normalizeClaworldToolProfile(options.toolProfile);
|
|
485
|
-
const toolNames = resolveToolNames({ toolProfile });
|
|
486
483
|
const summary = [];
|
|
487
484
|
const replaceManagedRuntime = options.replaceManagedRuntime !== false;
|
|
488
485
|
const preserveDefaultAccount = options.preserveDefaultAccount === true;
|
|
@@ -492,21 +489,28 @@ export function applyClaworldManagedRuntimeConfig(inputConfig = {}, options = {}
|
|
|
492
489
|
throw new Error('claworld registration agentCode is required when appToken is absent');
|
|
493
490
|
}
|
|
494
491
|
|
|
495
|
-
config.tools = ensureObject(config.tools);
|
|
496
492
|
const removedManagedToolNames = new Set([
|
|
493
|
+
...CLAWORLD_PUBLIC_TOOL_NAMES,
|
|
497
494
|
...CLAWORLD_COMPATIBILITY_TOOL_NAMES,
|
|
498
495
|
...CLAWORLD_RETIRED_PUBLIC_TOOL_NAMES,
|
|
499
496
|
]);
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
497
|
+
if (inputConfig?.tools && typeof inputConfig.tools === 'object') {
|
|
498
|
+
config.tools = ensureObject(config.tools);
|
|
499
|
+
const existingAllow = asStringArray(config.tools.allow);
|
|
500
|
+
const filteredAllow = existingAllow.filter((toolName) => !removedManagedToolNames.has(toolName));
|
|
501
|
+
const removedManagedTools = existingAllow.filter((toolName) => removedManagedToolNames.has(toolName));
|
|
502
|
+
if (removedManagedTools.length > 0) {
|
|
503
|
+
if (filteredAllow.length > 0) {
|
|
504
|
+
config.tools.allow = uniqueStrings(filteredAllow);
|
|
505
|
+
} else {
|
|
506
|
+
delete config.tools.allow;
|
|
507
|
+
}
|
|
508
|
+
summary.push(`tools.allow removed managed claworld entries (${removedManagedTools.join(',')})`);
|
|
509
|
+
}
|
|
510
|
+
if (Object.keys(config.tools).length === 0) {
|
|
511
|
+
delete config.tools;
|
|
512
|
+
}
|
|
508
513
|
}
|
|
509
|
-
summary.push(`tools.allow updated for ${toolProfile} profile (${describeToolAllowEntries(toolNames)})`);
|
|
510
514
|
|
|
511
515
|
config.session = ensureObject(config.session);
|
|
512
516
|
if (!Object.prototype.hasOwnProperty.call(config.session, 'dmScope')) {
|
|
@@ -533,7 +537,6 @@ export function applyClaworldManagedRuntimeConfig(inputConfig = {}, options = {}
|
|
|
533
537
|
if (agentIndex >= 0) {
|
|
534
538
|
const existingAgent = ensureObject(existingAgentList[agentIndex]);
|
|
535
539
|
const existingAgentDir = normalizeText(existingAgent.agentDir, null);
|
|
536
|
-
const managedSkills = resolveManagedAgentSkills({ toolProfile: options.toolProfile });
|
|
537
540
|
const keepExistingAgentDir = Boolean(
|
|
538
541
|
existingAgentDir
|
|
539
542
|
&& (
|
|
@@ -546,12 +549,11 @@ export function applyClaworldManagedRuntimeConfig(inputConfig = {}, options = {}
|
|
|
546
549
|
id: options.agentId,
|
|
547
550
|
workspace: normalizeText(existingAgent.workspace, options.workspace),
|
|
548
551
|
...(keepExistingAgentDir ? { agentDir: existingAgentDir } : {}),
|
|
549
|
-
...(managedSkills === undefined ? {} : { skills: managedSkills }),
|
|
550
552
|
};
|
|
551
553
|
if (!keepExistingAgentDir) {
|
|
552
554
|
delete nextAgentEntry.agentDir;
|
|
553
555
|
}
|
|
554
|
-
if (
|
|
556
|
+
if (hasOnlyManagedBundledSkills(existingAgent.skills)) {
|
|
555
557
|
delete nextAgentEntry.skills;
|
|
556
558
|
}
|
|
557
559
|
existingAgentList[agentIndex] = nextAgentEntry;
|
|
@@ -619,7 +621,6 @@ export function applyClaworldManagedRuntimeConfig(inputConfig = {}, options = {}
|
|
|
619
621
|
|
|
620
622
|
return {
|
|
621
623
|
config,
|
|
622
|
-
toolNames,
|
|
623
624
|
summary,
|
|
624
625
|
canonicalAgentCode: canonicalRelayAgentCode(
|
|
625
626
|
options.registrationAgentCode,
|
|
@@ -401,13 +401,13 @@ function buildRegisteredTools(api, plugin) {
|
|
|
401
401
|
{
|
|
402
402
|
name: 'claworld_join_world',
|
|
403
403
|
label: 'Claworld Join World',
|
|
404
|
-
description: 'Canonical world-entry tool. Retry this same tool with profileDraft plus profileUpdate until the backend stops returning needs_profile; on success it returns candidate-review and request_chat follow-up payloads.',
|
|
404
|
+
description: 'Canonical world-entry tool. Retry this same tool with profileDraft plus profileUpdate until the backend stops returning needs_profile; on success it returns online candidate-review and request_chat follow-up payloads.',
|
|
405
405
|
metadata: buildToolMetadata({
|
|
406
406
|
category: 'world_join',
|
|
407
407
|
usageNotes: [
|
|
408
408
|
'This is the only public join entrypoint for the default flow.',
|
|
409
409
|
'When status is needs_profile, merge the user reply into profileDraft and retry this same tool.',
|
|
410
|
-
'When status is joined, use
|
|
410
|
+
'When status is joined, read candidateFeed/candidateDelivery online state, then use requestChatAction and move to claworld_request_chat.',
|
|
411
411
|
],
|
|
412
412
|
examples: [
|
|
413
413
|
{
|
|
@@ -435,7 +435,7 @@ function buildRegisteredTools(api, plugin) {
|
|
|
435
435
|
interests: ['running', 'climbing'],
|
|
436
436
|
},
|
|
437
437
|
},
|
|
438
|
-
outcome: 'Returns joined plus candidateDelivery and requestChatAction.',
|
|
438
|
+
outcome: 'Returns joined plus online candidateDelivery and requestChatAction.',
|
|
439
439
|
},
|
|
440
440
|
],
|
|
441
441
|
}),
|
|
@@ -1195,7 +1195,7 @@ export function registerClaworldPluginFull(api, plugin) {
|
|
|
1195
1195
|
}
|
|
1196
1196
|
if (typeof api.registerTool === 'function') {
|
|
1197
1197
|
for (const tool of buildRegisteredTools(api, plugin)) {
|
|
1198
|
-
api.registerTool(tool
|
|
1198
|
+
api.registerTool(tool);
|
|
1199
1199
|
}
|
|
1200
1200
|
}
|
|
1201
1201
|
return plugin;
|
|
@@ -422,6 +422,7 @@ function normalizeCandidate(candidate = {}, index = 0) {
|
|
|
422
422
|
worldId: normalizeText(candidate.worldId, 'unknown-world'),
|
|
423
423
|
targetAgentId: normalizeText(candidate.targetAgentId, null),
|
|
424
424
|
sourceMembershipId: normalizeText(candidate.sourceMembershipId, null),
|
|
425
|
+
online: candidate.online === true,
|
|
425
426
|
targetAgentId,
|
|
426
427
|
requestChat,
|
|
427
428
|
profileSummary: normalizeCandidateProfileSummary(candidate.profileSummary),
|
|
@@ -457,7 +458,7 @@ function normalizeCandidateFeedResponse(payload = {}, { worldId = null, agentId
|
|
|
457
458
|
candidateDelivery: payload.candidateDelivery && typeof payload.candidateDelivery === 'object'
|
|
458
459
|
? payload.candidateDelivery
|
|
459
460
|
: null,
|
|
460
|
-
candidateSource: normalizeText(payload.candidateSource, '
|
|
461
|
+
candidateSource: normalizeText(payload.candidateSource, 'active_memberships_online'),
|
|
461
462
|
candidateModel: payload.candidateModel && typeof payload.candidateModel === 'object' ? payload.candidateModel : {},
|
|
462
463
|
strategy: payload.strategy && typeof payload.strategy === 'object' ? payload.strategy : {},
|
|
463
464
|
limit: normalizeInteger(payload.limit, candidates.length),
|
|
@@ -230,6 +230,7 @@ function projectToolCandidateDeliverySummary(
|
|
|
230
230
|
sourceMembershipId: normalizeText(summary.sourceMembershipId, null),
|
|
231
231
|
displayName: normalizeText(summary.displayName, null),
|
|
232
232
|
headline: normalizeText(summary.headline, null),
|
|
233
|
+
online: summary.online === true,
|
|
233
234
|
rank: normalizeOptionalInteger(summary.rank, null),
|
|
234
235
|
score: normalizeOptionalInteger(summary.score, null),
|
|
235
236
|
targetAgentId: normalizeText(summary.targetAgentId, summary.requestChat?.targetAgentId || null),
|
|
@@ -383,6 +384,7 @@ function projectToolCandidateSummary(summary = {}, index = 0) {
|
|
|
383
384
|
targetAgentId: normalizeText(summary.targetAgentId, null),
|
|
384
385
|
displayName: normalizeText(summary.displayName, `Candidate ${index + 1}`),
|
|
385
386
|
headline: normalizeText(summary.headline, null),
|
|
387
|
+
online: summary.online === true,
|
|
386
388
|
rank: normalizeInteger(summary.rank, 0) || null,
|
|
387
389
|
score: normalizeInteger(summary.score, 0) || null,
|
|
388
390
|
summary: normalizeText(summary.summary, null),
|
|
@@ -405,6 +407,7 @@ function projectToolCandidateFeed(joinResult = {}) {
|
|
|
405
407
|
targetAgentId: candidate.targetAgentId,
|
|
406
408
|
displayName: candidate.profileSummary?.displayName,
|
|
407
409
|
headline: candidate.profileSummary?.headline,
|
|
410
|
+
online: candidate.online === true,
|
|
408
411
|
rank: candidate.rank,
|
|
409
412
|
score: candidate.score,
|
|
410
413
|
summary: normalizeText(candidate.deliveryReason?.summary, null),
|
|
@@ -83,8 +83,8 @@ export const DEFAULT_WORLD_MANIFESTS = Object.freeze([
|
|
|
83
83
|
mode: 'scored_push',
|
|
84
84
|
cadence: 'periodic',
|
|
85
85
|
strategySummary:
|
|
86
|
-
'Score active memberships by intent, location, and interests, deliver candidate summaries first, and route live contact through request_chat after review.',
|
|
87
|
-
candidateSources: ['
|
|
86
|
+
'Score active online memberships by intent, location, and interests, deliver candidate summaries first, and route live contact through request_chat after review.',
|
|
87
|
+
candidateSources: ['active_memberships_online'],
|
|
88
88
|
},
|
|
89
89
|
sessionTemplate: {
|
|
90
90
|
mode: 'a2a',
|
|
@@ -179,8 +179,8 @@ export const DEFAULT_WORLD_MANIFESTS = Object.freeze([
|
|
|
179
179
|
mode: 'intent_filter',
|
|
180
180
|
cadence: 'on_demand',
|
|
181
181
|
strategySummary:
|
|
182
|
-
'Filter by capability overlap, deliver candidate summaries first, and let members request_chat before negotiating fit in a short session.',
|
|
183
|
-
candidateSources: ['
|
|
182
|
+
'Filter active online world members by capability overlap, deliver candidate summaries first, and let members request_chat before negotiating fit in a short session.',
|
|
183
|
+
candidateSources: ['active_memberships_online'],
|
|
184
184
|
},
|
|
185
185
|
sessionTemplate: {
|
|
186
186
|
mode: 'a2a',
|
|
@@ -273,8 +273,8 @@ export const DEFAULT_WORLD_MANIFESTS = Object.freeze([
|
|
|
273
273
|
mode: 'profile_overlap',
|
|
274
274
|
cadence: 'periodic',
|
|
275
275
|
strategySummary:
|
|
276
|
-
'Use target role, experience summary, and location/work mode as the first-pass scoring surface, deliver candidate summaries, and route contact through request_chat after review.',
|
|
277
|
-
candidateSources: ['
|
|
276
|
+
'Use active online memberships plus target role, experience summary, and location/work mode as the first-pass scoring surface, deliver candidate summaries, and route contact through request_chat after review.',
|
|
277
|
+
candidateSources: ['active_memberships_online'],
|
|
278
278
|
},
|
|
279
279
|
sessionTemplate: {
|
|
280
280
|
mode: 'a2a',
|
|
@@ -7,6 +7,7 @@ export const CANDIDATE_OBJECT_FIELDS = Object.freeze([
|
|
|
7
7
|
'worldId',
|
|
8
8
|
'targetAgentId',
|
|
9
9
|
'sourceMembershipId',
|
|
10
|
+
'online',
|
|
10
11
|
'requestChat',
|
|
11
12
|
'profileSummary',
|
|
12
13
|
'compatibilitySignals',
|
|
@@ -180,7 +181,7 @@ function buildCompatibilitySignals(world, viewerProfile = {}, candidateProfile =
|
|
|
180
181
|
type: 'world_ready',
|
|
181
182
|
fieldIds: [],
|
|
182
183
|
score: 0.05,
|
|
183
|
-
summary: 'Candidate
|
|
184
|
+
summary: 'Candidate is online with an active world membership and is ready for agent review before live session handoff.',
|
|
184
185
|
}),
|
|
185
186
|
];
|
|
186
187
|
}
|
|
@@ -192,7 +193,7 @@ function buildDeliveryReason(signals = []) {
|
|
|
192
193
|
return {
|
|
193
194
|
code: 'world_membership_ready',
|
|
194
195
|
matchedFieldIds: [],
|
|
195
|
-
summary: 'Delivered for manual review because the candidate is active in this world, even though no direct profile overlap signal was detected yet.',
|
|
196
|
+
summary: 'Delivered for manual review because the candidate is online and active in this world, even though no direct profile overlap signal was detected yet.',
|
|
196
197
|
};
|
|
197
198
|
}
|
|
198
199
|
|
|
@@ -264,7 +265,7 @@ export function projectCandidateFeedModel(world) {
|
|
|
264
265
|
},
|
|
265
266
|
liveDeliveryEvent: projectLiveDeliveryEvent(world, candidateFields),
|
|
266
267
|
summary:
|
|
267
|
-
'Active members can review candidate opportunities first, then call request_chat with the selected targetAgentId when they want to start a world-scoped conversation request.',
|
|
268
|
+
'Active online members can review candidate opportunities first, then call request_chat with the selected targetAgentId when they want to start a world-scoped conversation request.',
|
|
268
269
|
status: 'phase1_candidate_feed',
|
|
269
270
|
};
|
|
270
271
|
}
|
|
@@ -274,6 +275,7 @@ function projectCandidateOpportunity({
|
|
|
274
275
|
viewerProfile,
|
|
275
276
|
candidateMembership,
|
|
276
277
|
candidateAgent,
|
|
278
|
+
candidatePresence,
|
|
277
279
|
expiresAt,
|
|
278
280
|
}) {
|
|
279
281
|
const profileSnapshot = candidateMembership.profileSnapshot || {};
|
|
@@ -290,6 +292,7 @@ function projectCandidateOpportunity({
|
|
|
290
292
|
worldId: world.worldId,
|
|
291
293
|
sourceMembershipId: candidateMembership.membershipId,
|
|
292
294
|
targetAgentId: requestChat?.targetAgentId || null,
|
|
295
|
+
online: candidatePresence?.online === true,
|
|
293
296
|
requestChat,
|
|
294
297
|
profileSummary: projectProfileSummary(world, profileSnapshot, candidateAgent),
|
|
295
298
|
compatibilitySignals,
|
|
@@ -304,6 +307,7 @@ export function buildCandidateFeed({
|
|
|
304
307
|
viewerAgent = null,
|
|
305
308
|
candidateMemberships = [],
|
|
306
309
|
getAgent = () => null,
|
|
310
|
+
getPresence = () => ({ online: true }),
|
|
307
311
|
nowMs = Date.now(),
|
|
308
312
|
limit = DEFAULT_CANDIDATE_FEED_LIMIT,
|
|
309
313
|
}) {
|
|
@@ -315,12 +319,18 @@ export function buildCandidateFeed({
|
|
|
315
319
|
|
|
316
320
|
const candidates = candidateMemberships
|
|
317
321
|
.filter((membership) => membership?.status === 'active' && membership.membershipId !== viewerMembership.membershipId)
|
|
318
|
-
.map((membership) => {
|
|
322
|
+
.map((membership) => ({
|
|
323
|
+
membership,
|
|
324
|
+
candidatePresence: getPresence(membership.agentId),
|
|
325
|
+
}))
|
|
326
|
+
.filter(({ candidatePresence }) => candidatePresence?.online === true)
|
|
327
|
+
.map(({ membership, candidatePresence }) => {
|
|
319
328
|
const opportunity = projectCandidateOpportunity({
|
|
320
329
|
world,
|
|
321
330
|
viewerProfile,
|
|
322
331
|
candidateMembership: membership,
|
|
323
332
|
candidateAgent: getAgent(membership.agentId),
|
|
333
|
+
candidatePresence,
|
|
324
334
|
expiresAt,
|
|
325
335
|
});
|
|
326
336
|
|
|
@@ -12,6 +12,24 @@ function normalizeStringList(values = []) {
|
|
|
12
12
|
return [...new Set(values.map((value) => normalizeText(value, null)).filter(Boolean))];
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
function normalizeCandidateSource(value) {
|
|
16
|
+
const normalized = normalizeText(value, null);
|
|
17
|
+
if (!normalized) return null;
|
|
18
|
+
if (
|
|
19
|
+
normalized === 'active_memberships'
|
|
20
|
+
|| normalized === 'world_members'
|
|
21
|
+
|| normalized === 'search_results'
|
|
22
|
+
) {
|
|
23
|
+
return 'active_memberships_online';
|
|
24
|
+
}
|
|
25
|
+
return normalized;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function normalizeCandidateSources(values = []) {
|
|
29
|
+
if (!Array.isArray(values)) return [];
|
|
30
|
+
return [...new Set(values.map((value) => normalizeCandidateSource(value)).filter(Boolean))];
|
|
31
|
+
}
|
|
32
|
+
|
|
15
33
|
function normalizeWorldEligibility(value, fallback = 'active') {
|
|
16
34
|
const normalized = normalizeText(value, fallback);
|
|
17
35
|
if (normalized === 'joined') return 'joined';
|
|
@@ -188,7 +206,7 @@ export function normalizeWorldManifest(manifest = {}, index = 0) {
|
|
|
188
206
|
mode: normalizeText(manifest.matching?.mode, 'manual_review'),
|
|
189
207
|
cadence: normalizeText(manifest.matching?.cadence, 'on_demand'),
|
|
190
208
|
strategySummary: normalizeText(manifest.matching?.strategySummary, null),
|
|
191
|
-
candidateSources:
|
|
209
|
+
candidateSources: normalizeCandidateSources(manifest.matching?.candidateSources),
|
|
192
210
|
},
|
|
193
211
|
sessionTemplate: {
|
|
194
212
|
mode: normalizeText(manifest.sessionTemplate?.mode, 'a2a'),
|
|
@@ -388,6 +388,7 @@ function normalizeCandidate(candidate = {}, index = 0) {
|
|
|
388
388
|
candidateId: normalizeText(candidate.candidateId, `candidate_${index + 1}`),
|
|
389
389
|
worldId: normalizeText(candidate.worldId, 'unknown-world'),
|
|
390
390
|
sourceMembershipId: normalizeText(candidate.sourceMembershipId, null),
|
|
391
|
+
online: candidate.online === true,
|
|
391
392
|
targetAgentId,
|
|
392
393
|
requestChat,
|
|
393
394
|
profileSummary: normalizeCandidateProfileSummary(candidate.profileSummary),
|
|
@@ -420,7 +421,7 @@ function normalizeCandidateFeedResponse(payload = {}, { worldId = null, agentId
|
|
|
420
421
|
payload.nextAction,
|
|
421
422
|
candidates.length > 0 ? 'review_candidates_then_request_chat' : 'wait_for_more_candidates',
|
|
422
423
|
),
|
|
423
|
-
candidateSource: normalizeText(payload.candidateSource, '
|
|
424
|
+
candidateSource: normalizeText(payload.candidateSource, 'active_memberships_online'),
|
|
424
425
|
candidateModel: payload.candidateModel && typeof payload.candidateModel === 'object' ? payload.candidateModel : {},
|
|
425
426
|
strategy: payload.strategy && typeof payload.strategy === 'object' ? payload.strategy : {},
|
|
426
427
|
limit: normalizeInteger(payload.limit, candidates.length),
|
|
@@ -949,6 +950,7 @@ export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail
|
|
|
949
950
|
.map((signal) => sentenceCase(signal.summary, ''))
|
|
950
951
|
.filter(Boolean);
|
|
951
952
|
const deliveryReasonSummary = sentenceCase(candidate.deliveryReason.summary, '');
|
|
953
|
+
const availabilitySummary = candidate.online === true ? 'Online now.' : 'Currently offline.';
|
|
952
954
|
const scoreSummary = candidate.score == null
|
|
953
955
|
? null
|
|
954
956
|
: `Score ${candidate.score}${candidate.rank == null ? '' : `, rank ${candidate.rank}`}.`;
|
|
@@ -958,12 +960,14 @@ export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail
|
|
|
958
960
|
optionalFieldSummary.length > 0 ? `Optional context: ${optionalFieldSummary.join('; ')}.` : null,
|
|
959
961
|
compatibilitySummary.length > 0 ? compatibilitySummary.join(' ') : null,
|
|
960
962
|
deliveryReasonSummary || null,
|
|
963
|
+
availabilitySummary,
|
|
961
964
|
scoreSummary,
|
|
962
965
|
].filter(Boolean).join(' ');
|
|
963
966
|
|
|
964
967
|
return {
|
|
965
968
|
candidateId: candidate.candidateId,
|
|
966
969
|
sourceMembershipId: candidate.sourceMembershipId,
|
|
970
|
+
online: candidate.online === true,
|
|
967
971
|
targetAgentId: candidate.targetAgentId,
|
|
968
972
|
requestChat: candidate.requestChat,
|
|
969
973
|
displayName: name,
|
|
@@ -982,11 +986,11 @@ export function buildCandidateDeliverySummary(candidateFeed = {}, { worldDetail
|
|
|
982
986
|
const totalCandidateCount = Math.max(normalizedFeed.totalCandidates, deliveredCandidateCount);
|
|
983
987
|
const remainingCandidateCount = Math.max(totalCandidateCount - deliveredCandidateCount, 0);
|
|
984
988
|
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.`;
|
|
989
|
+
? `${displayName} has ${deliveredCandidateCount} online candidate profile ${deliveredCandidateCount === 1 ? 'summary' : 'summaries'} ready for review now.`
|
|
990
|
+
: `No online candidate profile summaries are ready for review in ${displayName} yet.`;
|
|
987
991
|
const promptBody = deliveredCandidateCount > 0
|
|
988
992
|
? candidateSummaries.map((summary, index) => buildCandidateDeliverySummaryLine(summary, index)).join('\n\n')
|
|
989
|
-
: 'No candidates are currently available from the active-membership feed.';
|
|
993
|
+
: 'No online candidates are currently available from the active-membership feed.';
|
|
990
994
|
|
|
991
995
|
return {
|
|
992
996
|
worldId: normalizedFeed.worldId,
|
|
@@ -33,7 +33,7 @@ export function createClaworldProductShell({
|
|
|
33
33
|
worldService,
|
|
34
34
|
membershipService,
|
|
35
35
|
});
|
|
36
|
-
const matchmakingService = createMatchmakingService({ worldService, worldAuthorizationService, store });
|
|
36
|
+
const matchmakingService = createMatchmakingService({ worldService, worldAuthorizationService, store, presence });
|
|
37
37
|
const searchService = createWorldSearchService({ worldService, worldAuthorizationService, store, presence });
|
|
38
38
|
const worldAdminService = createWorldAdminService({ worldService, worldAuthorizationService, store });
|
|
39
39
|
const chatRequestService = createChatRequestService({
|
|
@@ -217,7 +217,14 @@ function buildViewerContext({ world, membershipStore, normalizedAgentId, worldAu
|
|
|
217
217
|
};
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
function buildBaseFeed({
|
|
220
|
+
function buildBaseFeed({
|
|
221
|
+
world,
|
|
222
|
+
membershipStore,
|
|
223
|
+
normalizedAgentId,
|
|
224
|
+
limit,
|
|
225
|
+
worldAuthorizationService,
|
|
226
|
+
resolvePresence,
|
|
227
|
+
}) {
|
|
221
228
|
const { viewerAgent, viewerMembership } = buildViewerContext({
|
|
222
229
|
world,
|
|
223
230
|
membershipStore,
|
|
@@ -236,6 +243,7 @@ function buildBaseFeed({ world, membershipStore, normalizedAgentId, limit, world
|
|
|
236
243
|
viewerAgent,
|
|
237
244
|
candidateMemberships: activeMemberships,
|
|
238
245
|
getAgent: (candidateAgentId) => membershipStore.getAgent(candidateAgentId),
|
|
246
|
+
getPresence: (candidateAgentId) => resolvePresence(candidateAgentId),
|
|
239
247
|
nowMs,
|
|
240
248
|
limit: activeMemberships.length,
|
|
241
249
|
});
|
|
@@ -248,13 +256,21 @@ function buildBaseFeed({ world, membershipStore, normalizedAgentId, limit, world
|
|
|
248
256
|
};
|
|
249
257
|
}
|
|
250
258
|
|
|
251
|
-
function buildDatingDemoFeed({
|
|
259
|
+
function buildDatingDemoFeed({
|
|
260
|
+
world,
|
|
261
|
+
membershipStore,
|
|
262
|
+
normalizedAgentId,
|
|
263
|
+
limit,
|
|
264
|
+
worldAuthorizationService,
|
|
265
|
+
resolvePresence,
|
|
266
|
+
}) {
|
|
252
267
|
const { viewerMembership, activeMemberships, normalizedLimit, baseFeed } = buildBaseFeed({
|
|
253
268
|
world,
|
|
254
269
|
membershipStore,
|
|
255
270
|
normalizedAgentId,
|
|
256
271
|
limit,
|
|
257
272
|
worldAuthorizationService,
|
|
273
|
+
resolvePresence,
|
|
258
274
|
});
|
|
259
275
|
const membershipById = new Map(activeMemberships.map((membership) => [membership.membershipId, membership]));
|
|
260
276
|
const rankedCandidates = baseFeed.candidates
|
|
@@ -278,19 +294,35 @@ function buildDatingDemoFeed({ world, membershipStore, normalizedAgentId, limit,
|
|
|
278
294
|
...baseFeed,
|
|
279
295
|
agentId: normalizedAgentId,
|
|
280
296
|
limit: normalizedLimit,
|
|
281
|
-
candidateSource: '
|
|
297
|
+
candidateSource: 'active_memberships_online',
|
|
282
298
|
strategy: buildStrategy(world),
|
|
283
299
|
totalCandidates: rankedCandidates.length,
|
|
284
300
|
candidates,
|
|
285
301
|
};
|
|
286
302
|
}
|
|
287
303
|
|
|
288
|
-
export function createMatchmakingService({
|
|
304
|
+
export function createMatchmakingService({
|
|
305
|
+
worldService,
|
|
306
|
+
worldAuthorizationService,
|
|
307
|
+
store = null,
|
|
308
|
+
presence = null,
|
|
309
|
+
} = {}) {
|
|
289
310
|
function assertStore() {
|
|
290
311
|
if (!store) throw createConfigurationError();
|
|
291
312
|
return store;
|
|
292
313
|
}
|
|
293
314
|
|
|
315
|
+
function resolvePresence(agentId) {
|
|
316
|
+
if (!presence) {
|
|
317
|
+
return {
|
|
318
|
+
online: true,
|
|
319
|
+
connectedAt: null,
|
|
320
|
+
lastHeartbeatAt: null,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
return presence.getPresence(agentId);
|
|
324
|
+
}
|
|
325
|
+
|
|
294
326
|
return {
|
|
295
327
|
describeStrategy(worldId) {
|
|
296
328
|
const world = worldService.requireWorld(worldId);
|
|
@@ -312,6 +344,7 @@ export function createMatchmakingService({ worldService, worldAuthorizationServi
|
|
|
312
344
|
normalizedAgentId,
|
|
313
345
|
limit,
|
|
314
346
|
worldAuthorizationService,
|
|
347
|
+
resolvePresence,
|
|
315
348
|
});
|
|
316
349
|
}
|
|
317
350
|
|
|
@@ -321,13 +354,14 @@ export function createMatchmakingService({ worldService, worldAuthorizationServi
|
|
|
321
354
|
normalizedAgentId,
|
|
322
355
|
limit,
|
|
323
356
|
worldAuthorizationService,
|
|
357
|
+
resolvePresence,
|
|
324
358
|
});
|
|
325
359
|
|
|
326
360
|
return {
|
|
327
361
|
...baseFeed,
|
|
328
362
|
agentId: normalizedAgentId,
|
|
329
363
|
limit: normalizedLimit,
|
|
330
|
-
candidateSource: '
|
|
364
|
+
candidateSource: 'active_memberships_online',
|
|
331
365
|
strategy: buildStrategy(world),
|
|
332
366
|
totalCandidates: baseFeed.candidates.length,
|
|
333
367
|
candidates: baseFeed.candidates.slice(0, normalizedLimit),
|
|
@@ -239,8 +239,8 @@ function buildMatchingStrategy(entryProfileSchema) {
|
|
|
239
239
|
mode: 'profile_overlap',
|
|
240
240
|
cadence: 'on_demand',
|
|
241
241
|
strategySummary:
|
|
242
|
-
`Rank world members by overlap on ${entryProfileSchema.searchableFieldIds.join(', ')}, deliver candidate summaries first, and let members request_chat after review.`,
|
|
243
|
-
candidateSources: ['
|
|
242
|
+
`Rank active online world members by overlap on ${entryProfileSchema.searchableFieldIds.join(', ')}, deliver candidate summaries first, and let members request_chat after review.`,
|
|
243
|
+
candidateSources: ['active_memberships_online'],
|
|
244
244
|
};
|
|
245
245
|
}
|
|
246
246
|
|