@xfxstudio/claworld 2026.4.22-testing.7 → 2026.4.27-testing.1

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.
@@ -511,10 +511,42 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
511
511
  minLength: 1,
512
512
  examples: ['网球 搭子 周末约球'],
513
513
  }),
514
+ keywords: arrayParam({
515
+ description: 'Structured keywords for agent-authored search.',
516
+ items: stringParam({ minLength: 1 }),
517
+ }),
518
+ topics: arrayParam({
519
+ description: 'Structured topics for agent-authored search.',
520
+ items: stringParam({ minLength: 1 }),
521
+ }),
522
+ location: stringParam({
523
+ description: 'Optional structured location signal.',
524
+ minLength: 1,
525
+ examples: ['上海'],
526
+ }),
527
+ timeWindow: stringParam({
528
+ description: 'Optional structured time-window signal.',
529
+ minLength: 1,
530
+ examples: ['周末'],
531
+ }),
532
+ intent: stringParam({
533
+ description: 'Agent task intent for ranking and result action selection.',
534
+ enumValues: ['join_world', 'find_member', 'find_public_person'],
535
+ examples: ['join_world'],
536
+ }),
537
+ desiredInteraction: stringParam({
538
+ description: 'Optional structured interaction preference.',
539
+ minLength: 1,
540
+ examples: ['线下约球'],
541
+ }),
542
+ constraints: arrayParam({
543
+ description: 'Structured constraints that should influence search matching.',
544
+ items: stringParam({ minLength: 1 }),
545
+ }),
514
546
  sort: stringParam({
515
547
  description: 'Sort mode for the selected scope.',
516
- enumValues: ['match', 'hot', 'latest', 'likes'],
517
- examples: ['match'],
548
+ enumValues: ['relevance', 'hot', 'latest', 'likes', 'activity'],
549
+ examples: ['relevance'],
518
550
  }),
519
551
  limit: integerParam({
520
552
  description: 'Maximum result count.',
@@ -529,8 +561,8 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
529
561
  }),
530
562
  },
531
563
  examples: [
532
- { accountId: 'claworld', scope: 'worlds', query: '网球', sort: 'match', limit: 5 },
533
- { accountId: 'claworld', scope: 'world_members', worldId: 'dating-demo-world', query: '上海 周末', limit: 5 },
564
+ { accountId: 'claworld', scope: 'worlds', keywords: ['网球', '上海', '周末'], intent: 'join_world', sort: 'relevance', limit: 5 },
565
+ { accountId: 'claworld', scope: 'world_members', worldId: 'dating-demo-world', keywords: ['上海', '周末'], intent: 'find_member', limit: 5 },
534
566
  { accountId: 'claworld', scope: 'people', query: 'Moza', limit: 5 },
535
567
  { accountId: 'claworld', scope: 'mixed', query: '网球 Moza', limit: 5 },
536
568
  ],
@@ -549,6 +581,13 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
549
581
  scope,
550
582
  worldId: params.worldId || null,
551
583
  query: params.query || null,
584
+ keywords: params.keywords || [],
585
+ topics: params.topics || [],
586
+ location: params.location || null,
587
+ timeWindow: params.timeWindow || null,
588
+ intent: params.intent || null,
589
+ desiredInteraction: params.desiredInteraction || null,
590
+ constraints: params.constraints || [],
552
591
  sort: params.sort || null,
553
592
  limit: params.limit ?? null,
554
593
  page: params.page ?? null,
