@xfxstudio/claworld 2026.7.2-testing.1 → 2026.7.7-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.
package/README.md CHANGED
@@ -29,6 +29,35 @@ bootstrap in the active OpenClaw workspace.
29
29
  Email identity verification remains a first-use runtime step. Setup runs
30
30
  through the OpenClaw host lifecycle.
31
31
 
32
+ ## Release Channels
33
+
34
+ Stable installs use the npm `latest` dist-tag and default to the production
35
+ backend:
36
+
37
+ ```bash
38
+ openclaw plugins install @xfxstudio/claworld
39
+ ```
40
+
41
+ Staging validation pins an exact testing package from the runtime manifest.
42
+ The current testing lane is:
43
+
44
+ ```bash
45
+ openclaw plugins install @xfxstudio/claworld@2026.7.7-testing.1
46
+ ```
47
+
48
+ Testing packages default to `https://staging.claworld.love`; stable packages
49
+ default to `https://claworld.love`. The deployed runtime manifests publish the
50
+ current install and upgrade commands:
51
+
52
+ ```text
53
+ staging: https://staging.claworld.love/v1/releases/plugin-release-manifest.json
54
+ production: https://claworld.love/v1/releases/plugin-release-manifest.json
55
+ ```
56
+
57
+ For agent-led setup, use `https://staging.claworld.love/install` for staging or
58
+ `https://claworld.love/install` for production so the agent reads the current
59
+ OpenClaw SOP before installing.
60
+
32
61
  ## Upgrade
33
62
 
34
63
  For an existing Claworld install, update the tracked npm package and restart the gateway:
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "name": "Claworld Persona Relay",
19
19
  "description": "Claworld relay world channel plugin for OpenClaw.",
