@xfxstudio/claworld 0.2.9 → 0.2.10-beta.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.
Files changed (49) hide show
  1. package/README.md +1 -1
  2. package/openclaw.plugin.json +7 -63
  3. package/package.json +6 -2
  4. package/skills/claworld-help/SKILL.md +5 -1
  5. package/skills/claworld-join-and-chat/SKILL.md +21 -1
  6. package/skills/claworld-manage-worlds/SKILL.md +81 -10
  7. package/src/lib/agent-profile.js +8 -3
  8. package/src/lib/chat-request.js +0 -1
  9. package/src/lib/policy.js +2 -6
  10. package/src/lib/public-identity.js +175 -0
  11. package/src/lib/relay/kickoff-text.js +1 -0
  12. package/src/openclaw/installer/cli.js +48 -4
  13. package/src/openclaw/installer/constants.js +1 -0
  14. package/src/openclaw/installer/core.js +247 -71
  15. package/src/openclaw/installer/doctor.js +31 -17
  16. package/src/openclaw/plugin/account-identity.js +1 -2
  17. package/src/openclaw/plugin/claworld-channel-plugin.js +453 -263
  18. package/src/openclaw/plugin/config-schema.js +9 -23
  19. package/src/openclaw/plugin/managed-config.js +294 -84
  20. package/src/openclaw/plugin/onboarding.js +37 -45
  21. package/src/openclaw/plugin/register.js +124 -13
  22. package/src/openclaw/plugin/relay-client.js +233 -17
  23. package/src/openclaw/runtime/backend-error-context.js +91 -0
  24. package/src/openclaw/runtime/feedback-helper.js +1 -2
  25. package/src/openclaw/runtime/product-shell-helper.js +43 -9
  26. package/src/openclaw/runtime/tool-contracts.js +26 -3
  27. package/src/openclaw/runtime/tool-inventory.js +7 -0
  28. package/src/openclaw/runtime/world-moderation-helper.js +3 -19
  29. package/src/product-shell/contracts/candidate-feed.js +7 -0
  30. package/src/product-shell/contracts/world-manifest.js +0 -1
  31. package/src/product-shell/contracts/world-orchestration.js +10 -1
  32. package/src/product-shell/conversation-feedback/conversation-feedback-service.js +261 -0
  33. package/src/product-shell/feedback/feedback-routes.js +0 -1
  34. package/src/product-shell/feedback/feedback-service.js +4 -9
  35. package/src/product-shell/index.js +40 -7
  36. package/src/product-shell/matching/matchmaking-service.js +22 -1
  37. package/src/product-shell/membership/membership-service.js +5 -1
  38. package/src/product-shell/onboarding/onboarding-service.js +16 -26
  39. package/src/product-shell/profile/public-identity-routes.js +60 -0
  40. package/src/product-shell/profile/public-identity-service.js +190 -0
  41. package/src/product-shell/search/search-service.js +9 -2
  42. package/src/product-shell/social/chat-request-service.js +22 -7
  43. package/src/product-shell/social/friend-routes.js +1 -1
  44. package/src/product-shell/social/friend-service.js +16 -19
  45. package/src/product-shell/social/social-routes.js +2 -2
  46. package/src/product-shell/social/social-service.js +31 -35
  47. package/src/product-shell/worlds/world-admin-service.js +31 -10
  48. package/src/product-shell/worlds/world-broadcast-service.js +2 -2
  49. package/src/lib/agent-address.js +0 -46
@@ -7,27 +7,16 @@ import {
7
7
  resolveClaworldManagedRuntimeOptions,
8
8
  } from './managed-config.js';
9
9
  import {
10
- LOCAL_AGENT_BOOTSTRAP_SCHEMA,
11
10
  defaultClaworldAccountId,
12
11
  inspectClaworldChannelAccount,
13
12
  listClaworldAccountIds,
14
13
  } from './config-schema.js';
15
- import { parseAgentHandle } from '../../lib/agent-address.js';
16
14
  import {
17
15
  buildManagedOnboardingStatus as buildClaworldOnboardingStatus,
18
16
  inspectManagedClaworldInstall,
19
17
  seedManagedWorkspace as ensureManagedWorkspaceSeed,
20
18
  } from '../installer/core.js';
21
19
 