@@ -559,7 +598,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
559
598
  {
560
599
  name: publicProfileTool,
561
600
  label: 'Claworld Get Public Profile',
562
- description: 'Read the current account public profile/readiness projection. Target public-profile lookup is exposed as a terminal tool name and remains backend-fact based.',
601
+ description: 'Read the current account public profile or perform exact displayName#code public-profile lookup.',
563
602
  metadata: buildToolMetadata({
564
603
  category: 'public_profile',
565
604
  usageNotes: [
@@ -571,13 +610,27 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
571
610
  description: 'Public profile lookup payload.',
572
611
  properties: {
573
612
  accountId: accountIdProperty,
613
+ action: stringParam({
614
+ description: 'Public-profile action.',
615
+ enumValues: ['get_profile', 'lookup_profile'],
616
+ examples: ['lookup_profile'],
617
+ }),
618
+ identity: stringParam({
619
+ description: 'Exact public identity in displayName#code form for action=lookup_profile.',
620
+ minLength: 1,
621
+ examples: ['Runtime Peer#ZX82QP'],
622
+ }),
623
+ agentId: stringParam({
624
+ description: 'Optional target agent id for action=get_profile; defaults to the current account binding.',
625
+ minLength: 1,
626
+ }),
574
627
  agentCode: stringParam({
575
- description: 'Optional public code for a future target lookup route.',
628
+ description: 'Public code paired with displayName for action=lookup_profile.',
576
629
  minLength: 1,
577
630
  examples: ['ZX82QP'],
578
631
  }),
579
632
  displayName: stringParam({
580
- description: 'Optional display name paired with agentCode for a future target lookup route.',
633
+ description: 'Display name paired with agentCode for action=lookup_profile.',
581
634
  minLength: 1,
582
635
  examples: ['Runtime Peer'],
583
636
  }),
@@ -592,11 +645,31 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
592
645
  },
593
646
  }),
594
647
  async execute(toolCallId, params = {}) {
595
- const result = await requireTerminalTool(internalTools, 'claworld_account').execute(toolCallId, {
596
- ...params,
597
- action: 'view',
598
- });
599
- return rewriteToolResultName(result, publicProfileTool);
648
+ const action = normalizeText(
649
+ params.action,
650
+ params.identity || params.agentCode || params.displayName ? 'lookup_profile' : 'get_profile',
651
+ );
652
+ if (!['get_profile', 'lookup_profile'].includes(action)) {
653
+ requireManageWorldField('action', 'action must be one of get_profile or lookup_profile');
654
+ }
655
+ const context = await resolveToolContext(api, plugin, params);
656
+ const lookupIdentity = normalizeText(
657
+ params.identity,
658
+ params.displayName && params.agentCode ? `${params.displayName}#${params.agentCode}` : null,
659
+ );
660
+ if (action === 'lookup_profile' && !lookupIdentity) {
661
+ requireManageWorldField('identity', 'identity or displayName+agentCode is required for action=lookup_profile');
662
+ }
663
+ const payload = action === 'lookup_profile'
664
+ ? await plugin.runtime.productShell.publicProfiles.lookupPublicProfile({
665
+ ...context,
666
+ identity: lookupIdentity,
667
+ })
668
+ : await plugin.runtime.productShell.publicProfiles.getPublicProfile({
669
+ ...context,
670
+ agentId: normalizeText(params.agentId, context.agentId),
671
+ });
672
+ return buildTerminalActionResult({ tool: publicProfileTool, action, payload });
600
673
  },
601
674
  },
602
675
  {
@@ -610,7 +683,6 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
610
683
  'action=create_world creates an owner-managed world.',
611
684
  'Owner governance and member self-service actions use terminal action names such as update_world, publish_broadcast, and update_world_profile.',
612
685
  'Subscription, activity, and member-list actions are backed by the product-shell terminal routes.',
613
- 'Retired feed-style recommendations are not exposed as terminal public actions.',
614
686
  ],
615
687
  }),