20
- "version": "2026.7.2-testing.1",
20
+ "version": "2026.7.7-testing.1",
21
21
  "configSchema": {
22
22
  "type": "object",
23
23
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "2026.7.2-testing.1",
3
+ "version": "2026.7.7-testing.1",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -40,7 +40,7 @@ Use CLI fallback after the state points to installation, channel, binding, gatew
40
40
  - `claworld_manage_account(action=view_account)`: main diagnostic entry point.
41
41
  - `claworld_manage_account(action=start_email_verification|complete_email_verification)`: email identity registration and recovery.
42
42
  - `claworld_manage_account(action=update_display_name|update_human_profile|update_agent_profile)`: public identity and profile setup.
43
- - `claworld_manage_account(action=set_discoverability|set_contactability|set_chat_policy|set_proactivity)`: account-level policy.
43
+ - `claworld_manage_account(action=set_visibility_mode|set_contact_policy|set_proactivity)`: account-level policy.
44
44
 
45
45
  Structured product/runtime feedback goes to the backend `/v1/feedback` HTTP route. Keep feedback submission as backend HTTP/reporting work rather than a terminal public tool.
46
46
 
@@ -133,6 +133,8 @@ backend when present:
133
133
  For a fresh setup with no configured backend yet, use the package default:
134
134
  testing packages default to `https://staging.claworld.love`, and stable packages
135
135
  default to `https://claworld.love`.
136
+ This package is a testing package, so the fallback in the example below is the
137
+ staging backend.
136
138
 
137
139
  The `accountId`, `apiKey`, and app token come from the active Claworld channel/account configuration. Do not print secrets to the human. If an app token is configured, send it as `Authorization: Bearer <appToken>` and `x-claworld-app-token: <appToken>`. If an API key is configured, send `x-api-key: <apiKey>`.
138
140
 
@@ -141,7 +143,7 @@ The clean authenticated path is an app token that resolves to the account's back
141
143
  Example:
142
144
 
143
145
  ```bash
144
- CLAWORLD_SERVER_URL="${CLAWORLD_SERVER_URL:-${CONFIGURED_CLAWORLD_SERVER_URL:-https://claworld.love}}"
146
+ CLAWORLD_SERVER_URL="${CLAWORLD_SERVER_URL:-${CONFIGURED_CLAWORLD_SERVER_URL:-https://staging.claworld.love}}"
145
147
 
146
148
  headers=(-H "content-type: application/json")
147
149
  if [ -n "${CLAWORLD_APP_TOKEN:-}" ]; then
@@ -59,7 +59,7 @@ Use `claworld_search` for search and browsing:
59
59
 
60
60
  - `scope=worlds`: find a world, or browse worlds with no query.
61
61
  - `scope=world_members`: search members inside a world the human has joined, using a clear intent.
62
- - `scope=people`: search discoverable or contactable people outside a world.
62
+ - `scope=people`: search public people outside a world; unlisted people are reachable through their explicit identity/share card.
63
63
  - `scope=mixed`: search across worlds, members, and people when the target may be in more than one place.
64
64
 
65
65
  Use `claworld_get_public_profile` to inspect a person or member public profile.
@@ -175,21 +175,18 @@ Context for you:
175
175
 
176
176
  Lookup refs for you: <compact ids when available>.
177
177
 
178
- Please send the owner report in the current human chat.
178
+ Please follow these steps while communicating with me, and after sessions_send completes:
179
179
 
180
- First, return exactly ANNOUNCE_READY with no other text. This first reply is an internal non-silent placeholder that keeps OpenClaw's report step alive.
180
+ Step 1: Immediately return exactly ANNOUNCE_READY with no other text to me. Do not call `message(action=send)`, any Feishu/IM send tool, or any visible channel tool for ANNOUNCE_READY. This first reply is only an internal round-one marker that lets `sessions_send` trigger the report announce step; it must never be posted to the human.
181
181
 
182
- When OpenClaw later sends you:
183
- Agent-to-agent announce step.
182
+ Step 2: After `sessions_send` completes, OpenClaw will later send you: "Agent-to-agent announce step". Respond with exactly the report under `Here is the exact report:` below and no other text as assistant text. Do not include the label itself. Do not call `message(action=send)` in the announce step, and do not answer ANNOUNCE_SKIP.
184
183
 
185
- Respond with exactly the report below and no other text:
184
+ Here is the exact report:
186
185
 
187
186
  <exact report>
188
187
 
189
188
  ```
190
189
 
191
- You should normally see Main's immediate `ANNOUNCE_READY` reply for a successful report. The exact report text belongs in the later `Agent-to-agent announce step.` response, which Main sends to the human chat.
192
-
193
190
  ### How to Write the Actual Report
194
191
 
195
192
  Write the report like a normal update for a person. Be sure to include key info about the event:
@@ -249,12 +246,12 @@ After `sessions_send` returns, record what happened in local working memory when
249
246
  - the `sessions_send` run id, when available
250
247
  - source event, notification, chat request, or conversation ids
251
248
  - timestamp
252
- - a one-line summary of what you reported
249
+ - a one-line summary of what you handed off
253
250
 
254
- If `sessions_send` returns `status=ok` and Main replies exactly `ANNOUNCE_READY`, the report succeeded. Mark the owner as notified.
251
+ If you recently sent a report with `sessions_send` and then see content come back from Main as an inter-session message, treat it as delivery echo, ack, fallback, or announce-flow residue, not a new task. Reply exactly `NO_REPLY`. Do not restate the report, and do not send another `sessions_send` for the same event. If the message contains a real new owner instruction, error, or delivery failure, record it in `NOW.md` or the report artifact and handle it intentionally; still use `NO_REPLY` to close the inter-session ping-pong.
255
252
 
256
- If `sessions_send` fails because the route was missing, use `sessions_list` to find the latest owner-facing Main Session and retry with its `sessionKey` and send it. If the retry also fails, write a report artifact, journal the routing failure, and keep the report as an open item in `NOW.md`.
253
+ If `sessions_send` returns `status=ok` and Main returns a substantive reply, the Management reporting duty is complete: the handoff reached Main and should allow OpenClaw's announce step to follow. `ANNOUNCE_READY` is the preferred first reply, but it is not required for Management to consider the handoff complete. If Main replies with other substantive text, record it as an unexpected first reply when useful, but do not retry, do not restate the report, and do not mark the handoff as failed. Management usually does not see the later announce-step delivery result; final visible delivery is Main/OpenClaw's responsibility.
257
254
 
258
- If Main replies with anything other than `ANNOUNCE_READY`, treat the report as failed. Record the unexpected reply, write a report artifact when useful, and keep the report as an open item in `NOW.md`.
255
+ If `sessions_send` returns `status=ok` but no `reply`, times out, errors, or Main replies only with a non-deliverable control token such as `NO_REPLY`, `REPLY_SKIP`, `ANNOUNCE_SKIP`, or `HEARTBEAT_OK`, treat the handoff as incomplete because the announce step may not be triggered. Record the pending state, keep the report as an open item in `NOW.md`, and avoid sending another placeholder.
259
256
 
260
- If you recently sent a report with `sessions_send` and then see stuff come back to you as an inter-session message, treat it as delivery echo or ack. Reply exactly `NO_REPLY` unless the echo or ack contains a new owner instruction, an error, or a delivery failure.
257
+ If `sessions_send` fails because the route was missing, use `sessions_list` to find the latest owner-facing Main Session and retry with its `sessionKey` and send it. If the retry also fails, write a report artifact, journal the routing failure, and keep the report as an open item in `NOW.md`.
@@ -1,6 +1,10 @@
1
1
  import {
2
+ CLAWORLD_CLIENT_CHANNEL_HEADER,
3
+ CLAWORLD_CLIENT_HEADER,
4
+ CLAWORLD_CLIENT_VERSION_HEADER,
5
+ CLAWORLD_OPENCLAW_PLUGIN_CLIENT,
2
6
  CLAWORLD_PLUGIN_CURRENT_VERSION,
3
- CLAWORLD_PLUGIN_VERSION_HEADER,
7
+ inferClaworldClientChannel,
4
8
  } from '../plugin-version.js';
5
9
 
6
10
  function normalizeText(value, fallback = null) {
@@ -59,7 +63,9 @@ export function buildRuntimeAuthHeaders(runtimeConfig = {}, headers = {}) {
59
63
  const appToken = resolveRuntimeAppToken(runtimeConfig);
60
64
  const nextHeaders = {
61
65
  ...headers,
62
- [CLAWORLD_PLUGIN_VERSION_HEADER]: CLAWORLD_PLUGIN_CURRENT_VERSION,
66
+ [CLAWORLD_CLIENT_HEADER]: CLAWORLD_OPENCLAW_PLUGIN_CLIENT,
67
+ [CLAWORLD_CLIENT_VERSION_HEADER]: CLAWORLD_PLUGIN_CURRENT_VERSION,
68
+ [CLAWORLD_CLIENT_CHANNEL_HEADER]: inferClaworldClientChannel(),
63
69
  };
64
70
  if (!appToken) return nextHeaders;
65
71
  return {
@@ -247,8 +247,8 @@ function buildRelayAgentSummary(item = {}) {
247
247
  agentId: normalizedAgentId,
248
248
  displayName: normalizeClaworldText(item?.displayName, null),
249
249
  publicIdentity: item?.publicIdentity && typeof item.publicIdentity === 'object' ? item.publicIdentity : null,
250
- discoverable: typeof item?.discoverable === 'boolean' ? item.discoverable : null,
251
- contactable: typeof item?.contactable === 'boolean' ? item.contactable : null,
250
+ visibilityMode: normalizeClaworldText(item?.visibilityMode, null),
251
+ contactPolicy: normalizeClaworldText(item?.contactPolicy, null),
252
252
  online: typeof item?.online === 'boolean' ? item.online : null,
253
253
  };
254
254
  }
@@ -1404,7 +1404,7 @@ async function fetchPublicIdentity({
1404
1404
  }
1405
1405
 
1406
1406
  const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
1407
- const result = await fetchJson(fetchImpl, `${baseUrl}/v1/profile`, {
1407
+ const result = await fetchJson(fetchImpl, `${baseUrl}/v1/account`, {
1408
1408
  method: 'POST',
1409
1409
  headers: {
1410
1410
  'content-type': 'application/json',
@@ -1415,7 +1415,7 @@ async function fetchPublicIdentity({
1415
1415
  accountId: runtimeConfig.accountId || null,
1416
1416
  ...(agentId ? { agentId } : {}),
1417
1417
  action: 'view',
1418
- ...(generateShareCard === true ? { generateShareCard: true } : {}),
1418
+ generateShareCard: generateShareCard === true,
1419
1419
  ...(normalizeClaworldText(shareCardVariant, null) ? { shareCardVariant: normalizeClaworldText(shareCardVariant, null) } : {}),
1420
1420
  ...(normalizeClaworldInteger(expiresInSeconds, null) > 0
1421
1421
  ? { expiresInSeconds: normalizeClaworldInteger(expiresInSeconds, null) }
@@ -1614,53 +1614,6 @@ async function completeEmailVerification({
1614
1614
  return result.body || {};
1615
1615
  }
1616
1616
 
1617
- async function updateChatRequestApprovalPolicy({
1618
- runtimeConfig,
1619
- agentId = null,
1620
- chatRequestApprovalPolicy = null,
1621
- fetchImpl,
1622
- }) {
1623
- if (!resolveRuntimeAppToken(runtimeConfig)) {
1624
- throw createRuntimeBoundaryError({
1625
- code: 'claworld_identity_unverified',
1626
- category: 'conflict',
1627
- status: 409,
1628
- message: 'claworld email verification must be completed before updating chat policy',
1629
- publicMessage: 'complete Claworld email verification before changing chat policy',
1630
- recoverable: true,
1631
- });
1632
- }
1633
-
1634
- const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
1635
- const result = await fetchJson(fetchImpl, `${baseUrl}/v1/profile`, {
1636
- method: 'POST',
1637
- headers: {
1638
- 'content-type': 'application/json',
1639
- ...(runtimeConfig.apiKey ? { 'x-api-key': runtimeConfig.apiKey } : {}),
1640
- ...buildRuntimeAuthHeaders(runtimeConfig),
1641
- },
1642
- body: JSON.stringify({
1643
- accountId: runtimeConfig.accountId || null,
1644
- ...(agentId ? { agentId } : {}),
1645
- action: 'update_chat_policy',
1646
- chatRequestApprovalPolicy,
1647
- }),
1648
- });
1649
- if (!result.ok) {
1650
- createRelayRouteError({
1651
- result,
1652
- runtimeConfig,
1653
- code: 'chat_request_approval_policy_update_failed',
1654
- publicMessage: 'failed to update chat policy',
1655
- context: {
1656
- accountId: runtimeConfig.accountId || null,
1657
- agentId: normalizeClaworldText(agentId, null),
1658
- },
1659
- });
1660
- }
1661
- return result.body || {};
1662
- }
1663
-
1664
1617
  async function updateGlobalProfile({
1665
1618
  runtimeConfig,
1666
1619
  agentId = null,
@@ -1763,8 +1716,8 @@ async function resolveRelayAgentSummary({
1763
1716
  agentId: normalizedAgentId,
1764
1717
  displayName: normalizeClaworldText(runtimeConfig.registration?.displayName, null),
1765
1718
  publicIdentity: null,
1766
- discoverable: null,
1767
- contactable: null,
1719
+ visibilityMode: null,
1720
+ contactPolicy: null,
1768
1721
  online: null,
1769
1722
  resolved: false,
1770
1723
  resolutionSource: 'fallback',
@@ -1812,9 +1765,8 @@ async function executeRuntimeAccountAction({
1812
1765
  profile = undefined,
1813
1766
  humanProfile = undefined,
1814
1767
  agentProfile = undefined,
1815
- discoverable = undefined,
1816
- contactable = undefined,
1817
- chatRequestApprovalPolicy = null,
1768
+ visibilityMode = undefined,
1769
+ contactPolicy = undefined,
1818
1770
  proactivitySettings = undefined,
1819
1771
  generateShareCard = false,
1820
1772
  expiresInSeconds = null,
@@ -1848,9 +1800,8 @@ async function executeRuntimeAccountAction({
1848
1800
  ...(profile !== undefined ? { profile } : {}),
1849
1801
  ...(humanProfile !== undefined ? { humanProfile } : {}),
1850
1802
  ...(agentProfile !== undefined ? { agentProfile } : {}),
1851
- ...(discoverable !== undefined ? { discoverable } : {}),
1852
- ...(contactable !== undefined ? { contactable } : {}),
1853
- ...(chatRequestApprovalPolicy ? { chatRequestApprovalPolicy } : {}),
1803
+ ...(visibilityMode !== undefined ? { visibilityMode } : {}),
1804
+ ...(contactPolicy !== undefined ? { contactPolicy } : {}),
1854
1805
  ...(proactivitySettings !== undefined ? { proactivitySettings } : {}),
1855
1806
  ...(generateShareCard === true ? { generateShareCard: true } : {}),
1856
1807
  ...(normalizeClaworldText(shareCardVariant, null) ? { shareCardVariant: normalizeClaworldText(shareCardVariant, null) } : {}),
@@ -4367,19 +4318,16 @@ async function getRuntimeIdentityStatus(context = {}) {
4367
4318
  email: normalizeClaworldText(account.email, null),
4368
4319
  verifiedAt: normalizeClaworldText(account.verifiedAt, null),
4369
4320
  reason: emailVerified ? null : 'no_email_identity',
4321
+ relay: accountView?.relay && typeof accountView.relay === 'object' && !Array.isArray(accountView.relay)
4322
+ ? accountView.relay
4323
+ : null,
4324
+ diagnostics: accountView?.diagnostics && typeof accountView.diagnostics === 'object' && !Array.isArray(accountView.diagnostics)
4325
+ ? accountView.diagnostics
4326
+ : null,
4327
+ accountView,
4370
4328
  };
4371
4329
  }
4372
4330
 
4373
- async function updateRuntimeChatRequestApprovalPolicy(context = {}) {
4374
- const resolvedContext = await resolveBoundRuntimeContext(context);
4375
- return updateChatRequestApprovalPolicy({
4376
- runtimeConfig: resolvedContext.runtimeConfig,
4377
- agentId: resolvedContext.agentId || null,
4378
- chatRequestApprovalPolicy: context.chatRequestApprovalPolicy || null,
4379
- fetchImpl,
4380
- });
4381
- }
4382
-
4383
4331
  async function updateRuntimeProfile(context = {}) {
4384
4332
  const resolvedContext = await resolveBoundRuntimeContext(context);
4385
4333
  return updateGlobalProfile({
@@ -4670,9 +4618,8 @@ async function generateRuntimeProfileCard(context = {}) {
4670
4618
  profile: Object.prototype.hasOwnProperty.call(context, 'profile') ? context.profile : undefined,
4671
4619
  humanProfile: Object.prototype.hasOwnProperty.call(context, 'humanProfile') ? context.humanProfile : undefined,
4672
4620
  agentProfile: Object.prototype.hasOwnProperty.call(context, 'agentProfile') ? context.agentProfile : undefined,
4673
- discoverable: Object.prototype.hasOwnProperty.call(context, 'discoverable') ? context.discoverable : undefined,
4674
- contactable: Object.prototype.hasOwnProperty.call(context, 'contactable') ? context.contactable : undefined,
4675
- chatRequestApprovalPolicy: context.chatRequestApprovalPolicy || null,
4621
+ visibilityMode: Object.prototype.hasOwnProperty.call(context, 'visibilityMode') ? context.visibilityMode : undefined,
4622
+ contactPolicy: Object.prototype.hasOwnProperty.call(context, 'contactPolicy') ? context.contactPolicy : undefined,
4676
4623
  proactivitySettings: Object.prototype.hasOwnProperty.call(context, 'proactivitySettings') ? context.proactivitySettings : undefined,
4677
4624
  generateShareCard: context.generateShareCard === true,
4678
4625
  expiresInSeconds: context.expiresInSeconds ?? null,
@@ -4682,7 +4629,6 @@ async function generateRuntimeProfileCard(context = {}) {
4682
4629
  },
4683
4630
  updatePublicIdentity: updateRuntimePublicIdentity,
4684
4631
  updateProfile: updateRuntimeProfile,
4685
- updateChatRequestApprovalPolicy: updateRuntimeChatRequestApprovalPolicy,
4686
4632
  generateShareCard: generateRuntimeProfileCard,
4687
4633
  },
4688
4634
  identity: {
@@ -5059,9 +5005,8 @@ async function generateRuntimeProfileCard(context = {}) {
5059
5005
  profile: Object.prototype.hasOwnProperty.call(context, 'profile') ? context.profile : undefined,
5060
5006
  humanProfile: Object.prototype.hasOwnProperty.call(context, 'humanProfile') ? context.humanProfile : undefined,
5061
5007
  agentProfile: Object.prototype.hasOwnProperty.call(context, 'agentProfile') ? context.agentProfile : undefined,
5062
- discoverable: Object.prototype.hasOwnProperty.call(context, 'discoverable') ? context.discoverable : undefined,
5063
- contactable: Object.prototype.hasOwnProperty.call(context, 'contactable') ? context.contactable : undefined,
5064
- chatRequestApprovalPolicy: context.chatRequestApprovalPolicy || null,
5008
+ visibilityMode: Object.prototype.hasOwnProperty.call(context, 'visibilityMode') ? context.visibilityMode : undefined,
5009
+ contactPolicy: Object.prototype.hasOwnProperty.call(context, 'contactPolicy') ? context.contactPolicy : undefined,
5065
5010
  proactivitySettings: Object.prototype.hasOwnProperty.call(context, 'proactivitySettings') ? context.proactivitySettings : undefined,
5066
5011
  generateShareCard: context.generateShareCard === true,
5067
5012
  expiresInSeconds: context.expiresInSeconds ?? null,
@@ -5071,7 +5016,6 @@ async function generateRuntimeProfileCard(context = {}) {
5071
5016
  },
5072
5017
  updatePublicIdentity: updateRuntimePublicIdentity,
5073
5018
  updateProfile: updateRuntimeProfile,
5074
- updateChatRequestApprovalPolicy: updateRuntimeChatRequestApprovalPolicy,
5075
5019
  generateShareCard: generateRuntimeProfileCard,
5076
5020
  },
5077
5021
  identity: {
@@ -496,7 +496,6 @@ export const ACCOUNT_ACTIONS = Object.freeze([
496
496
  'view',
497
497
  'update_identity',
498
498
  'update_profile',
499
- 'update_chat_policy',
500
499
  ]);
501
500
 
502
501
  function normalizeAccountAction(value, fallback = null) {
@@ -509,7 +508,6 @@ export function inferAccountAction(params = {}) {
509
508
  if (explicitAction) return explicitAction;
510
509
  if (normalizeText(params.displayName, null)) return 'update_identity';
511
510
  if (Object.prototype.hasOwnProperty.call(params, 'profile')) return 'update_profile';
512
- if (normalizeObject(params.chatRequestApprovalPolicy, null)) return 'update_chat_policy';
513
511
  return 'view';
514
512
  }
515
513
 
@@ -629,31 +627,12 @@ function projectToolAccountProfileState(identityPayload = null) {
629
627
  };
630
628
  }
631
629
 
632
- function projectToolChatRequestApprovalPolicy(payload = null) {
633
- const policy = normalizeObject(payload, null);
634
- if (!policy) return null;
635
- return {
636
- agentId: normalizeText(policy.agentId, null),
637
- schemaVersion: Number.isInteger(policy.schemaVersion) ? policy.schemaVersion : null,
638
- syncedAt: normalizeText(policy.syncedAt, null),
639
- credentialId: normalizeText(policy.credentialId, null),
640
- source: normalizeObject(policy.source, {})
641
- ? {
642
- channel: normalizeText(policy.source?.channel, null),
643
- integration: normalizeText(policy.source?.integration, null),
644
- accountId: normalizeText(policy.source?.accountId, null),
645
- }
646
- : {},
647
- policy: normalizeObject(policy.policy, {})
648
- ? {
649
- mode: normalizeText(policy.policy?.mode, null),
650
- blocks: {
651
- originTypes: Array.isArray(policy.policy?.blocks?.originTypes) ? policy.policy.blocks.originTypes : [],
652
- worldIds: Array.isArray(policy.policy?.blocks?.worldIds) ? policy.policy.blocks.worldIds : [],
653
- },
654
- }
655
- : null,
656
- };
630
+ function resolveToolPublicIdentityReady(identityPayload = null, publicIdentityState = {}) {
631
+ const diagnostics = normalizeObject(identityPayload?.diagnostics, null);
632
+ if (typeof diagnostics?.publicIdentityReady === 'boolean') return diagnostics.publicIdentityReady;
633
+ const identityStatus = normalizeText(publicIdentityState?.publicIdentity?.status, null);
634
+ if (identityStatus) return identityStatus === 'ready';
635
+ return identityPayload?.ready === true;
657
636
  }
658
637
 
659
638
  function projectToolPluginVersionStatus(payload = null) {
@@ -685,11 +664,34 @@ export function projectToolAccountViewResponse({
685
664
  accountId = null,
686
665
  pairingPayload = null,
687
666
  identityPayload = null,
667
+ accountPayload = null,
688
668
  } = {}) {
689
669
  const publicIdentityState = projectToolAccountIdentityFields(identityPayload);
690
670
  const accountProfile = projectToolAccountProfileState(identityPayload);
691
- const publicIdentityReady = identityPayload?.ready === true;
692
- const accountProfileReady = accountProfile.ready === true;
671
+ const accountView = normalizeObject(accountPayload, null);
672
+ const accountRecord = normalizeObject(accountView?.account, null);
673
+ const accountRelay = normalizeObject(accountView?.relay, null);
674
+ const accountDiagnostics = normalizeObject(accountView?.diagnostics, null);
675
+ const publicIdentityReady = resolveToolPublicIdentityReady(identityPayload, publicIdentityState);
676
+ const accountProfileReady = accountProfile.ready === true
677
+ || accountRecord?.profileReady === true
678
+ || accountDiagnostics?.accountProfileReady === true;
679
+ const resolvedAccountProfile = accountProfile.ready === true
680
+ ? accountProfile
681
+ : {
682
+ ...accountProfile,
683
+ status: accountProfileReady ? 'ready' : accountProfile.status,
684
+ ready: accountProfileReady,
685
+ profile: normalizeText(
686
+ accountRecord?.agentProfile,
687
+ normalizeText(accountRecord?.profile, accountProfile.profile),
688
+ ),
689
+ reason: accountProfileReady ? null : accountProfile.reason,
690
+ requiredAction: accountProfileReady ? null : accountProfile.requiredAction,
691
+ nextAction: accountProfileReady ? null : accountProfile.nextAction,
692
+ nextTool: accountProfileReady ? null : accountProfile.nextTool,
693
+ missingFields: accountProfileReady ? [] : accountProfile.missingFields,
694
+ };
693
695
  const emailVerified = pairingPayload?.emailVerified === true;
694
696
  const runtimePaired = pairingPayload?.status === 'paired';
695
697
  const bindingReady = typeof pairingPayload?.bindingReady === 'boolean'
@@ -720,11 +722,11 @@ export function projectToolAccountViewResponse({
720
722
  }
721
723
  : !accountProfileReady
722
724
  ? {
723
- requiredAction: accountProfile.requiredAction,
724
- nextAction: accountProfile.nextAction,
725
- nextTool: accountProfile.nextTool,
726
- missingFields: accountProfile.missingFields,
727
- reason: accountProfile.reason,
725
+ requiredAction: resolvedAccountProfile.requiredAction,
726
+ nextAction: resolvedAccountProfile.nextAction,
727
+ nextTool: resolvedAccountProfile.nextTool,
728
+ missingFields: resolvedAccountProfile.missingFields,
729
+ reason: resolvedAccountProfile.reason,
728
730
  }
729
731
  : {
730
732
  requiredAction: null,
@@ -733,8 +735,17 @@ export function projectToolAccountViewResponse({
733
735
  missingFields: [],
734
736
  reason: null,
735
737
  };
736
- const relayResolved = pairingPayload?.relayAgent?.resolved ?? null;
737
- const relayOnline = pairingPayload?.relayAgent?.online ?? null;
738
+ const relayIdentityResolved = typeof pairingPayload?.relayAgent?.resolved === 'boolean'
739
+ ? pairingPayload.relayAgent.resolved
740
+ : null;
741
+ const relayOnline = typeof accountRelay?.online === 'boolean'
742
+ ? accountRelay.online
743
+ : (typeof accountDiagnostics?.relayOnline === 'boolean'
744
+ ? accountDiagnostics.relayOnline
745
+ : (typeof pairingPayload?.relayAgent?.online === 'boolean' ? pairingPayload.relayAgent.online : null));
746
+ const relayResolved = typeof accountRelay?.resolved === 'boolean'
747
+ ? accountRelay.resolved
748
+ : (typeof relayOnline === 'boolean');
738
749
  const resolvedShareCard = identityPayload && Object.prototype.hasOwnProperty.call(identityPayload, 'shareCard')
739
750
  ? projectToolShareCard(identityPayload.shareCard)
740
751
  : undefined;
@@ -765,6 +776,7 @@ export function projectToolAccountViewResponse({
765
776
  bindingStatus,
766
777
  publicIdentityReady,
767
778
  accountProfileReady,
779
+ relayIdentityResolved,
768
780
  relayPresenceResolved: relayResolved,
769
781
  relayOnline,
770
782
  },
@@ -774,21 +786,20 @@ export function projectToolAccountViewResponse({
774
786
  normalizeText(pairingPayload?.relayAgent?.agentId, null),
775
787
  ),
776
788
  displayName: normalizeText(pairingPayload?.relayAgent?.displayName, null),
777
- discoverable: pairingPayload?.relayAgent?.discoverable ?? null,
778
- contactable: pairingPayload?.relayAgent?.contactable ?? null,
789
+ visibilityMode: normalizeText(pairingPayload?.relayAgent?.visibilityMode, null),
790
+ contactPolicy: normalizeText(pairingPayload?.relayAgent?.contactPolicy, null),
779
791
  online: relayOnline,
780
792
  resolved: relayResolved,
781
793
  bindingStatus,
782
794
  },
783
- profile: accountProfile.profile,
795
+ profile: resolvedAccountProfile.profile,
784
796
  ...publicIdentityState,
785
- accountProfile,
797
+ accountProfile: resolvedAccountProfile,
786
798
  requiredAction: blockedAction.requiredAction,
787
799
  nextAction: blockedAction.nextAction,
788
800
  nextTool: blockedAction.nextTool,
789
801
  missingFields: blockedAction.missingFields,
790
802
  pluginVersionStatus: projectToolPluginVersionStatus(identityPayload?.pluginVersionStatus),
791
- chatRequestApprovalPolicy: projectToolChatRequestApprovalPolicy(identityPayload?.chatRequestApprovalPolicy),
792
803
  ...(resolvedShareCard !== undefined ? { shareCard: resolvedShareCard } : {}),
793
804
  };
794
805
  }
@@ -807,7 +818,7 @@ export function projectToolAccountMutationResponse({
807
818
  : (identityPayload && Object.prototype.hasOwnProperty.call(identityPayload, 'shareCard')
808
819
  ? projectToolShareCard(identityPayload.shareCard)
809
820
  : undefined);
810
- const publicIdentityReady = identityPayload?.ready === true;
821
+ const publicIdentityReady = resolveToolPublicIdentityReady(identityPayload, publicIdentityState);
811
822
  const accountProfileReady = accountProfile.ready === true;
812
823
  const emailVerificationPayload = normalizeObject(identityPayload?.emailVerification, null);
813
824
  const emailVerified = identityPayload?.emailVerified === true
@@ -875,7 +886,6 @@ export function projectToolAccountMutationResponse({
875
886
  missingFields: blockedAction.missingFields,
876
887
  reason: blockedAction.reason,
877
888
  pluginVersionStatus: projectToolPluginVersionStatus(identityPayload?.pluginVersionStatus),
878
- chatRequestApprovalPolicy: projectToolChatRequestApprovalPolicy(identityPayload?.chatRequestApprovalPolicy),
879
889
  ...(resolvedShareCard !== undefined ? { shareCard: resolvedShareCard } : {}),
880
890
  ...(runtimeIdentity ? { runtimeIdentity } : {}),
881
891
  ...(action === 'update_identity'
@@ -888,10 +898,6 @@ export function projectToolAccountMutationResponse({
888
898
  ? {
889
899
  updated: ['profile'],
890
900
  }
891
- : action === 'update_chat_policy'
892
- ? {
893
- updated: ['chatRequestApprovalPolicy'],
894
- }
895
901
  : {}),
896
902
  };
897
903
  }
@@ -25,10 +25,6 @@ import {
25
25
  } from '../runtime/working-memory.js';
26
26
  import { resolveOpenClawWorkspaceRoot } from '../runtime/workspace-resolver.js';
27
27
  import { setClaworldRuntime } from './runtime.js';
28
- import {
29
- CHAT_REQUEST_APPROVAL_POLICY_MODES,
30
- CHAT_REQUEST_APPROVAL_POLICY_ORIGIN_TYPES,
31
- } from '../../product-shell/contracts/chat-request-approval-policy.js';
32
28
  import { PUBLIC_TOOL_ACTION_CATALOG } from '../../product-shell/contracts/search-item.js';
33
29
  import {
34
30
  ACCOUNT_ACTIONS,
@@ -236,13 +232,50 @@ function normalizeTerminalAccountAction(params = {}) {
236
232
  Object.prototype.hasOwnProperty.call(params, 'agentProfile')
237
233
  || Object.prototype.hasOwnProperty.call(params, 'profile')
238
234
  ) return 'update_agent_profile';
239
- if (Object.prototype.hasOwnProperty.call(params, 'discoverable')) return 'set_discoverability';
240
- if (Object.prototype.hasOwnProperty.call(params, 'contactable')) return 'set_contactability';
241
- if (normalizeObject(params.chatRequestApprovalPolicy, null)) return 'set_chat_policy';
235
+ if (Object.prototype.hasOwnProperty.call(params, 'visibilityMode')) return 'set_visibility_mode';
236
+ if (Object.prototype.hasOwnProperty.call(params, 'contactPolicy')) return 'set_contact_policy';
237
+ if (Object.prototype.hasOwnProperty.call(params, 'chatRequestPolicy')) {
238
+ requireManageWorldField('chatRequestPolicy', 'chatRequestPolicy is not supported by claworld_manage_account; use contactPolicy');
239
+ }
242
240
  if (Object.prototype.hasOwnProperty.call(params, 'proactivitySettings')) return 'set_proactivity';
243
241
  return 'view_account';
244
242
  }
245
243
 
244
+ function hasProvidedTerminalAccountPolicyField(params = {}, fieldId) {
245
+ if (!Object.prototype.hasOwnProperty.call(params, fieldId)) return false;
246
+ const value = params[fieldId];
247
+ if (value == null) return false;
248
+ if (typeof value === 'string') return normalizeText(value, null) != null;
249
+ return true;
250
+ }
251
+
252
+ function validateTerminalAccountPolicyPayload(action, params = {}) {
253
+ if (action === 'set_visibility_mode') {
254
+ if (!normalizeText(params.visibilityMode, null)) {
255
+ requireManageWorldField('visibilityMode', 'visibilityMode is required for action=set_visibility_mode');
256
+ }
257
+ if (hasProvidedTerminalAccountPolicyField(params, 'contactPolicy')) {
258
+ requireManageWorldField('contactPolicy', 'contactPolicy is not supported for action=set_visibility_mode');
259
+ }
260
+ if (hasProvidedTerminalAccountPolicyField(params, 'chatRequestPolicy')) {
261
+ requireManageWorldField('chatRequestPolicy', 'chatRequestPolicy is not supported by claworld_manage_account; use contactPolicy');
262
+ }
263
+ return;
264
+ }
265
+ if (action === 'set_contact_policy') {
266
+ if (!normalizeText(params.contactPolicy, null)) {
267
+ requireManageWorldField('contactPolicy', 'contactPolicy is required for action=set_contact_policy');
268
+ }
269
+ if (hasProvidedTerminalAccountPolicyField(params, 'visibilityMode')) {
270
+ requireManageWorldField('visibilityMode', 'visibilityMode is not supported for action=set_contact_policy');
271
+ }
272
+ if (hasProvidedTerminalAccountPolicyField(params, 'chatRequestPolicy')) {
273
+ requireManageWorldField('chatRequestPolicy', 'chatRequestPolicy is not supported by claworld_manage_account; use contactPolicy');
274
+ }
275
+ return;
276
+ }
277
+ }
278
+
246
279
  function normalizeTerminalWorldAction(params = {}) {
247
280
  if (hasExplicitAction(params)) {
248
281
  const explicitAction = normalizeText(params.action, null);
@@ -470,12 +503,12 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
470
503
  {
471
504
  name: accountTool,
472
505
  label: 'Claworld Manage Account',
473
- description: 'Terminal account surface for readiness, public identity, global profile, share-card generation, account-level chat policy, and email-based identity verification.',
506
+ description: 'Terminal account surface for readiness, public identity, global profile, share-card generation, visibility, inbound contact policy, and email-based identity verification.',
474
507
  metadata: buildToolMetadata({
475
508
  category: 'account',
476
509
  usageNotes: [
477
510
  'Use this human-facing account surface for identity verification, profile, policy, and subscription decisions.',
478
- 'Use action=view_account for readiness; update_display_name, update_agent_profile, or set_chat_policy for common account mutations.',
511
+ 'Use action=view_account for readiness; update_display_name, update_agent_profile, or set_contact_policy for common account mutations.',
479
512
  'Use start_email_verification with email + optional displayName to start email-based identity verification, then complete_email_verification with email + code to finish.',
480
513
  'Use subscribe_person or unsubscribe_person when a search/profile result exposes a person subscription target.',
481
514
  ],
@@ -488,7 +521,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
488
521
  action: stringParam({
489
522
  description: 'Account action.',
490
523
  enumValues: TERMINAL_ACCOUNT_ACTIONS,
491
- examples: ['view_account', 'start_email_verification', 'update_display_name', 'set_chat_policy'],
524
+ examples: ['view_account', 'start_email_verification', 'update_display_name', 'set_contact_policy'],
492
525
  }),
493
526
  displayName: stringParam({
494
527
  description: 'Public-facing display name for update_display_name or start_email_verification.',
@@ -507,11 +540,15 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
507
540
  description: 'Agent-facing profile/personality text for update_agent_profile.',
508
541
  examples: ['偏主动但会先确认边界,擅长总结和约局。'],
509
542
  }),
510
- discoverable: booleanParam({ description: 'Whether this account can appear in global people search.' }),
511
- contactable: booleanParam({ description: 'Whether this account can receive direct public-profile chat requests.' }),
512
- chatRequestApprovalPolicy: objectParam({
513
- description: 'Backend-managed inbound chat-request policy for this account.',
514
- additionalProperties: true,
543
+ visibilityMode: stringParam({
544
+ description: 'Account visibility mode: public is searchable, unlisted is explicit-identity reachable, private is not publicly reachable.',
545
+ enumValues: ['public', 'unlisted', 'private'],
546
+ examples: ['public', 'unlisted', 'private'],
547
+ }),
548
+ contactPolicy: stringParam({
549
+ description: 'Inbound contact policy: open accepts eligible requests, approval_required keeps the request path open but requires review, closed blocks new inbound contact.',
550
+ enumValues: ['open', 'approval_required', 'closed'],
551
+ examples: ['open', 'approval_required', 'closed'],
515
552
  }),
516
553
  proactivitySettings: objectParam({
517
554
  description: 'Account-level proactive-management settings.',
@@ -554,6 +591,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
554
591
  }),
555
592
  async execute(toolCallId, params = {}) {
556
593
  const action = normalizeTerminalAccountAction(params);
594
+ validateTerminalAccountPolicyPayload(action, params);
557
595
  const subscriptionTargetId = normalizeText(params.targetAgentId, normalizeText(params.targetId, null));
558
596
  if (action === 'subscribe_person') {
559
597
  if (!subscriptionTargetId) requireManageWorldField('targetAgentId', 'targetAgentId is required for action=subscribe_person');
@@ -641,9 +679,8 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
641
679
  profile: params.profile,
642
680
  humanProfile: params.humanProfile,
643
681
  agentProfile: params.agentProfile,
644
- discoverable: params.discoverable,
645
- contactable: params.contactable,
646
- chatRequestApprovalPolicy: params.chatRequestApprovalPolicy || null,
682
+ visibilityMode: params.visibilityMode,
683
+ contactPolicy: params.contactPolicy,
647
684
  proactivitySettings: params.proactivitySettings,
648
685
  generateShareCard,
649
686
  shareCardVariant: params.shareCardVariant ?? null,
@@ -661,7 +698,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
661
698
  usageNotes: [
662
699
  'scope=worlds searches or browses visible worlds.',
663
700
  'scope=world_members searches members in an authorized world.',
664
- 'scope=people searches globally discoverable/contactable public identities.',
701
+ 'scope=people searches globally public identities; unlisted people require explicit identity/profile lookup.',
665
702
  'scope=mixed combines world, optional world-member, and global people search results in one SearchItemEnvelope list.',
666
703
  ],
667
704
  }),
@@ -1200,43 +1237,6 @@ function buildRegisteredTools(api, plugin) {
1200
1237
  enumValues: ['en', 'zh'],
1201
1238
  examples: ['en', 'zh'],
1202
1239
  });
1203
- const chatRequestApprovalPolicyProperty = objectParam({
1204
- description: 'Backend-managed inbound chat-request policy for this account.',
1205
- required: ['mode'],
1206
- properties: {
1207
- mode: stringParam({
1208
- description: 'Policy mode controlling which new inbound chat requests auto-accept.',
1209
- enumValues: CHAT_REQUEST_APPROVAL_POLICY_MODES,
1210
- examples: ['open', 'manual_review'],
1211
- }),
1212
- blocks: objectParam({
1213
- description: 'Optional deny rules applied before the allow mode is evaluated.',
1214
- properties: {
1215
- originTypes: arrayParam({
1216
- description: 'Canonical request origin types that should always be rejected.',
1217
- items: stringParam({
1218
- enumValues: CHAT_REQUEST_APPROVAL_POLICY_ORIGIN_TYPES,
1219
- }),
1220
- examples: [['world_broadcast']],
1221
- }),
1222
- worldIds: arrayParam({
1223
- description: 'World ids that should always be rejected.',
1224
- items: stringParam({}),
1225
- examples: [['dating-demo-world']],
1226
- }),
1227
- },
1228
- }),
1229
- },
1230
- examples: [
1231
- {
1232
- mode: 'trusted_or_world',
1233
- blocks: {
1234
- originTypes: ['world_broadcast'],
1235
- worldIds: [],
1236
- },
1237
- },
1238
- ],
1239
- });
1240
1240
  const worldIdProperty = stringParam({
1241
1241
  description: 'Canonical world id returned by claworld_search(scope=worlds) or claworld_manage_worlds(action=get_world).',
1242
1242
  minLength: 1,
@@ -1925,20 +1925,6 @@ function buildRegisteredTools(api, plugin) {
1925
1925
  },
1926
1926
  outcome: 'Stores the current account profile text. Pass an empty string to clear it.',
1927
1927
  },
1928
- {
1929
- title: 'Update the inbound chat policy',
1930
- input: {
1931
- accountId: 'claworld',
1932
- action: 'update_chat_policy',
1933
- chatRequestApprovalPolicy: {
1934
- mode: 'trusted_or_world',
1935
- blocks: {
1936
- originTypes: ['world_broadcast'],
1937
- },
1938
- },
1939
- },
1940
- outcome: 'Stores the account-level chat-request policy in the backend and returns the updated policy snapshot.',
1941
- },
1942
1928
  ],
1943
1929
  }),
1944
1930
  parameters: objectParam({
@@ -1947,9 +1933,9 @@ function buildRegisteredTools(api, plugin) {
1947
1933
  properties: {
1948
1934
  accountId: accountIdProperty,
1949
1935
  action: stringParam({
1950
- description: 'Account action. Defaults to view; inferred from displayName, profile, or chatRequestApprovalPolicy when omitted.',
1936
+ description: 'Account action. Defaults to view; inferred from displayName or profile when omitted.',
1951
1937
  enumValues: ACCOUNT_ACTIONS,
1952
- examples: ['view', 'update_identity', 'update_profile', 'update_chat_policy'],
1938
+ examples: ['view', 'update_identity', 'update_profile'],
1953
1939
  }),
1954
1940
  displayName: stringParam({
1955
1941
  description: 'Public-facing display name. Required for action=update_identity. # is reserved and must not appear here.',
@@ -1960,7 +1946,6 @@ function buildRegisteredTools(api, plugin) {
1960
1946
  description: 'Global plain-text profile for this account. Maximum 500 characters. Use an empty string to clear it. HTML is not supported.',
1961
1947
  examples: ['喜欢慢节奏介绍和小范围世界,也愿意先让 agent 帮我做初步认识。🙂'],
1962
1948
  }),
1963
- chatRequestApprovalPolicy: chatRequestApprovalPolicyProperty,
1964
1949
  generateShareCard: booleanParam({
1965
1950
  description: 'When true, return a temporary public identity card URL. Defaults to false for view and true for update_identity.',
1966
1951
  }),
@@ -1988,13 +1973,6 @@ function buildRegisteredTools(api, plugin) {
1988
1973
  action: 'update_profile',
1989
1974
  profile: '喜欢慢节奏介绍和小范围世界,也愿意先让 agent 帮我做初步认识。🙂',
1990
1975
  },
1991
- {
1992
- accountId: 'claworld',
1993
- action: 'update_chat_policy',
1994
- chatRequestApprovalPolicy: {
1995
- mode: 'manual_review',
1996
- },
1997
- },
1998
1976
  ],
1999
1977
  }),
2000
1978
  async execute(_toolCallId, params = {}) {
@@ -2040,26 +2018,6 @@ function buildRegisteredTools(api, plugin) {
2040
2018
  }));
2041
2019
  }
2042
2020
 
2043
- if (action === 'update_chat_policy') {
2044
- const context = await resolveToolContext(api, plugin, params);
2045
- const chatRequestApprovalPolicy = normalizeObject(params.chatRequestApprovalPolicy, null);
2046
- if (!chatRequestApprovalPolicy) {
2047
- requireManageWorldField(
2048
- 'chatRequestApprovalPolicy',
2049
- 'chatRequestApprovalPolicy is required for action=update_chat_policy',
2050
- );
2051
- }
2052
- const payload = await plugin.runtime.productShell.profile.updateChatRequestApprovalPolicy({
2053
- ...context,
2054
- chatRequestApprovalPolicy,
2055
- });
2056
- return buildToolResult(projectToolAccountMutationResponse({
2057
- action,
2058
- accountId: context.accountId,
2059
- identityPayload: payload,
2060
- }));
2061
- }
2062
-
2063
2021
  const context = await resolveToolContext(api, plugin, params);
2064
2022
  const cfg = context.cfg || await loadCurrentConfig(api);
2065
2023
  const accountId = context.accountId;
@@ -2094,8 +2052,8 @@ function buildRegisteredTools(api, plugin) {
2094
2052
  normalizeText(runtimeConfig?.name, normalizeText(runtimeConfig?.registration?.displayName, null)),
2095
2053
  ),
2096
2054
  ),
2097
- discoverable: null,
2098
- contactable: null,
2055
+ visibilityMode: null,
2056
+ contactPolicy: null,
2099
2057
  online: null,
2100
2058
  resolved: null,
2101
2059
  }
@@ -2113,7 +2071,13 @@ function buildRegisteredTools(api, plugin) {
2113
2071
  agentId: pairedAgentId,
2114
2072
  })
2115
2073
  : null;
2116
- const emailVerified = identityStatus?.emailVerified === true;
2074
+ const accountPayload = normalizeObject(identityStatus?.accountView, null)
2075
+ || (normalizeObject(identityStatus?.relay, null) ? identityStatus : null);
2076
+ const accountViewAccount = normalizeObject(accountPayload?.account, null);
2077
+ const accountViewDiagnostics = normalizeObject(accountPayload?.diagnostics, null);
2078
+ const emailVerified = identityStatus?.emailVerified === true
2079
+ || accountViewAccount?.emailVerified === true
2080
+ || accountViewDiagnostics?.emailVerified === true;
2117
2081
  const bindingReady = hasConfiguredAppToken && Boolean(pairedAgentId);
2118
2082
  const bindingStatus = hasConfiguredAppToken
2119
2083
  ? (bindingReady ? 'bound' : 'identity_unresolved')
@@ -2140,8 +2104,8 @@ function buildRegisteredTools(api, plugin) {
2140
2104
  bindingReady,
2141
2105
  bindingStatus,
2142
2106
  emailVerified,
2143
- email: identityStatus?.email || null,
2144
- verifiedAt: identityStatus?.verifiedAt || null,
2107
+ email: identityStatus?.email || accountViewAccount?.email || null,
2108
+ verifiedAt: identityStatus?.verifiedAt || accountViewAccount?.verifiedAt || null,
2145
2109
  reason: hasConfiguredAppToken
2146
2110
  ? (pairedAgentId ? null : 'missing_agent_id')
2147
2111
  : 'missing_app_token',
@@ -2155,6 +2119,7 @@ function buildRegisteredTools(api, plugin) {
2155
2119
  accountId,
2156
2120
  pairingPayload,
2157
2121
  identityPayload,
2122
+ accountPayload,
2158
2123
  }));
2159
2124
  },
2160
2125
  },
@@ -2,7 +2,10 @@ import { EventEmitter } from 'events';
2
2
  import WebSocket from 'ws';
3
3
  import { resolveClaworldRuntimeConfig } from './config-schema.js';
4
4
  import { buildRuntimeAuthHeaders } from './account-identity.js';
5
- import { buildClaworldRelayClientVersion } from '../plugin-version.js';
5
+ import {
6
+ CLAWORLD_OPENCLAW_PLUGIN_CLIENT,
7
+ CLAWORLD_PLUGIN_CURRENT_VERSION,
8
+ } from '../plugin-version.js';
6
9
  import { createRelayEventProtocol } from '../protocol/relay-event-protocol.js';
7
10
  import { createInboundSessionRouter } from '../runtime/inbound-session-router.js';
8
11
  import { createOutboundSessionBridge } from '../runtime/outbound-session-bridge.js';
@@ -372,6 +375,7 @@ export class ClaworldRelayClient extends EventEmitter {
372
375
  type: 'auth',
373
376
  agentId,
374
377
  credential,
378
+ client: CLAWORLD_OPENCLAW_PLUGIN_CLIENT,
375
379
  clientVersion,
376
380
  bridgeProtocol: this.protocol.version,
377
381
  });
@@ -552,7 +556,7 @@ export class ClaworldRelayClient extends EventEmitter {
552
556
  config,
553
557
  agentId,
554
558
  credential = null,
555
- clientVersion = buildClaworldRelayClientVersion(),
559
+ clientVersion = CLAWORLD_PLUGIN_CURRENT_VERSION,
556
560
  sessionTarget,
557
561
  fallbackTarget,
558
562
  } = {}) {
@@ -1,7 +1,10 @@
1
1
  import repoPackageJson from '../../package.json' with { type: 'json' };
2
2
 
3
3
  export const CLAWORLD_PLUGIN_PACKAGE_NAME = '@xfxstudio/claworld';
4
- export const CLAWORLD_PLUGIN_VERSION_HEADER = 'x-claworld-plugin-version';
4
+ export const CLAWORLD_CLIENT_HEADER = 'x-claworld-client';
5
+ export const CLAWORLD_CLIENT_VERSION_HEADER = 'x-claworld-client-version';
6
+ export const CLAWORLD_CLIENT_CHANNEL_HEADER = 'x-claworld-client-channel';
7
+ export const CLAWORLD_OPENCLAW_PLUGIN_CLIENT = 'openclaw-plugin';
5
8
 
6
9
  function normalizeText(value, fallback = null) {
7
10
  if (value == null) return fallback;
@@ -9,15 +12,6 @@ function normalizeText(value, fallback = null) {
9
12
  return normalized || fallback;
10
13
  }
11
14
 
12
- function normalizeHeaderValue(value) {
13
- if (Array.isArray(value)) {
14
- return normalizeHeaderValue(value[0]);
15
- }
16
- const normalized = normalizeText(value, null);
17
- if (!normalized) return null;
18
- return normalized.split(',')[0]?.trim() || null;
19
- }
20
-
21
15
  export function normalizeClaworldPluginVersion(value, fallback = null) {
22
16
  const normalized = normalizeText(value, null);
23
17
  if (!normalized) return fallback;
@@ -39,16 +33,9 @@ function resolveCurrentPluginVersion() {
39
33
 
40
34
  export const CLAWORLD_PLUGIN_CURRENT_VERSION = resolveCurrentPluginVersion();
41
35
 
42
- export function readClaworldPluginVersionFromHeaders(headers = {}) {
43
- const rawVersion = normalizeHeaderValue(headers?.[CLAWORLD_PLUGIN_VERSION_HEADER]);
44
- return {
45
- rawVersion,
46
- reportedVersion: normalizeClaworldPluginVersion(rawVersion, rawVersion),
47
- normalizedVersion: normalizeClaworldPluginVersion(rawVersion, null),
48
- source: rawVersion ? CLAWORLD_PLUGIN_VERSION_HEADER : null,
49
- };
50
- }
51
-
52
- export function buildClaworldRelayClientVersion(version = CLAWORLD_PLUGIN_CURRENT_VERSION) {
53
- return `claworld-plugin/${normalizeClaworldPluginVersion(version, CLAWORLD_PLUGIN_CURRENT_VERSION)}`;
36
+ export function inferClaworldClientChannel(version = CLAWORLD_PLUGIN_CURRENT_VERSION, fallback = null) {
37
+ const normalized = normalizeClaworldPluginVersion(version, null);
38
+ if (!normalized) return fallback;
39
+ if (/-testing(?:\.|$)/.test(normalized)) return 'testing';
40
+ return 'stable';
54
41
  }
@@ -583,8 +583,8 @@ function projectToolAgentSummary(agent = {}) {
583
583
  displayName: normalizeText(agent.displayName, null),
584
584
  identity: normalizeText(agent.publicIdentity?.displayIdentity, null),
585
585
  online: agent.online === true,
586
- discoverable: typeof agent.discoverable === 'boolean' ? agent.discoverable : null,
587
- contactable: typeof agent.contactable === 'boolean' ? agent.contactable : null,
586
+ visibilityMode: normalizeText(agent.visibilityMode, null),
587
+ contactPolicy: normalizeText(agent.contactPolicy, null),
588
588
  };
589
589
  }
590
590
 
@@ -5,6 +5,7 @@ const SUPPORTED_MODES = Object.freeze([
5
5
  'trusted_only',
6
6
  'trusted_or_world',
7
7
  'open',
8
+ 'reject_all',
8
9
  ]);
9
10
  const SUPPORTED_ORIGIN_TYPES = Object.freeze([
10
11
  'chat_request',
@@ -59,6 +60,11 @@ export function normalizeChatRequestApprovalMode(value, fallback = DEFAULT_MODE)
59
60
  case 'auto_accept':
60
61
  case 'all':
61
62
  return 'open';
63
+ case 'reject':
64
+ case 'reject_all':
65
+ case 'closed':
66
+ case 'do_not_disturb':
67
+ return 'reject_all';
62
68
  default:
63
69
  return SUPPORTED_MODES.includes(fallback) ? fallback : DEFAULT_MODE;
64
70
  }
@@ -37,9 +37,8 @@ export const PUBLIC_TOOL_ACTION_CATALOG = Object.freeze({
37
37
  'update_display_name',
38
38
  'update_human_profile',
39
39
  'update_agent_profile',
40
- 'set_discoverability',
41
- 'set_contactability',
42
- 'set_chat_policy',
40
+ 'set_visibility_mode',
41
+ 'set_contact_policy',
43
42
  'set_proactivity',
44
43
  'subscribe_person',
45
44
  'unsubscribe_person',