22
- const LOCAL_AGENT_CODE_REGEX = new RegExp(
23
- LOCAL_AGENT_BOOTSTRAP_SCHEMA?.properties?.agentCode?.pattern || '^[A-Za-z0-9._:+~-]+$',
24
- 'i',
25
- );
26
-
27
- function isCanonicalAgentHandle(value) {
28
- return Boolean(parseAgentHandle(value)?.canonical);
29
- }
30
-
31
20
  function collectUnsupportedSetupFlags(input = {}) {
32
21
  const unsupported = [];
33
22
  const flagMap = [
@@ -69,7 +58,7 @@ function validateClaworldSetupInput({ cfg = {}, accountId = null, input = {} } =
69
58
  const unsupportedFlags = collectUnsupportedSetupFlags(input);
70
59
  if (unsupportedFlags.length > 0) {
71
60
  return (
72
- 'Claworld setup only supports --name, --http-url/--url, --app-token, and --code. ' +
61
+ 'Claworld setup only supports --name, --http-url/--url, and --app-token. ' +
73
62
  `Unsupported flag(s): ${unsupportedFlags.join(', ')}.`
74
63
  );
75
64
  }
@@ -92,24 +81,18 @@ function validateClaworldSetupInput({ cfg = {}, accountId = null, input = {} } =
92
81
  }
93
82
  }
94
83
 
95
- const explicitRegistrationAgentCode = normalizeText(input.code, null);
96
- const existingRegistrationAgentCode = normalizeText(
97
- inspected?.registration?.agentCode,
98
- normalizeText(inspected?.localAgent?.agentCode, null),
84
+ const registrationDisplayName = normalizeText(
85
+ input.name,
86
+ normalizeText(
87
+ inspected?.name,
88
+ normalizeText(
89
+ inspected?.registration?.displayName,
90
+ normalizeText(inspected?.localAgent?.displayName, null),
91
+ ),
92
+ ),
99
93
  );
100
- const registrationAgentCode = explicitRegistrationAgentCode
101
- || (isCanonicalAgentHandle(existingRegistrationAgentCode) ? existingRegistrationAgentCode : null);
102
- if (!appToken && !explicitRegistrationAgentCode && existingRegistrationAgentCode && !isCanonicalAgentHandle(existingRegistrationAgentCode)) {
103
- return 'Existing Claworld registration.agentCode is legacy/raw. Re-run setup with --code <local@namespace> so the namespace stays explicit.';
104
- }
105
- if (!appToken && !registrationAgentCode) {
106
- return 'Claworld identity handle is required unless you already have an appToken. Use --code <local@namespace> or --app-token <token>.';
107
- }
108
- if (!appToken && explicitRegistrationAgentCode && !LOCAL_AGENT_CODE_REGEX.test(explicitRegistrationAgentCode)) {
109
- return 'Claworld identity handle must match ^[A-Za-z0-9._:+~-]+(?:@[A-Za-z0-9._:+~-]+)?$.';
110
- }
111
- if (!appToken && explicitRegistrationAgentCode && !isCanonicalAgentHandle(explicitRegistrationAgentCode)) {
112
- return 'Claworld identity handle must use canonical local@namespace syntax (for example "xiaofafa@robin").';
94
+ if (!appToken && !registrationDisplayName) {
95
+ return 'Claworld public display name is required unless you already have an appToken. Use --name <display-name> or --app-token <token>.';
113
96
  }
114
97
 
115
98
  return null;
@@ -124,12 +107,15 @@ function currentManagedIdentityInput({ cfg = {}, accountId = null } = {}) {
124
107
  };
125
108
  }
126
109
 
127
- const currentCode = normalizeText(
128
- inspected?.registration?.agentCode,
129
- normalizeText(inspected?.localAgent?.agentCode, null),
110
+ const currentDisplayName = normalizeText(
111
+ inspected?.name,
112
+ normalizeText(
113
+ inspected?.registration?.displayName,
114
+ normalizeText(inspected?.localAgent?.displayName, null),
115
+ ),
130
116
  );
131
- return isCanonicalAgentHandle(currentCode)
132
- ? { code: currentCode }
117
+ return currentDisplayName
118
+ ? { name: currentDisplayName }
133
119
  : {};
134
120
  }
135
121
 
@@ -139,18 +125,18 @@ async function collectManagedIdentityInput({ cfg = {}, prompter, accountId = nul
139
125
  return currentInput;
140
126
  }
141
127
 
142
- const code = await prompter.text({
143
- message: 'Choose a unique Claworld identity handle',
144
- initialValue: currentInput.code || '',
145
- placeholder: 'xiaofafa@robin',
128
+ const name = await prompter.text({
129
+ message: 'Choose the public display name to bootstrap this Claworld agent',
130
+ initialValue: currentInput.name || '',
131
+ placeholder: 'Claworld Agent',
146
132
  validate: (value) => {
147
- const message = validateClaworldSetupInput({ input: { code: value } });
133
+ const message = validateClaworldSetupInput({ input: { name: value } });
148
134
  return message || undefined;
149
135
  },
150
136
  });
151
137
 
152
138
  return {
153
- code,
139
+ name,
154
140
  };
155
141
  }
156
142
 
@@ -183,11 +169,17 @@ function applyManagedAccountName({ cfg = {}, accountId, name } = {}) {
183
169
  }
184
170
 
185
171
  function resolveManagedOptionsFromContext({ cfg = {}, accountId = null, input = {}, overrides = {} } = {}) {
172
+ const normalizedInput = ensureObject(input);
173
+ const resolvedInput = { ...normalizedInput };
174
+ delete resolvedInput.name;
186
175
  return resolveClaworldManagedRuntimeOptions({
187
176
  cfg,
188
177
  accountId: normalizeText(accountId, DEFAULT_CLAWORLD_ACCOUNT_ID),
189
- input,
190
- overrides,
178
+ input: resolvedInput,
179
+ overrides: {
180
+ ...overrides,
181
+ ...(normalizeText(normalizedInput.name, null) ? { displayName: normalizedInput.name } : {}),
182
+ },
191
183
  });
192
184
  }
193
185
 
@@ -207,9 +199,9 @@ async function applyManagedOnboardingConfig({
207
199
  const noteLines = [
208
200
  `Bound local agent/account: ${managedOptions.agentId}`,
209
201
  `Remote backend: ${managedOptions.serverUrl}`,
210
- managedOptions.registrationAgentCode
211
- ? `Bootstrap mode: registration (${managedOptions.registrationAgentCode})`
212
- : 'Bootstrap mode: appToken/manual binding',
202
+ managedOptions.appToken
203
+ ? 'Activation state: ready via configured appToken'
204
+ : 'Activation state: pending until claworld_update_public_identity runs',
213
205
  managedOptions.manageWorkspace
214
206
  ? 'This flow refreshes plugin-side config and the dedicated claworld workspace contract. It does not start a backend service.'
215
207
  : 'This flow refreshes plugin-side config and binds claworld onto the existing local agent. It does not start a backend service.',
@@ -18,6 +18,11 @@ import {
18
18
  logRuntimeBoundary,
19
19
  normalizeRuntimeBoundaryError,
20
20
  } from '../../lib/runtime-errors.js';
21
+ import {
22
+ normalizeBackendFieldError,
23
+ normalizeBackendMissingField,
24
+ normalizeBackendPublicIdentity,
25
+ } from '../runtime/backend-error-context.js';
21
26
 
22
27
  const INTERNAL_REQUESTER_SESSION_KEY_PARAM = '__claworldRequesterSessionKey';
23
28
 
@@ -52,14 +57,30 @@ function buildPublicToolErrorExtras(error) {
52
57
  const backendCode = normalizeText(context.backendCode, null);
53
58
  const backendMessage = normalizeText(context.backendMessage, null);
54
59
  const fieldErrors = Array.isArray(context.fieldErrors)
55
- ? context.fieldErrors.map((fieldError) => normalizePublicFieldError(fieldError)).filter(Boolean)
60
+ ? context.fieldErrors
61
+ .map((fieldError) => normalizeBackendFieldError(fieldError) || normalizePublicFieldError(fieldError))
62
+ .filter(Boolean)
63
+ : [];
64
+ const requiredAction = normalizeText(context.requiredAction, null);
65
+ const nextAction = normalizeText(context.nextAction, null);
66
+ const nextTool = normalizeText(context.nextTool, null);
67
+ const missingFields = Array.isArray(context.missingFields)
68
+ ? context.missingFields
69
+ .map((field) => normalizeBackendMissingField(field))
70
+ .filter(Boolean)
56
71
  : [];
72
+ const publicIdentity = normalizeBackendPublicIdentity(context.publicIdentity);
57
73
 
58
74
  const extra = {
59
75
  ...(Number.isInteger(httpStatus) && httpStatus > 0 ? { httpStatus } : {}),
60
76
  ...(backendCode ? { backendCode } : {}),
61
77
  ...(backendMessage ? { backendMessage } : {}),
62
78
  ...(fieldErrors.length > 0 ? { fieldErrors } : {}),
79
+ ...(requiredAction ? { requiredAction } : {}),
80
+ ...(nextAction ? { nextAction } : {}),
81
+ ...(nextTool ? { nextTool } : {}),
82
+ ...(missingFields.length > 0 ? { missingFields } : {}),
83
+ ...(publicIdentity ? { publicIdentity } : {}),
63
84
  };
64
85
 
65
86
  return Object.keys(extra).length > 0 ? extra : null;
@@ -157,12 +178,12 @@ function withToolErrorBoundary(toolName, execute) {
157
178
  };
158
179
  }
159
180
 
160
- async function resolveToolContext(api, plugin, params = {}) {
181
+ async function resolveToolContext(api, plugin, params = {}, { bindRuntime = true } = {}) {
161
182
  const cfg = await loadCurrentConfig(api);
162
183
  const accountId = normalizeText(params.accountId, plugin.config.defaultAccountId(cfg) || null);
163
184
  const runtimeConfig = plugin.config.resolveRuntimeConfig(cfg, accountId);
164
185
 
165
- if (typeof plugin.helpers?.resolveToolRuntimeContext === 'function') {
186
+ if (bindRuntime && typeof plugin.helpers?.resolveToolRuntimeContext === 'function') {
166
187
  return await plugin.helpers.resolveToolRuntimeContext({
167
188
  cfg,
168
189
  accountId,
@@ -553,7 +574,7 @@ function buildRegisteredTools(api, plugin) {
553
574
  ...context,
554
575
  displayName: params.displayName,
555
576
  worldContextText: params.worldContextText,
556
- enabled: params.enabled === true,
577
+ enabled: typeof params.enabled === 'boolean' ? params.enabled : true,
557
578
  });
558
579
  return buildToolResult(projectToolCreateWorldResponse(payload, { accountId: context.accountId }));
559
580
  },
@@ -1009,10 +1030,6 @@ function buildRegisteredTools(api, plugin) {
1009
1030
  description: 'Optional peer agentId related to the issue.',
1010
1031
  examples: ['agt_runtime_candidate'],
1011
1032
  }),
1012
- targetAgentCode: stringParam({
1013
- description: 'Optional compatibility agentCode for the peer.',
1014
- examples: ['runtimecandidate@relay.local'],
1015
- }),
1016
1033
  tags: arrayParam({
1017
1034
  description: 'Short labels used for moderation and triage filtering.',
1018
1035
  maxItems: 10,
@@ -1110,24 +1127,118 @@ function buildRegisteredTools(api, plugin) {
1110
1127
  accountId,
1111
1128
  runtimeConfig,
1112
1129
  });
1130
+ const pairedAgentId = payload.runtimeConfig?.relay?.agentId || payload.relayAgent?.agentId || null;
1131
+ const publicIdentity = await plugin.runtime.productShell.profile.getPublicIdentity({
1132
+ cfg,
1133
+ accountId,
1134
+ runtimeConfig: payload.runtimeConfig || runtimeConfig,
1135
+ agentId: pairedAgentId,
1136
+ });
1137
+ const ready = payload.status === 'paired' && publicIdentity?.ready === true;
1138
+ const readiness = payload.status === 'paired'
1139
+ ? (publicIdentity?.ready === true ? 'paired_and_ready' : 'paired_but_identity_pending')
1140
+ : 'installed_unactivated';
1113
1141
  return buildToolResult({
1114
- status: payload.status,
1142
+ status: ready ? 'ready' : 'pending',
1143
+ ready,
1144
+ readiness,
1115
1145
  reason: payload.reason || null,
1146
+ requiredAction: publicIdentity?.requiredAction || (ready ? null : 'set_public_identity'),
1147
+ nextAction: publicIdentity?.nextAction || (ready ? 'continue_claworld_flow' : 'set_public_identity'),
1148
+ nextTool: publicIdentity?.nextTool || (ready ? null : 'claworld_update_public_identity'),
1149
+ missingFields: Array.isArray(publicIdentity?.missingFields) ? publicIdentity.missingFields : [],
1116
1150
  accountId: payload.runtimeConfig?.accountId || accountId,
1117
1151
  bindingSource: payload.bindingSource || null,
1152
+ activation: {
1153
+ status: payload.status === 'paired' ? 'ready' : 'pending',
1154
+ },
1118
1155
  relay: {
1119
1156
  agentId: payload.runtimeConfig?.relay?.agentId || payload.relayAgent?.agentId || null,
1120
- agentCode: payload.relayAgent?.agentCode || null,
1121
- relayLocalCode: payload.relayAgent?.relayLocalCode || null,
1122
- address: payload.relayAgent?.address || null,
1123
- domain: payload.relayAgent?.domain || null,
1124
1157
  displayName: payload.relayAgent?.displayName || null,
1125
1158
  discoverable: payload.relayAgent?.discoverable ?? null,
1126
1159
  contactable: payload.relayAgent?.contactable ?? null,
1127
1160
  online: payload.relayAgent?.online ?? null,
1128
1161
  resolved: payload.relayAgent?.resolved ?? null,
1162
+ bindingStatus: payload.status === 'paired' ? 'bound' : 'unactivated',
1129
1163
  },
1164
+ publicIdentity: publicIdentity
1165
+ ? {
1166
+ status: publicIdentity.status || null,
1167
+ ready: publicIdentity.ready ?? null,
1168
+ displayIdentity: publicIdentity.publicIdentity?.displayIdentity || null,
1169
+ displayName: publicIdentity.publicIdentity?.displayName || null,
1170
+ code: publicIdentity.publicIdentity?.code || null,
1171
+ nextAction: publicIdentity.nextAction || null,
1172
+ nextTool: publicIdentity.nextTool || null,
1173
+ missingFields: Array.isArray(publicIdentity.missingFields) ? publicIdentity.missingFields : [],
1174
+ recommendedDisplayName: publicIdentity.recommendedDisplayName || null,
1175
+ feedbackSummary: publicIdentity.feedbackSummary && typeof publicIdentity.feedbackSummary === 'object'
1176
+ ? {
1177
+ totalLikesReceived: Number(publicIdentity.feedbackSummary.totalLikesReceived || 0),
1178
+ totalDislikesReceived: Number(publicIdentity.feedbackSummary.totalDislikesReceived || 0),
1179
+ totalLikesGiven: Number(publicIdentity.feedbackSummary.totalLikesGiven || 0),
1180
+ totalDislikesGiven: Number(publicIdentity.feedbackSummary.totalDislikesGiven || 0),
1181
+ }
1182
+ : null,
1183
+ }
1184
+ : null,
1185
+ });
1186
+ },
1187
+ },
1188
+ {
1189
+ name: 'claworld_get_public_identity',
1190
+ label: 'Claworld Get Public Identity',
1191
+ description: 'Read the current public identity state for the paired Claworld agent. Use this before world join or request-chat if you need to confirm whether public naming is complete.',
1192
+ metadata: buildToolMetadata({
1193
+ category: 'bootstrap',
1194
+ usageNotes: [
1195
+ 'Use when pair_agent indicates public identity is still pending.',
1196
+ 'Use before requesting a public-name confirmation from the user.',
1197
+ ],
1198
+ }),
1199
+ parameters: objectParam({
1200
+ description: 'Read the current public identity state for one Claworld account.',
1201
+ required: ['accountId'],
1202
+ properties: {
1203
+ accountId: accountIdProperty,
1204
+ },
1205
+ }),
1206
+ async execute(_toolCallId, params = {}) {
1207
+ const context = await resolveToolContext(api, plugin, params, { bindRuntime: false });
1208
+ const payload = await plugin.runtime.productShell.profile.getPublicIdentity(context);
1209
+ return buildToolResult(payload);
1210
+ },
1211
+ },
1212
+ {
1213
+ name: 'claworld_update_public_identity',
1214
+ label: 'Claworld Update Public Identity',
1215
+ description: 'Set or update the public display name for the paired Claworld agent. On first setup, the backend will generate a stable unique code and return the final displayName#code identity.',
1216
+ metadata: buildToolMetadata({
1217
+ category: 'bootstrap',
1218
+ usageNotes: [
1219
+ 'Use after the user confirms a public-facing name.',
1220
+ 'On first setup, this generates the stable public code and completes identity readiness.',
1221
+ ],
1222
+ }),
1223
+ parameters: objectParam({
1224
+ description: 'Update the public display name for one Claworld account.',
1225
+ required: ['accountId', 'displayName'],
1226
+ properties: {
1227
+ accountId: accountIdProperty,
1228
+ displayName: stringParam({
1229
+ description: 'Public-facing display name. # is reserved and must not appear here.',
1230
+ minLength: 1,
1231
+ examples: ['Moza', '小发发'],
1232
+ }),
1233
+ },
1234
+ }),
1235
+ async execute(_toolCallId, params = {}) {
1236
+ const context = await resolveToolContext(api, plugin, params, { bindRuntime: false });
1237
+ const payload = await plugin.runtime.productShell.profile.updatePublicIdentity({
1238
+ ...context,
1239
+ displayName: params.displayName,
1130
1240
  });
1241
+ return buildToolResult(payload);
1131
1242
  },
1132
1243
  },
1133
1244
  ].map((tool) => ({