616
688
  parameters: objectParam({
@@ -632,6 +704,10 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
632
704
  excludeSelf: booleanParam({ description: 'Whether broadcast excludes the sender.' }),
633
705
  includeDisabled: booleanParam({ description: 'Whether list actions include disabled rows.' }),
634
706
  enabled: booleanParam({ description: 'Whether create/resume should enable the world.' }),
707
+ visibility: stringParam({ description: 'World visibility for discovery/access policy.', enumValues: ['public', 'private'] }),
708
+ identityMode: stringParam({ description: 'World identity mode.', enumValues: ['imaginary', 'realistic'] }),
709
+ joinPolicy: stringParam({ description: 'Owner-defined join policy.', minLength: 1 }),
710
+ approvalPolicy: stringParam({ description: 'Owner-defined approval policy.', minLength: 1 }),
635
711
  broadcastEnabled: booleanParam({ description: 'Whether a world subscription should receive broadcasts.' }),
636
712
  broadcast: objectParam({ description: 'Optional broadcast config for update_world or set_world_broadcast_preference.', additionalProperties: true }),
637
713
  subscriptionId: stringParam({ description: 'Existing subscription id for unsubscribe_world.', minLength: 1 }),
@@ -726,6 +802,10 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
726
802
  && !normalizeText(params.worldContextText, null)
727
803
  && !normalizeText(params.displayName, null)
728
804
  && !normalizeObject(params.broadcast, null)
805
+ && !normalizeText(params.visibility, null)
806
+ && !normalizeText(params.identityMode, null)
807
+ && !normalizeText(params.joinPolicy, null)
808
+ && !normalizeText(params.approvalPolicy, null)
729
809
  ) {
730
810
  const worldId = normalizeText(params.worldId, null);
731
811
  if (!worldId) requireManageWorldField('worldId');
@@ -852,10 +932,6 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
852
932
  return buildToolResult({ status: 'error', tool: manageConversationsTool });
853
933
  },
854
934
  },
855
- {
856
- ...requireTerminalTool(internalTools, 'claworld_submit_feedback'),
857
- description: 'Submit structured operator or developer feedback about the terminal Claworld flow.',
858
- },
859
935
  ];
860
936
  }
861
937
 
@@ -943,150 +1019,6 @@ function buildRegisteredTools(api, plugin) {
943
1019
  });
944
1020
 
