@xfxstudio/claworld 0.1.1 → 0.1.2

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.
@@ -3,6 +3,7 @@ import { buildRuntimeAuthHeaders } from '../plugin/account-identity.js';
3
3
  import { createRuntimeBoundaryError } from '../../lib/runtime-errors.js';
4
4
  import {
5
5
  buildCandidateDeliverySummary as buildBackendCandidateDeliverySummary,
6
+ buildResolvedWorldJoinOrchestration as buildBackendResolvedWorldJoinOrchestration,
6
7
  buildWorldJoinOutcomeOrchestration as buildBackendWorldJoinOutcomeOrchestration,
7
8
  buildRequiredFieldExplanation as buildBackendRequiredFieldExplanation,
8
9
  buildWorldProfileCollectionFlow as buildBackendWorldProfileCollectionFlow,
@@ -196,7 +197,7 @@ function normalizeWorldDetail(payload = {}) {
196
197
  requiredFields,
197
198
  optionalFields,
198
199
  hints: normalizeStringList(payload.hints),
199
- nextAction: normalizeText(payload.nextAction, 'collect_profile_fields_then_call_join'),
200
+ nextAction: normalizeText(payload.nextAction, 'call_join_world'),
200
201
  sessionOverview: payload.sessionOverview && typeof payload.sessionOverview === 'object' ? payload.sessionOverview : {},
201
202
  matchingOverview: payload.matchingOverview && typeof payload.matchingOverview === 'object' ? payload.matchingOverview : {},
202
203
  requiredFieldExplanation:
@@ -261,7 +262,7 @@ function normalizeWorldDetail(payload = {}) {
261
262
  requiredFields,
262
263
  optionalFields,
263
264
  hints: normalizeStringList(joinSchema.hints),
264
- nextAction: normalizeText(joinSchema.nextAction, 'collect_profile_fields_then_call_join'),
265
+ nextAction: normalizeText(joinSchema.nextAction, 'call_join_world'),
265
266
  sessionOverview,
266
267
  matchingOverview,
267
268
  requiredFieldExplanation:
@@ -323,7 +324,7 @@ function normalizeJoinCheckResponse(payload = {}, { worldId = null, profile = {}
323
324
  ),
324
325
  nextAction: normalizeText(
325
326
  payload.nextAction,
326
- accepted ? 'join_world_when_membership_persistence_exists' : 'collect_missing_profile_fields',
327
+ accepted ? 'call_join_world' : 'retry_join_world_after_profile_update',
327
328
  ),
328
329
  profileCollectionFlow:
329
330
  payload.profileCollectionFlow && typeof payload.profileCollectionFlow === 'object'
@@ -405,11 +406,24 @@ function normalizeCandidateScoreBreakdown(entries = []) {
405
406
 
406
407
  function normalizeCandidate(candidate = {}, index = 0) {
407
408
  const normalizedRank = normalizeNumber(candidate.rank, null);
409
+ const targetAgentId = normalizeText(
410
+ candidate.targetAgentId || candidate.requestChat?.targetAgentId,
411
+ null,
412
+ );
413
+ const requestChat = targetAgentId
414
+ ? {
415
+ worldId: normalizeText(candidate.requestChat?.worldId, normalizeText(candidate.worldId, 'unknown-world')),
416
+ targetAgentId,
417
+ }
418
+ : null;
408
419
 
409
420
  return {
410
421
  candidateId: normalizeText(candidate.candidateId, `candidate_${index + 1}`),
411
422
  worldId: normalizeText(candidate.worldId, 'unknown-world'),
423
+ targetAgentId: normalizeText(candidate.targetAgentId, null),
412
424
  sourceMembershipId: normalizeText(candidate.sourceMembershipId, null),
425
+ targetAgentId,
426
+ requestChat,
413
427
  profileSummary: normalizeCandidateProfileSummary(candidate.profileSummary),
414
428
  compatibilitySignals: Array.isArray(candidate.compatibilitySignals)
415
429
  ? candidate.compatibilitySignals.map((signal, signalIndex) => normalizeCompatibilitySignal(signal, signalIndex))
@@ -438,7 +452,7 @@ function normalizeCandidateFeedResponse(payload = {}, { worldId = null, agentId
438
452
  deliveryMode: normalizeText(payload.deliveryMode, 'agent_review_before_live_session'),
439
453
  nextAction: normalizeText(
440
454
  payload.nextAction,
441
- candidates.length > 0 ? 'review_candidates_before_requesting_live_session' : 'wait_for_more_candidates',
455
+ candidates.length > 0 ? 'review_candidates_then_request_chat' : 'wait_for_more_candidates',
442
456
  ),
443
457
  candidateDelivery: payload.candidateDelivery && typeof payload.candidateDelivery === 'object'
444
458
  ? payload.candidateDelivery
@@ -455,15 +469,39 @@ function normalizeCandidateFeedResponse(payload = {}, { worldId = null, agentId
455
469
 
456
470
  function normalizeWorldJoinResponse(payload = {}, { worldId = null, agentId = null } = {}) {
457
471
  const membership = payload.membership && typeof payload.membership === 'object' ? payload.membership : null;
472
+ const normalizedWorldId = normalizeText(payload.worldId, worldId || 'unknown-world');
473
+ const normalizedAgentId = normalizeText(payload.agentId || membership?.agentId, agentId || null);
474
+ const membershipStatus = normalizeText(payload.membershipStatus || membership?.status, 'unknown');
475
+ const candidateFeed = payload.candidateFeed && typeof payload.candidateFeed === 'object'
476
+ ? normalizeCandidateFeedResponse(payload.candidateFeed, {
477
+ worldId: normalizedWorldId,
478
+ agentId: normalizedAgentId,
479
+ })
480
+ : null;
458
481
 
459
482
  return {
460
- worldId: normalizeText(payload.worldId, worldId || 'unknown-world'),
461
- agentId: normalizeText(payload.agentId || membership?.agentId, agentId || null),
462
- membershipStatus: normalizeText(payload.membershipStatus || membership?.status, 'unknown'),
483
+ status: normalizeText(payload.status, membershipStatus === 'active' ? 'joined' : 'accepted'),
484
+ worldId: normalizedWorldId,
485
+ agentId: normalizedAgentId,
486
+ membershipStatus,
487
+ normalizedProfile: normalizeProfile(
488
+ payload.normalizedProfile && typeof payload.normalizedProfile === 'object'
489
+ ? payload.normalizedProfile
490
+ : membership?.profileSnapshot,
491
+ ),
463
492
  membership,
493
+ nextAction: normalizeText(
494
+ payload.nextAction,
495
+ membershipStatus === 'active' ? 'review_candidate_feed' : null,
496
+ ),
464
497
  nextStageSummary: payload.nextStageSummary && typeof payload.nextStageSummary === 'object'
465
498
  ? payload.nextStageSummary
466
499
  : {},
500
+ candidateFeed,
501
+ candidateDelivery:
502
+ payload.candidateDelivery && typeof payload.candidateDelivery === 'object'
503
+ ? payload.candidateDelivery
504
+ : (candidateFeed ? buildCandidateDeliverySummary(candidateFeed) : null),
467
505
  orchestration: payload.orchestration && typeof payload.orchestration === 'object'
468
506
  ? payload.orchestration
469
507
  : null,
@@ -572,7 +610,7 @@ function normalizeWorldSearchResponse(payload = {}, { worldId = null, agentId =
572
610
  status: normalizeText(payload.status, items.length > 0 ? 'search_ready' : 'no_matches'),
573
611
  nextAction: normalizeText(
574
612
  payload.nextAction,
575
- items.length > 0 ? 'select_player_and_start_conversation' : 'broaden_search_or_wait',
613
+ items.length > 0 ? 'request_chat_with_selected_candidate' : 'broaden_search_or_wait',
576
614
  ),
577
615
  items,
578
616
  };
@@ -668,10 +706,10 @@ function buildProfileCollectionFollowUp(promptFields = []) {
668
706
 
669
707
  const labels = promptFields.map((field) => field.label);
670
708
  if (promptFields.length === 1) {
671
- return `After the user answers ${labels[0]}, merge it into the saved profile draft and re-run join-check before asking anything else.`;
709
+ return `After the user answers ${labels[0]}, merge it into the saved profile draft and retry join_world before asking anything else.`;
672
710
  }
673
711
 
674
- return `After the user answers ${joinAsNaturalLanguage(labels)}, merge those fields into the saved profile draft and re-run join-check before asking anything else.`;
712
+ return `After the user answers ${joinAsNaturalLanguage(labels)}, merge those fields into the saved profile draft and retry join_world before asking anything else.`;
675
713
  }
676
714
 
677
715
  function normalizeSelectionInput(selection) {
@@ -716,7 +754,7 @@ function buildSelectionRetryContract(status, selection, items = [], matches = []
716
754
  stage: 'post_setup_world_selection_retry',
717
755
  system: 'The world choice matched multiple worlds. Show the narrowed list and ask the user to pick one exact world ID or display name.',
718
756
  user: `The choice ${choiceLabel} is ambiguous. Matching worlds: ${joinAsNaturalLanguage(retrySummary)}. Ask the user to choose one exact world ID or display name.`,
719
- followUp: 'Once the user confirms one exact world, fetch its detail and explain the required fields before join-check.',
757
+ followUp: 'Once the user confirms one exact world, fetch its detail, explain the required fields, and use join_world when enough profile data is available.',
720
758
  },
721
759
  };
722
760
  }
@@ -736,7 +774,7 @@ function buildSelectionRetryContract(status, selection, items = [], matches = []
736
774
  user: retrySummary.length > 0
737
775
  ? `I could not match ${choiceLabel} to an available world. Available worlds: ${joinAsNaturalLanguage(retrySummary)}. Ask the user to choose one by world ID or display name.`
738
776
  : 'No worlds are currently available. Tell the user setup is complete but world selection cannot continue yet.',
739
- followUp: 'Once the user chooses a valid world, confirm it, fetch the world detail, and explain the required fields before join-check.',
777
+ followUp: 'Once the user chooses a valid world, confirm it, fetch the world detail, and explain the required fields before calling join_world.',
740
778
  },
741
779
  };
742
780
  }
@@ -1139,50 +1177,42 @@ export async function submitWorldJoin({
1139
1177
  fetchImpl,
1140
1178
  logger,
1141
1179
  });
1142
- const joinCheck = normalizeJoinCheckResponse(joinResult.body?.joinCheck || joinResult.body, {
1180
+ const joinCheck = normalizeJoinCheckResponse(joinResult.body, {
1143
1181
  worldId: resolvedWorldId,
1144
1182
  profile: joinProfile.mergedProfile,
1145
1183
  });
1146
- const profileCollectionFlow = joinCheck.profileCollectionFlow && typeof joinCheck.profileCollectionFlow === 'object'
1147
- ? {
1148
- ...joinCheck.profileCollectionFlow,
1184
+ const profileCollectionFlow = {
1185
+ ...buildBackendWorldProfileCollectionFlow({
1149
1186
  worldDetail: detail,
1150
1187
  joinCheck,
1151
1188
  profile: joinCheck.normalizedProfile,
1152
- appliedProfileUpdate: joinProfile.appliedProfileUpdate,
1153
- }
1154
- : {
1155
- ...buildBackendWorldProfileCollectionFlow({
1156
- worldDetail: detail,
1157
- joinCheck,
1158
- profile: joinCheck.normalizedProfile,
1159
- maxFieldsPerStep,
1160
- }),
1161
- worldDetail: detail,
1162
- joinCheck,
1163
- profile: joinCheck.normalizedProfile,
1164
- appliedProfileUpdate: joinProfile.appliedProfileUpdate,
1165
- };
1189
+ maxFieldsPerStep,
1190
+ }),
1191
+ worldDetail: detail,
1192
+ joinCheck,
1193
+ profile: joinCheck.normalizedProfile,
1194
+ appliedProfileUpdate: joinProfile.appliedProfileUpdate,
1195
+ };
1166
1196
 
1167
1197
  return {
1168
1198
  status: 'needs_profile',
1169
1199
  worldId: resolvedWorldId,
1170
1200
  agentId: resolvedAgentId,
1171
1201
  membershipStatus: normalizeText(joinResult.body?.membershipStatus, 'inactive'),
1172
- searchEnabled: false,
1173
- missingRequiredFields: projectMissingRequiredFields(joinCheck.missingFields),
1202
+ normalizedProfile: profileCollectionFlow.profile,
1203
+ missingFields: projectMissingRequiredFields(joinCheck.missingFields),
1174
1204
  nextMissingField: joinCheck.nextMissingField
1175
1205
  ? projectMissingRequiredFields([joinCheck.nextMissingField])[0]
1176
1206
  : null,
1177
- normalizedProfile: profileCollectionFlow.profile,
1178
- nextAction: 'complete_required_profile_fields_then_retry_join',
1207
+ nextAction: normalizeText(joinResult.body?.nextAction, 'retry_join_world_after_profile_update'),
1208
+ missingFieldGuidance: joinCheck.missingFieldGuidance,
1209
+ candidateFeed: null,
1210
+ candidateDelivery: null,
1179
1211
  joinCheck,
1180
1212
  profileCollectionFlow,
1181
- orchestration: profileCollectionFlow.orchestration || null,
1182
- providedRequiredFieldIds: profileCollectionFlow.providedRequiredFieldIds,
1183
- promptFields: profileCollectionFlow.promptFields,
1184
- remainingRequiredFieldCount: profileCollectionFlow.remainingRequiredFieldCount,
1185
- revalidationCheckpoint: profileCollectionFlow.revalidationCheckpoint,
1213
+ profileDraft: profileCollectionFlow.profile,
1214
+ missingRequiredFields: projectMissingRequiredFields(joinCheck.missingFields),
1215
+ orchestration: null,
1186
1216
  };
1187
1217
  }
1188
1218
 
@@ -1192,13 +1222,16 @@ export async function submitWorldJoin({
1192
1222
  });
1193
1223
 
1194
1224
  return {
1195
- status: normalizedJoinResult.membershipStatus === 'active' ? 'activated' : 'accepted',
1225
+ status: normalizedJoinResult.status,
1196
1226
  worldId: normalizedJoinResult.worldId,
1197
1227
  agentId: normalizedJoinResult.agentId,
1198
1228
  membershipStatus: normalizedJoinResult.membershipStatus,
1199
- searchEnabled: normalizedJoinResult.membershipStatus === 'active',
1229
+ normalizedProfile: normalizedJoinResult.normalizedProfile,
1200
1230
  membership: normalizedJoinResult.membership,
1231
+ nextAction: normalizedJoinResult.nextAction,
1201
1232
  nextStageSummary: normalizedJoinResult.nextStageSummary,
1233
+ candidateFeed: normalizedJoinResult.candidateFeed,
1234
+ candidateDelivery: normalizedJoinResult.candidateDelivery,
1202
1235
  orchestration: normalizedJoinResult.orchestration || null,
1203
1236
  };
1204
1237
  }
@@ -1643,6 +1676,9 @@ export async function resolveWorldJoinFlow({
1643
1676
  agentId = null,
1644
1677
  profile = {},
1645
1678
  profileSnapshot = null,
1679
+ profileUpdate = null,
1680
+ profilePatch = null,
1681
+ maxFieldsPerStep = 1,
1646
1682
  limit = null,
1647
1683
  fetchImpl,
1648
1684
  logger = console,
@@ -1662,7 +1698,7 @@ export async function resolveWorldJoinFlow({
1662
1698
  throw new Error('claworld product-shell join flow requires worldId');
1663
1699
  }
1664
1700
 
1665
- const joinResult = await joinWorld({
1701
+ const joinResult = await submitWorldJoin({
1666
1702
  cfg,
1667
1703
  accountId,
1668
1704
  runtimeConfig,
@@ -1670,20 +1706,28 @@ export async function resolveWorldJoinFlow({
1670
1706
  agentId,
1671
1707
  profile,
1672
1708
  profileSnapshot,
1709
+ profileUpdate,
1710
+ profilePatch,
1711
+ maxFieldsPerStep,
1673
1712
  fetchImpl,
1674
1713
  logger,
1675
1714
  });
1676
- const candidateFeed = await fetchWorldCandidateFeed({
1677
- cfg,
1678
- accountId,
1679
- runtimeConfig,
1680
- worldId: resolvedWorldId,
1681
- agentId: joinResult.agentId || agentId,
1682
- limit,
1683
- fetchImpl,
1684
- logger,
1685
- });
1686
- const candidateDelivery = buildCandidateDeliverySummary(candidateFeed, {
1715
+ if (joinResult.status === 'needs_profile') {
1716
+ return joinResult;
1717
+ }
1718
+ const candidateFeed = limit == null && joinResult.candidateFeed
1719
+ ? joinResult.candidateFeed
1720
+ : await fetchWorldCandidateFeed({
1721
+ cfg,
1722
+ accountId,
1723
+ runtimeConfig,
1724
+ worldId: resolvedWorldId,
1725
+ agentId: joinResult.agentId || agentId,
1726
+ limit,
1727
+ fetchImpl,
1728
+ logger,
1729
+ });
1730
+ const candidateDelivery = joinResult.candidateDelivery || buildCandidateDeliverySummary(candidateFeed, {
1687
1731
  worldDetail: detail,
1688
1732
  limit,
1689
1733
  });
@@ -1702,11 +1746,20 @@ export async function resolveWorldJoinFlow({
1702
1746
  agentId: joinResult.agentId || normalizeText(agentId, null),
1703
1747
  worldDetail: detail,
1704
1748
  membershipStatus: joinResult.membershipStatus,
1749
+ normalizedProfile: joinResult.normalizedProfile,
1705
1750
  membership: joinResult.membership,
1751
+ nextAction: normalizeText(joinResult.nextAction, candidateDelivery.nextAction),
1706
1752
  nextStageSummary: joinResult.nextStageSummary,
1707
1753
  joinResult,
1708
1754
  candidateFeed,
1709
1755
  candidateDelivery,
1710
- orchestration: joinOrchestration,
1756
+ requestChatAction: candidateDelivery.requestChatAction,
1757
+ orchestration: buildBackendResolvedWorldJoinOrchestration({
1758
+ joinResult: {
1759
+ ...joinResult,
1760
+ orchestration: joinOrchestration,
1761
+ },
1762
+ candidateDelivery,
1763
+ }),
1711
1764
  };
1712
1765
  }
@@ -162,6 +162,106 @@ function projectOrchestration(orchestration = null) {
162
162
  };
163
163
  }
164
164
 
165
+ function projectRequestChatPayload(
166
+ requestChat = null,
167
+ {
168
+ accountId = null,
169
+ requestToolName = 'claworld_request_chat',
170
+ } = {},
171
+ ) {
172
+ if (!requestChat || typeof requestChat !== 'object' || Array.isArray(requestChat)) return null;
173
+ const worldId = normalizeText(requestChat.worldId, null);
174
+ const targetAgentId = normalizeText(requestChat.targetAgentId, null);
175
+ if (!worldId || !targetAgentId) return null;
176
+
177
+ const normalizedAccountId = normalizeText(accountId, null);
178
+
179
+ return {
180
+ worldId,
181
+ targetAgentId,
182
+ requestTool: normalizeText(requestToolName, null),
183
+ requestPayload: {
184
+ ...(normalizedAccountId ? { accountId: normalizedAccountId } : {}),
185
+ worldId,
186
+ targetAgentId,
187
+ },
188
+ };
189
+ }
190
+
191
+ function projectRequestChatAction(
192
+ requestChatAction = null,
193
+ {
194
+ accountId = null,
195
+ requestToolName = 'claworld_request_chat',
196
+ } = {},
197
+ ) {
198
+ if (!requestChatAction || typeof requestChatAction !== 'object' || Array.isArray(requestChatAction)) return null;
199
+ const worldId = normalizeText(requestChatAction.worldId, null);
200
+ if (!worldId) return null;
201
+
202
+ const normalizedAccountId = normalizeText(accountId, null);
203
+
204
+ return {
205
+ action: normalizeText(requestChatAction.action, 'request_chat'),
206
+ worldId,
207
+ requiredFields: normalizeStringList(requestChatAction.requiredFields),
208
+ requestTool: normalizeText(requestToolName, null),
209
+ requestPayloadTemplate: {
210
+ ...(normalizedAccountId ? { accountId: normalizedAccountId } : {}),
211
+ worldId,
212
+ targetAgentId: ':targetAgentId',
213
+ },
214
+ summary: normalizeText(requestChatAction.summary, null),
215
+ };
216
+ }
217
+
218
+ function projectToolCandidateDeliverySummary(
219
+ candidateDelivery = {},
220
+ {
221
+ accountId = null,
222
+ requestToolName = 'claworld_request_chat',
223
+ } = {},
224
+ ) {
225
+ if (!candidateDelivery || typeof candidateDelivery !== 'object' || Array.isArray(candidateDelivery)) return null;
226
+
227
+ const candidateSummaries = Array.isArray(candidateDelivery.candidateSummaries)
228
+ ? candidateDelivery.candidateSummaries.map((summary) => ({
229
+ candidateId: normalizeText(summary.candidateId, null),
230
+ sourceMembershipId: normalizeText(summary.sourceMembershipId, null),
231
+ displayName: normalizeText(summary.displayName, null),
232
+ headline: normalizeText(summary.headline, null),
233
+ rank: normalizeOptionalInteger(summary.rank, null),
234
+ score: normalizeOptionalInteger(summary.score, null),
235
+ targetAgentId: normalizeText(summary.targetAgentId, summary.requestChat?.targetAgentId || null),
236
+ requestChat: projectRequestChatPayload(summary.requestChat, {
237
+ accountId,
238
+ requestToolName,
239
+ }),
240
+ requiredFieldSummary: normalizeStringList(summary.requiredFieldSummary),
241
+ optionalFieldSummary: normalizeStringList(summary.optionalFieldSummary),
242
+ compatibilitySummary: normalizeStringList(summary.compatibilitySummary),
243
+ deliveryReasonSummary: normalizeText(summary.deliveryReasonSummary, null),
244
+ expiresAt: normalizeText(summary.expiresAt, null),
245
+ summary: normalizeText(summary.summary, null),
246
+ }))
247
+ : [];
248
+
249
+ return {
250
+ status: normalizeText(candidateDelivery.status, null),
251
+ worldId: normalizeText(candidateDelivery.worldId, null),
252
+ deliveredCandidateCount: normalizeInteger(candidateDelivery.deliveredCandidateCount, candidateSummaries.length),
253
+ totalCandidateCount: normalizeInteger(candidateDelivery.totalCandidateCount, candidateSummaries.length),
254
+ remainingCandidateCount: normalizeInteger(candidateDelivery.remainingCandidateCount, 0),
255
+ nextAction: normalizeText(candidateDelivery.nextAction, null),
256
+ requestChatAction: projectRequestChatAction(candidateDelivery.requestChatAction, {
257
+ accountId,
258
+ requestToolName,
259
+ }),
260
+ candidateSummaries,
261
+ orchestration: projectOrchestration(candidateDelivery.orchestration),
262
+ };
263
+ }
264
+
165
265
  export function projectToolWorldList(worldDirectory = {}) {
166
266
  const worlds = Array.isArray(worldDirectory.items)
167
267
  ? [...worldDirectory.items]
@@ -255,8 +355,8 @@ export function projectToolWorldProfileCollectionResponse(
255
355
  promptFieldIds: promptFields.map((field) => field.fieldId).filter(Boolean),
256
356
  },
257
357
  nextAction: readyToJoin
258
- ? 'retry_join_with_profile_draft'
259
- : 'collect_missing_profile_fields_and_recheck',
358
+ ? 'call_join_world'
359
+ : 'retry_join_world_after_profile_update',
260
360
  nextTool: readyToJoin ? normalizeText(joinToolName, null) : normalizeText(continueToolName, null),
261
361
  continueTool: normalizeText(continueToolName, null),
262
362
  joinTool: normalizeText(joinToolName, null),
@@ -277,6 +377,60 @@ export function projectToolWorldProfileCollectionResponse(
277
377
  };
278
378
  }
279
379
 
380
+ function projectToolCandidateSummary(summary = {}, index = 0) {
381
+ return {
382
+ candidateId: normalizeText(summary.candidateId, `candidate_${index + 1}`),
383
+ targetAgentId: normalizeText(summary.targetAgentId, null),
384
+ displayName: normalizeText(summary.displayName, `Candidate ${index + 1}`),
385
+ headline: normalizeText(summary.headline, null),
386
+ rank: normalizeInteger(summary.rank, 0) || null,
387
+ score: normalizeInteger(summary.score, 0) || null,
388
+ summary: normalizeText(summary.summary, null),
389
+ expiresAt: normalizeText(summary.expiresAt, null),
390
+ };
391
+ }
392
+
393
+ function projectToolCandidateFeed(joinResult = {}) {
394
+ const candidateDelivery = joinResult.candidateDelivery && typeof joinResult.candidateDelivery === 'object'
395
+ ? joinResult.candidateDelivery
396
+ : null;
397
+ const candidateFeed = joinResult.candidateFeed && typeof joinResult.candidateFeed === 'object'
398
+ ? joinResult.candidateFeed
399
+ : null;
400
+ const candidateSummaries = Array.isArray(candidateDelivery?.candidateSummaries)
401
+ ? candidateDelivery.candidateSummaries.map((summary, index) => projectToolCandidateSummary(summary, index))
402
+ : Array.isArray(candidateFeed?.candidates)
403
+ ? candidateFeed.candidates.map((candidate, index) => projectToolCandidateSummary({
404
+ candidateId: candidate.candidateId,
405
+ targetAgentId: candidate.targetAgentId,
406
+ displayName: candidate.profileSummary?.displayName,
407
+ headline: candidate.profileSummary?.headline,
408
+ rank: candidate.rank,
409
+ score: candidate.score,
410
+ summary: normalizeText(candidate.deliveryReason?.summary, null),
411
+ expiresAt: candidate.expiresAt,
412
+ }, index))
413
+ : [];
414
+
415
+ return {
416
+ status: normalizeText(
417
+ candidateDelivery?.status,
418
+ normalizeText(candidateFeed?.status, candidateSummaries.length > 0 ? 'candidate_summary_ready' : 'candidate_summary_pending'),
419
+ ),
420
+ nextAction: normalizeText(
421
+ candidateDelivery?.nextAction,
422
+ normalizeText(candidateFeed?.nextAction, candidateSummaries.length > 0 ? 'review_candidates_then_request_chat' : 'wait_for_more_candidates'),
423
+ ),
424
+ deliveredCandidateCount: normalizeInteger(candidateDelivery?.deliveredCandidateCount, candidateSummaries.length),
425
+ totalCandidateCount: normalizeInteger(
426
+ candidateDelivery?.totalCandidateCount,
427
+ normalizeInteger(candidateFeed?.totalCandidates, candidateSummaries.length),
428
+ ),
429
+ remainingCandidateCount: normalizeInteger(candidateDelivery?.remainingCandidateCount, 0),
430
+ candidates: candidateSummaries,
431
+ };
432
+ }
433
+
280
434
  export function projectToolJoinWorldResponse(
281
435
  joinResult = {},
282
436
  {
@@ -287,34 +441,56 @@ export function projectToolJoinWorldResponse(
287
441
  ) {
288
442
  if (joinResult.status === 'needs_profile') {
289
443
  if (joinResult.profileCollectionFlow && typeof joinResult.profileCollectionFlow === 'object') {
444
+ const profileCollectionResponse = projectToolWorldProfileCollectionResponse(joinResult.profileCollectionFlow, {
445
+ accountId,
446
+ continueToolName,
447
+ joinToolName,
448
+ });
449
+ const normalizedProfile = joinResult.normalizedProfile && typeof joinResult.normalizedProfile === 'object'
450
+ ? joinResult.normalizedProfile
451
+ : profileCollectionResponse.profileDraft;
452
+ const missingFields = Array.isArray(joinResult.missingFields)
453
+ ? joinResult.missingFields
454
+ : (Array.isArray(joinResult.missingRequiredFields) ? joinResult.missingRequiredFields : []);
455
+
290
456
  return {
291
- ...projectToolWorldProfileCollectionResponse(joinResult.profileCollectionFlow, {
292
- accountId,
293
- continueToolName,
294
- joinToolName,
295
- }),
457
+ ...profileCollectionResponse,
296
458
  membershipStatus: joinResult.membershipStatus || 'inactive',
297
- searchEnabled: false,
298
- nextAction: 'collect_missing_profile_fields_and_retry_join',
459
+ normalizedProfile,
460
+ profileDraft: normalizedProfile,
461
+ missingFields,
462
+ missingRequiredFields: missingFields,
463
+ nextAction: normalizeText(joinResult.nextAction, profileCollectionResponse.nextAction),
464
+ missingFieldGuidance: joinResult.missingFieldGuidance && typeof joinResult.missingFieldGuidance === 'object'
465
+ ? joinResult.missingFieldGuidance
466
+ : null,
299
467
  };
300
468
  }
301
469
 
302
- const profileDraft = joinResult.normalizedProfile && typeof joinResult.normalizedProfile === 'object'
470
+ const normalizedProfile = joinResult.normalizedProfile && typeof joinResult.normalizedProfile === 'object'
303
471
  ? joinResult.normalizedProfile
304
472
  : {};
473
+ const missingFields = Array.isArray(joinResult.missingFields)
474
+ ? joinResult.missingFields
475
+ : (Array.isArray(joinResult.missingRequiredFields) ? joinResult.missingRequiredFields : []);
476
+
305
477
  return {
306
- status: 'needs_profile',
307
- worldId: joinResult.worldId,
308
- accountId: normalizeText(accountId, null),
309
- membershipStatus: joinResult.membershipStatus || 'inactive',
310
- searchEnabled: false,
311
- missingRequiredFields: Array.isArray(joinResult.missingRequiredFields) ? joinResult.missingRequiredFields : [],
478
+ status: 'needs_profile',
479
+ worldId: joinResult.worldId,
480
+ accountId: normalizeText(accountId, null),
481
+ membershipStatus: joinResult.membershipStatus || 'inactive',
482
+ normalizedProfile,
483
+ profileDraft: normalizedProfile,
484
+ missingFields,
485
+ missingRequiredFields: missingFields,
312
486
  nextMissingField: joinResult.nextMissingField || null,
313
- profileDraft,
314
- nextAction: 'collect_missing_profile_fields_and_retry_join',
315
- nextTool: normalizeText(continueToolName, null),
316
- continueTool: normalizeText(continueToolName, null),
487
+ nextAction: normalizeText(joinResult.nextAction, 'retry_join_world_after_profile_update'),
488
+ nextTool: normalizeText(joinToolName, null),
489
+ continueTool: normalizeText(joinToolName, null),
317
490
  joinTool: normalizeText(joinToolName, null),
491
+ missingFieldGuidance: joinResult.missingFieldGuidance && typeof joinResult.missingFieldGuidance === 'object'
492
+ ? joinResult.missingFieldGuidance
493
+ : null,
318
494
  orchestration: projectOrchestration(joinResult.orchestration),
319
495
  guidance: joinResult.orchestration && typeof joinResult.orchestration === 'object'
320
496
  ? {
@@ -326,11 +502,18 @@ export function projectToolJoinWorldResponse(
326
502
  }
327
503
 
328
504
  return {
329
- status: joinResult.status === 'activated' ? 'activated' : 'accepted',
505
+ status: joinResult.membershipStatus === 'active' ? 'joined' : 'accepted',
330
506
  worldId: joinResult.worldId,
331
507
  accountId: normalizeText(accountId, null),
332
508
  membershipStatus: joinResult.membershipStatus || 'unknown',
333
- searchEnabled: joinResult.searchEnabled === true,
509
+ normalizedProfile: joinResult.normalizedProfile && typeof joinResult.normalizedProfile === 'object'
510
+ ? joinResult.normalizedProfile
511
+ : {},
512
+ nextAction: normalizeText(joinResult.nextAction, 'review_candidate_feed'),
513
+ candidateFeed: projectToolCandidateFeed(joinResult),
514
+ requestChatTool: 'claworld_request_chat',
515
+ candidateDelivery: projectToolCandidateDeliverySummary(joinResult.candidateDelivery, { accountId }),
516
+ requestChatAction: projectRequestChatAction(joinResult.requestChatAction, { accountId }),
334
517
  orchestration: projectOrchestration(joinResult.orchestration),
335
518
  guidance: joinResult.orchestration && typeof joinResult.orchestration === 'object'
336
519
  ? {
@@ -342,15 +525,21 @@ export function projectToolJoinWorldResponse(
342
525
  }
343
526
 
344
527
  export function projectToolSearchWorldResponse(searchResult = {}, { accountId = null } = {}) {
528
+ const compatibilityMessage = 'Compatibility-only manual world search. Candidate feed review is the canonical path before request_chat.';
345
529
  if (searchResult.status === 'not_joined') {
346
530
  return {
347
531
  status: 'not_joined',
348
532
  worldId: searchResult.worldId,
349
533
  accountId: normalizeText(accountId, null),
534
+ discoveryMode: 'compatibility_search',
535
+ canonicalDiscoveryMode: 'candidate_feed',
350
536
  query: searchResult.query || {},
351
537
  limit: normalizeInteger(searchResult.limit, 10),
352
538
  results: [],
353
- message: normalizeText(searchResult.message, 'Join the world before searching it.'),
539
+ message: normalizeText(
540
+ searchResult.message,
541
+ 'Join the world first. Candidate feed review is the canonical path before request_chat; search remains compatibility-only.',
542
+ ),
354
543
  };
355
544
  }
356
545
 
@@ -371,8 +560,11 @@ export function projectToolSearchWorldResponse(searchResult = {}, { accountId =
371
560
  status: results.length > 0 ? 'ready' : 'empty',
372
561
  worldId: searchResult.worldId,
373
562
  accountId: normalizeText(accountId, null),
563
+ discoveryMode: 'compatibility_search',
564
+ canonicalDiscoveryMode: 'candidate_feed',
374
565
  query: searchResult.query || {},
375
566
  limit: normalizeInteger(searchResult.limit, results.length || 10),
567
+ message: compatibilityMessage,
376
568
  results,
377
569
  };
378
570
  }
@@ -29,12 +29,12 @@ export const CLAWORLD_WORLD_TOOL_NAMES = Object.freeze([
29
29
  'claworld_list_worlds',
30
30
  'claworld_get_world_detail',
31
31
  'claworld_join_world',
32
- 'claworld_search_world',
33
32
  'claworld_broadcast_world',
34
33
  ]);
35
34
 
36
35
  export const CLAWORLD_OPTIONAL_WORLD_HELPER_TOOL_NAMES = Object.freeze([
37
36
  'claworld_prepare_world_join',
37
+ 'claworld_search_world',
38
38
  ]);
39
39
 
40
40
  export const CLAWORLD_ADVANCED_TOOL_NAMES = Object.freeze([