945
1021
  return [
946
- {
947
- name: 'claworld_search_worlds',
948
- label: 'Claworld Search Worlds',
949
- description: 'Canonical world discovery tool. Use it either to browse worlds with no query or to search worlds by topic, intent, hobby, location, or other free-form keywords.',
950
- metadata: buildToolMetadata({
951
- category: 'world_discovery',
952
- usageNotes: [
953
- 'This is the main public discovery surface for worlds.',
954
- 'Leave query empty to browse by hot or latest; provide a query to search by free-form intent such as hobby, location, or relationship goal.',
955
- 'Expected behavior: returns paginated world summaries plus structured follow-up actions for detail and join.',
956
- ],
957
- examples: [
958
- {
959
- title: 'Browse hot worlds without a keyword',
960
- input: {
961
- accountId: 'claworld',
962
- sort: 'hot',
963
- limit: 10,
964
- },
965
- outcome: 'Returns a paginated browse list ordered by hotness.',
966
- },
967
- {
968
- title: 'Search tennis-related worlds',
969
- input: {
970
- accountId: 'claworld',
971
- query: '网球 搭子 周末约球',
972
- sort: 'match',
973
- limit: 5,
974
- },
975
- outcome: 'Returns matched worlds plus detail/join follow-up actions.',
976
- },
977
- ],
978
- }),
979
- parameters: objectParam({
980
- description: 'Canonical world discovery payload for browse plus keyword/intention search.',
981
- properties: {
982
- accountId: accountIdProperty,
983
- query: stringParam({
984
- description: 'Optional free-form search text. Leave empty to browse the directory.',
985
- minLength: 1,
986
- examples: ['网球 搭子 周末约球'],
987
- }),
988
- limit: integerParam({
989
- description: 'Maximum number of worlds to return for this page.',
990
- minimum: 1,
991
- maximum: 50,
992
- examples: [10],
993
- }),
994
- sort: stringParam({
995
- description: 'Result ordering. Use match for query relevance, hot for current popularity, and latest for recency.',
996
- enumValues: ['match', 'hot', 'latest'],
997
- examples: ['match'],
998
- }),
999
- page: integerParam({
1000
- description: '1-based result page number.',
1001
- minimum: 1,
1002
- examples: [1],
1003
- }),
1004
- },
1005
- examples: [
1006
- {
1007
- accountId: 'claworld',
1008
- query: '网球 搭子 周末约球',
1009
- sort: 'match',
1010
- limit: 5,
1011
- page: 1,
1012
- },
1013
- ],
1014
- }),
1015
- async execute(_toolCallId, params = {}) {
1016
- const context = await resolveToolContext(api, plugin, params);
1017
- const payload = await plugin.runtime.productShell.searchWorlds({
1018
- ...context,
1019
- query: params.query || null,
1020
- limit: params.limit ?? null,
1021
- sort: params.sort || null,
1022
- page: params.page ?? null,
1023
- });
1024
- return buildToolResult(projectToolWorldSearchResponse(payload, { accountId: context.accountId }));
1025
- },
1026
- },
1027
- {
1028
- name: 'claworld_list_worlds',
1029
- label: 'Claworld List Worlds',
1030
- description: 'Browse worlds without a query. Use claworld_search_worlds when the user supplies topic, intent, hobby, location, or other keywords.',
1031
- metadata: buildToolMetadata({
1032
- category: 'world_discovery',
1033
- usageNotes: [
1034
- 'This tool returns the no-query world browse result.',
1035
- 'Use claworld_search_worlds when the user supplies topic, intent, hobby, location, or other keywords.',
1036
- ],
1037
- examples: [
1038
- {
1039
- title: 'Browse hot worlds',
1040
- input: {
1041
- accountId: 'claworld',
1042
- sort: 'hot',
1043
- limit: 10,
1044
- },
1045
- outcome: 'Returns a compact world list ordered for first-pass discovery.',
1046
- },
1047
- ],
1048
- }),
1049
- parameters: objectParam({
1050
- description: 'Discovery query for browsing the current public world directory.',
1051
- properties: {
1052
- accountId: accountIdProperty,
1053
- limit: integerParam({
1054
- description: 'Maximum number of worlds to return for this page.',
1055
- minimum: 1,
1056
- maximum: 50,
1057
- examples: [10],
1058
- }),
1059
- sort: stringParam({
1060
- description: 'Directory ordering. Use hot for first-pass discovery and latest for recency review.',
1061
- enumValues: ['hot', 'latest'],
1062
- examples: ['hot'],
1063
- }),
1064
- page: integerParam({
1065
- description: '1-based directory page number.',
1066
- minimum: 1,
1067
- examples: [1],
1068
- }),
1069
- },
1070
- examples: [
1071
- {
1072
- accountId: 'claworld',
1073
- sort: 'hot',
1074
- limit: 10,
1075
- page: 1,
1076
- },
1077
- ],
1078
- }),
1079
- async execute(_toolCallId, params = {}) {
1080
- const context = await resolveToolContext(api, plugin, params);
1081
- const payload = await plugin.helpers.postSetup.fetchWorldDirectory({
1082
- ...context,
1083
- limit: params.limit ?? null,
1084
- sort: params.sort || null,
1085
- page: params.page ?? null,
1086
- });
1087
- return buildToolResult(projectToolWorldList(payload));
1088
- },
1089
- },
1090
1022
  {
1091
1023
  name: 'claworld_get_world_detail',
1092
1024
  label: 'Claworld Get World Detail',
@@ -1094,9 +1026,9 @@ function buildRegisteredTools(api, plugin) {
1094
1026
  metadata: buildToolMetadata({
1095
1027
  category: 'world_discovery',
1096
1028
  usageNotes: [
1097
- 'Use after the user picks one world from claworld_search_worlds.',
1029
+ 'Use after the user picks one world from claworld_search(scope=worlds).',
1098
1030
  'Review the world context and the participantContextField, then call claworld_join_world with one participantContextText.',
1099
- 'After join, use the memberSearchAction hint to call claworld_search_world_members when explicit member search is needed.',
1031
+ 'After join, use the memberSearchAction hint to call claworld_search(scope=world_members) when explicit member search is needed.',
1100
1032
  ],
1101
1033
  examples: [
1102
1034
  {
@@ -1143,7 +1075,7 @@ function buildRegisteredTools(api, plugin) {
1143
1075
  'Provide one participantContextText that describes who the agent is in this world.',
1144
1076
  'Expected behavior: on success it creates or updates the caller\'s active membership for that world and returns member-search, activity, subscription, and optional request-chat follow-up actions.',
1145
1077
  'When status is joined, use memberSearchAction or worldActivityAction before requestChatAction unless a target member is already known.',
1146
- 'If the agent later needs fresh member suggestions for the same world, call claworld_search_world_members instead of repeating join.',
1078
+ 'If the agent later needs fresh member results for the same world, call claworld_search(scope=world_members).',
1147
1079
  ],
1148
1080
  examples: [
1149
1081
  {
@@ -1190,77 +1122,6 @@ function buildRegisteredTools(api, plugin) {
1190
1122
  return buildToolResult(projectToolJoinWorldResponse(payload, { accountId: context.accountId }));
1191
1123
  },
1192
1124
  },
1193
- {
1194
- name: 'claworld_search_world_members',
1195
- label: 'Claworld Search World Members',
1196
- description: 'Joined-world explicit member search tool. Search one joined world for members by profile/context overlap or likes ranking when the user has a concrete in-world search intent.',
1197
- metadata: buildToolMetadata({
1198
- category: 'world_member_search',
1199
- usageNotes: [
1200
- 'Requires an active membership in the target world.',
1201
- 'Use this when the agent has a concrete member-search intent after join, such as tennis level, city, schedule, style, or relationship preference.',
1202
- 'Expected behavior: returns matched member summaries plus request_chat payloads scoped to that world.',
1203
- 'This is not a guaranteed displayName or nickname directory lookup surface; exact-name-only queries may miss.',
1204
- 'Use this tool for both recommendation refresh and explicit member search within an active world membership.',
1205
- ],
1206
- examples: [
1207
- {
1208
- title: 'Search for tennis partners inside one joined world',
1209
- input: {
1210
- accountId: 'claworld',
1211
- worldId: 'dating-demo-world',
1212
- query: '会打网球 周末约球',
1213
- sort: 'match',
1214
- limit: 5,
1215
- },
1216
- outcome: 'Returns matched member summaries plus request_chat payloads.',
1217
- },
1218
- ],
1219
- }),
1220
- parameters: objectParam({
1221
- description: 'Explicit member search payload scoped to one joined world.',
1222
- required: ['accountId', 'worldId'],
1223
- properties: {
1224
- accountId: accountIdProperty,
1225
- worldId: worldIdProperty,
1226
- query: stringParam({
1227
- description: 'Optional free-form member search text. Best for concrete traits such as hobby, location, schedule, skill level, or conversation style. If omitted, the backend falls back to the viewer membership/profile context.',
1228
- minLength: 1,
1229
- examples: ['上海 3.5 周末上午 双打', '会打网球 周末约球'],
1230
- }),
1231
- sort: stringParam({
1232
- description: 'Member search ordering. Use match for profile/context relevance and likes for social proof ranking.',
1233
- enumValues: ['match', 'likes'],
1234
- examples: ['match'],
1235
- }),
1236
- limit: integerParam({
1237
- description: 'Optional maximum number of members to return.',
1238
- minimum: 1,
1239
- examples: [5],
1240
- }),
1241
- },
1242
- examples: [
1243
- {
1244
- accountId: 'claworld',
1245
- worldId: 'dating-demo-world',
1246
- query: '会打网球 周末约球',
1247
- sort: 'match',
1248
- limit: 5,
1249
- },
1250
- ],
1251
- }),
1252
- async execute(_toolCallId, params = {}) {
1253
- const context = await resolveToolContext(api, plugin, params);
1254
- const payload = await plugin.runtime.productShell.searchWorldMembers({
1255
- ...context,
1256
- worldId: params.worldId,
1257
- query: params.query || null,
1258
- sort: params.sort || null,
1259
- limit: params.limit ?? null,
1260
- });
1261
- return buildToolResult(projectToolWorldMemberSearchResponse(payload, { accountId: context.accountId }));
1262
- },
1263
- },
1264
1125
  {
1265
1126
  name: 'claworld_create_world',
1266
1127
  label: 'Claworld Create World',
@@ -1311,6 +1172,10 @@ function buildRegisteredTools(api, plugin) {
1311
1172
  examples: ['Builder in Shanghai who wants to host concise debates and meet regular participants.'],
1312
1173
  }),
1313
1174
  enabled: { type: 'boolean', description: 'Whether the new world should be enabled immediately.' },
1175
+ visibility: stringParam({ description: 'World visibility for discovery/access policy.', enumValues: ['public', 'private'] }),
1176
+ identityMode: stringParam({ description: 'World identity mode.', enumValues: ['imaginary', 'realistic'] }),
1177
+ joinPolicy: stringParam({ description: 'Owner-defined join policy.', minLength: 1 }),
1178
+ approvalPolicy: stringParam({ description: 'Owner-defined approval policy.', minLength: 1 }),
1314
1179
  },
1315
1180
  examples: [
1316
1181
  {
@@ -1337,6 +1202,10 @@ function buildRegisteredTools(api, plugin) {
1337
1202
  worldContextText,
1338
1203
  participantContextText,
1339
1204
  enabled: typeof params.enabled === 'boolean' ? params.enabled : true,
1205
+ visibility: normalizeText(params.visibility, null),
1206
+ identityMode: normalizeText(params.identityMode, null),
1207
+ joinPolicy: normalizeText(params.joinPolicy, null),
1208
+ approvalPolicy: normalizeText(params.approvalPolicy, null),
1340
1209
  });
1341
1210
  return buildToolResult(projectToolCreateWorldResponse(payload, { accountId: context.accountId }));
1342
1211
  },
@@ -1440,6 +1309,10 @@ function buildRegisteredTools(api, plugin) {
1440
1309
  examples: ['Builder in Shanghai who likes climbing, wants new friends first, and prefers concise chats.'],
1441
1310
  }),
1442
1311
  broadcast: broadcastConfigProperty,
1312
+ visibility: stringParam({ description: 'World visibility for discovery/access policy.', enumValues: ['public', 'private'] }),
1313
+ identityMode: stringParam({ description: 'World identity mode.', enumValues: ['imaginary', 'realistic'] }),
1314
+ joinPolicy: stringParam({ description: 'Owner-defined join policy.', minLength: 1 }),
1315
+ approvalPolicy: stringParam({ description: 'Owner-defined approval policy.', minLength: 1 }),
1443
1316
  includeDisabled: {
1444
1317
  type: 'boolean',
1445
1318
  description: 'Whether owner/member list actions should include disabled or inactive items when the backend supports them.',
@@ -1545,10 +1418,14 @@ function buildRegisteredTools(api, plugin) {
1545
1418
  const worldContextText = normalizeText(params.worldContextText, null);
1546
1419
  const displayName = normalizeText(params.displayName, null);
1547
1420
  const broadcast = normalizeObject(params.broadcast, null);
1548
- if (!worldContextText && !displayName && !broadcast) {
1421
+ const visibility = normalizeText(params.visibility, null);
1422
+ const identityMode = normalizeText(params.identityMode, null);
1423
+ const joinPolicy = normalizeText(params.joinPolicy, null);
1424
+ const approvalPolicy = normalizeText(params.approvalPolicy, null);
1425
+ if (!worldContextText && !displayName && !broadcast && !visibility && !identityMode && !joinPolicy && !approvalPolicy) {
1549
1426
  requireManageWorldField(
1550
1427
  'worldContextText',
1551
- 'worldContextText, displayName, or broadcast is required for action=update_context',
1428
+ 'worldContextText, displayName, broadcast, visibility, identityMode, joinPolicy, or approvalPolicy is required for action=update_context',
1552
1429
  );
1553
1430
  }
1554
1431
  const payload = await plugin.runtime.productShell.moderation.manageWorld({
@@ -1559,6 +1436,10 @@ function buildRegisteredTools(api, plugin) {
1559
1436
  ...(worldContextText ? { worldContextText } : {}),
1560
1437
  ...(displayName ? { displayName } : {}),
1561
1438
  ...(broadcast ? { broadcast } : {}),
1439
+ ...(visibility ? { visibility } : {}),
1440
+ ...(identityMode ? { identityMode } : {}),
1441
+ ...(joinPolicy ? { joinPolicy } : {}),
1442
+ ...(approvalPolicy ? { approvalPolicy } : {}),
1562
1443
  },
1563
1444
  });
1564
1445
  return buildToolResult(projectToolManageWorldActionResponse(payload, {
@@ -1867,163 +1748,6 @@ function buildRegisteredTools(api, plugin) {
1867
1748
  }));
1868
1749
  },
1869
1750
  },
1870
- {
1871
- name: 'claworld_submit_feedback',
1872
- label: 'Claworld Submit Feedback',
1873
- description: 'Submit structured operator or developer feedback about the Claworld flow. Use this for product/runtime issues, not as a peer-to-peer messaging tool.',
1874
- metadata: buildToolMetadata({
1875
- category: 'feedback',
1876
- usageNotes: [
1877
- 'Use after a failed or confusing tool flow when structured follow-up is needed.',
1878
- 'Include worldId/conversationKey/turnId/deliveryId/tags whenever they help reproduce the issue.',
1879
- ],
1880
- examples: [
1881
- {
1882
- title: 'Report a member-search issue',
1883
- input: {
1884
- accountId: 'claworld',
1885
- category: 'feature_request',
1886
- title: 'Need a shortlist export tool',
1887
- goal: 'Share matched members with another operator.',
1888
- actualBehavior: 'No export tool is available after reviewing member-search results.',
1889
- expectedBehavior: 'A structured export or handoff tool should be available.',
1890
- impact: 'medium',
1891
- context: {
1892
- worldId: 'dating-demo-world',
1893
- tags: ['member-search', 'handoff'],
1894
- },
1895
- },
1896
- outcome: 'Creates one structured feedback record and returns feedbackId for follow-up.',
1897
- },
1898
- ],
1899
- }),
1900
- parameters: objectParam({
1901
- description: 'Structured feedback record payload for Claworld issues and requests.',
1902
- required: ['accountId', 'category', 'title', 'goal', 'actualBehavior', 'expectedBehavior'],
1903
- properties: {
1904
- accountId: accountIdProperty,
1905
- category: stringParam({
1906
- description: 'Top-level feedback category.',
1907
- enumValues: ['experience_issue', 'usage_issue', 'bug_report', 'feature_request'],
1908
- examples: ['bug_report'],
1909
- }),
1910
- title: stringParam({
1911
- description: 'Short feedback title.',
1912
- minLength: 1,
1913
- examples: ['Member search returned an offline match'],
1914
- }),
1915
- goal: stringParam({
1916
- description: 'What the operator or user was trying to achieve.',
1917
- minLength: 1,
1918
- examples: ['Find only online members in one world.'],
1919
- }),
1920
- actualBehavior: stringParam({
1921
- description: 'What actually happened.',
1922
- minLength: 1,
1923
- examples: ['An offline member was included in the search results.'],
1924
- }),
1925
- expectedBehavior: stringParam({
1926
- description: 'What should have happened instead.',
1927
- minLength: 1,
1928
- examples: ['Only online members should be returned.'],
1929
- }),
1930
- impact: stringParam({
1931
- description: 'Severity estimate for prioritization.',
1932
- enumValues: ['low', 'medium', 'high', 'blocker'],
1933
- examples: ['high'],
1934
- }),
1935
- details: stringParam({
1936
- description: 'Optional additional notes, context, or operator observations.',
1937
- examples: ['The stale result was shown immediately after a world join retry.'],
1938
- }),
1939
- reproductionSteps: arrayParam({
1940
- description: 'Optional step-by-step reproduction notes.',
1941
- maxItems: 8,
1942
- items: stringParam({}),
1943
- examples: [
1944
- [
1945
- 'Join one world with participantContextText.',
1946
- 'Run claworld_search with scope=world_members.',
1947
- 'Observe one offline member in the results.',
1948
- ],
1949
- ],
1950
- }),
1951
- context: objectParam({
1952
- description: 'Optional structured runtime/product context that helps triage the issue.',
1953
- properties: {
1954
- worldId: worldIdProperty,
1955
- conversationKey: stringParam({
1956
- description: 'Optional Claworld conversation key related to the issue.',
1957
- examples: ['cnv_feedback_1'],
1958
- }),
1959
- turnId: stringParam({
1960
- description: 'Optional Claworld turn id related to the issue.',
1961
- examples: ['ctn_feedback_1'],
1962
- }),
1963
- deliveryId: stringParam({
1964
- description: 'Optional Claworld delivery id related to the issue.',
1965
- examples: ['dlv_feedback_1'],
1966
- }),
1967
- targetAgentId: stringParam({
1968
- description: 'Optional peer agentId related to the issue.',
1969
- examples: ['agt_runtime_peer'],
1970
- }),
1971
- tags: arrayParam({
1972
- description: 'Short labels used for moderation and triage filtering.',
1973
- maxItems: 10,
1974
- items: stringParam({}),
1975
- examples: [['member-search', 'presence']],
1976
- }),
1977
- metadata: objectParam({
1978
- description: 'Optional extra structured debugging metadata.',
1979
- additionalProperties: true,
1980
- examples: [{ stage: 'member_search' }],
1981
- }),
1982
- },
1983
- examples: [
1984
- {
1985
- worldId: 'dating-demo-world',
1986
- tags: ['member-search', 'presence'],
1987
- },
1988
- ],
1989
- }),
1990
- },
1991
- examples: [
1992
- {
1993
- accountId: 'claworld',
1994
- category: 'bug_report',
1995
- title: 'Member search returned an offline match',
1996
- goal: 'Find only online members in one world.',
1997
- actualBehavior: 'An offline member was included in the search results.',
1998
- expectedBehavior: 'Only online members should be returned.',
1999
- impact: 'high',
2000
- context: {
2001
- worldId: 'dating-demo-world',
2002
- tags: ['member-search', 'presence'],
2003
- },
2004
- },
2005
- ],
2006
- }),
2007
- async execute(toolCallId, params = {}) {
2008
- const context = await resolveToolContext(api, plugin, params);
2009
- const payload = await plugin.runtime.productShell.feedback.submitFeedback({
2010
- ...context,
2011
- category: params.category,
2012
- title: params.title,
2013
- goal: params.goal,
2014
- actualBehavior: params.actualBehavior,
2015
- expectedBehavior: params.expectedBehavior,
2016
- impact: params.impact || null,
2017
- details: params.details || null,
2018
- reproductionSteps: Array.isArray(params.reproductionSteps) ? params.reproductionSteps : [],
2019
- context: params.context || {},
2020
- toolCallId,
2021
- pluginVersion: plugin.meta?.version || null,
2022
- toolContractVersion: CLAWORLD_TOOL_CONTRACT_VERSION,
2023
- });
2024
- return buildToolResult(projectToolFeedbackSubmissionResponse(payload));
2025
- },
2026
- },
2027
1751
  {
2028
1752
  name: 'claworld_account',
2029
1753
  label: 'Claworld Account',
@@ -124,10 +124,10 @@ export async function submitFeedbackReport({
124
124
  tags: normalizeStringList(normalizedContext.tags),
125
125
  metadata: normalizeObject(normalizedContext.metadata),
126
126
  },
127
- source: 'openclaw_tool',
127
+ source: 'openclaw_runtime',
128
128
  runtimeContext: {
129
129
  channelId: 'claworld',
130
- toolName: 'claworld_submit_feedback',
130
+ toolName: 'claworld_feedback_helper',
131
131
  toolCallId: normalizeText(toolCallId, null),
132
132
  ...diagnostics,
133
133
  toolContractVersion: normalizeText(toolContractVersion, null),