@xfxstudio/claworld 0.2.14 → 0.2.15

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.
@@ -8,7 +8,7 @@
8
8
  ],
9
9
  "name": "Claworld Persona Relay",
10
10
  "description": "Claworld relay world channel plugin for OpenClaw.",
11
- "version": "0.2.14",
11
+ "version": "0.2.15",
12
12
  "configSchema": {
13
13
  "type": "object",
14
14
  "additionalProperties": false,
@@ -89,54 +89,6 @@
89
89
  }
90
90
  }
91
91
  },
92
- "approval": {
93
- "type": "object",
94
- "additionalProperties": false,
95
- "properties": {
96
- "mode": {
97
- "type": "string",
98
- "enum": [
99
- "manual_review",
100
- "world_only",
101
- "trusted_only",
102
- "trusted_or_world",
103
- "open"
104
- ],
105
- "description": "Declarative inbound chat-request approval policy for this account.",
106
- "default": "open"
107
- },
108
- "blocks": {
109
- "type": "object",
110
- "additionalProperties": false,
111
- "properties": {
112
- "originTypes": {
113
- "type": "array",
114
- "items": {
115
- "type": "string",
116
- "enum": [
117
- "chat_request",
118
- "world_broadcast"
119
- ]
120
- },
121
- "description": "Optional canonical chat-request origin types that should be rejected by backend policy."
122
- },
123
- "worldIds": {
124
- "type": "array",
125
- "items": {
126
- "type": "string",
127
- "minLength": 1
128
- },
129
- "description": "Optional world ids that should be rejected by backend policy."
130
- }
131
- }
132
- },
133
- "autoAccept": {
134
- "type": "boolean",
135
- "description": "Legacy alias. `true` maps to `approval.mode = \"open\"` and `false` maps to `approval.mode = \"manual_review\"`.",
136
- "default": false
137
- }
138
- }
139
- },
140
92
  "testing": {
141
93
  "type": "object",
142
94
  "additionalProperties": false,
@@ -284,54 +236,6 @@
284
236
  }
285
237
  }
286
238
  },
287
- "approval": {
288
- "type": "object",
289
- "additionalProperties": false,
290
- "properties": {
291
- "mode": {
292
- "type": "string",
293
- "enum": [
294
- "manual_review",
295
- "world_only",
296
- "trusted_only",
297
- "trusted_or_world",
298
- "open"
299
- ],
300
- "description": "Declarative inbound chat-request approval policy for this account.",
301
- "default": "open"
302
- },
303
- "blocks": {
304
- "type": "object",
305
- "additionalProperties": false,
306
- "properties": {
307
- "originTypes": {
308
- "type": "array",
309
- "items": {
310
- "type": "string",
311
- "enum": [
312
- "chat_request",
313
- "world_broadcast"
314
- ]
315
- },
316
- "description": "Optional canonical chat-request origin types that should be rejected by backend policy."
317
- },
318
- "worldIds": {
319
- "type": "array",
320
- "items": {
321
- "type": "string",
322
- "minLength": 1
323
- },
324
- "description": "Optional world ids that should be rejected by backend policy."
325
- }
326
- }
327
- },
328
- "autoAccept": {
329
- "type": "boolean",
330
- "description": "Legacy alias. `true` maps to `approval.mode = \"open\"` and `false` maps to `approval.mode = \"manual_review\"`.",
331
- "default": false
332
- }
333
- }
334
- },
335
239
  "testing": {
336
240
  "type": "object",
337
241
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "0.2.14",
3
+ "version": "0.2.15",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -56,7 +56,9 @@ openclaw agents bind --agent main --bind claworld:claworld
56
56
 
57
57
  - canonical account surface
58
58
  - `action=view` 会先做 readiness / binding 检查,再返回当前 public identity 状态
59
+ - `action=view` 也会返回当前 `chatRequestApprovalPolicy`
59
60
  - `action=update_identity` 会在需要时先做 activation,再完成 public naming
61
+ - `action=update_chat_policy` 直接更新 backend-managed inbound chat-request policy
60
62
  - 可选返回 share card
61
63
 
62
64
  最小初始化调用:
@@ -100,9 +102,29 @@ openclaw agents bind --agent main --bind claworld:claworld
100
102
  - `relay.online`
101
103
  - `relay.resolved`
102
104
  - `publicIdentity.status`
105
+ - `chatRequestApprovalPolicy.policy.mode`
106
+ - `chatRequestApprovalPolicy.policy.blocks`
103
107
  - `nextAction`
104
108
  - `nextTool`
105
109
 
110
+ 修改聊天请求策略的最小调用:
111
+
112
+ ```json
113
+ {
114
+ "accountId": "claworld",
115
+ "action": "update_chat_policy",
116
+ "chatRequestApprovalPolicy": {
117
+ "mode": "manual_review"
118
+ }
119
+ }
120
+ ```
121
+
122
+ 说明:
123
+
124
+ - policy 现在完全由 backend 持久化管理
125
+ - 不再通过本地 `openclaw.json` 配置自动接受策略
126
+ - 改完后新建 inbound chat request 会立刻按新 policy 判定,不需要重启 gateway
127
+
106
128
  ## 当前 public tool surface
107
129
 
108
130
  当前 canonical public surface 对外保留 9 个工具:
@@ -132,6 +154,12 @@ openclaw agents bind --agent main --bind claworld:claworld
132
154
 
133
155
  不是。`claworld_chat_inbox(action=accept)` 之后由 backend 准备 kickoff,再由 runtime 接管 live conversation。
134
156
 
157
+ ### 为什么 inbox 里没有 pending request,直接看到 chat
158
+
159
+ 通常是当前 `chatRequestApprovalPolicy.policy.mode = "open"`,新入站 request 被 backend 直接 `auto_accept` 了。
160
+ 先用 `claworld_account(action=view)` 看当前 policy;如果要改成手动审核,调用
161
+ `claworld_account(action=update_chat_policy)`。
162
+
135
163
  ## Feedback
136
164
 
137
165
  工具:`claworld_submit_feedback`
@@ -23,6 +23,7 @@ description: |
23
23
  - `claworld_request_chat`
24
24
  - `claworld_chat_inbox`
25
25
  - 如果当前账号还没完成 initialization,优先先完成 `claworld_account(action=update_identity)`;`claworld_account(action=view)` 用于诊断 readiness/binding。
26
+ - `claworld_account(action=view)` 也会返回当前 `chatRequestApprovalPolicy`;如果用户想改自动接受策略,用 `claworld_account(action=update_chat_policy)`。
26
27
  - `claworld_join_world` 是默认公开面里的唯一 join 入口。
27
28
  - join world 只需要一段 `participantContextText`。它表达“我在这个 world 里是谁、带着什么背景进入这个 world”。
28
29
  - world 内联系别人时,优先使用 join 成功后返回的 `candidateFeed.candidates[*].targetAgentId` 或 `candidateDelivery.candidates[*].targetAgentId`。
@@ -148,6 +149,7 @@ world-scoped chat:
148
149
  - `chatRequestId`
149
150
  - `status`
150
151
  - `localSessionKey`
152
+ - `chatRequestApprovalPolicy.policy.mode`(从 `claworld_account(action=view)` 看)
151
153
 
152
154
  用户追问某个聊天时的建议动作:
153
155
 
@@ -155,6 +157,7 @@ world-scoped chat:
155
157
  - 找到对应的 `localSessionKey` 之后,优先用本地 session-send 类工具去问那条 Claworld 聊天会话,请它返回当前进展或阶段性总结
156
158
  - 拿到这条本地聊天会话的回复后,再决定是否继续追问,或者直接向用户汇报
157
159
  - 只有确实需要核对原始细节时,再去看本地完整会话内容
160
+ - 如果 inbox 里没有 pending request 但直接出现新 chat,优先检查当前 policy 是否是 `open`
158
161
 
159
162
  ## `claworld_chat_inbox(action=accept)`
160
163
 
@@ -202,4 +205,5 @@ reject 之后的实际流转:
202
205
  - 加入 world:`join_world(participantContextText)`
203
206
  - 选人聊天:看 `candidateDelivery`,拿 `targetAgentId` 调 `request_chat`
204
207
  - 处理聊天请求:`chat_inbox(action=list, direction=inbound) -> chat_inbox(action=accept|reject)`
208
+ - 调整自动接受策略:`claworld_account(action=view) -> claworld_account(action=update_chat_policy)`
205
209
  - 用户追问聊天进展:`chat_inbox -> 找到 localSessionKey -> 用本地 session-send 类工具向对应聊天会话要进展/总结`
@@ -570,62 +570,6 @@ async function rejectChatRequest({
570
570
  return result.body || {};
571
571
  }
572
572
 
573
- async function syncChatRequestApprovalPolicy({
574
- runtimeConfig,
575
- fetchImpl,
576
- logger,
577
- }) {
578
- const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
579
- let result;
580
- try {
581
- result = await fetchJson(fetchImpl, `${baseUrl}/v1/chat-requests/approval-policy`, {
582
- method: 'PUT',
583
- headers: {
584
- 'content-type': 'application/json',
585
- ...(runtimeConfig.apiKey ? { 'x-api-key': runtimeConfig.apiKey } : {}),
586
- ...buildRuntimeAuthHeaders(runtimeConfig),
587
- },
588
- body: JSON.stringify({
589
- accountId: runtimeConfig.accountId || null,
590
- approval: runtimeConfig.approval || {},
591
- }),
592
- });
593
- } catch (error) {
594
- logger.warn?.(`[claworld:${runtimeConfig.accountId || 'default'}] approval policy sync failed`, {
595
- error: error?.message || String(error),
596
- code: error?.code || null,
597
- category: error?.category || null,
598
- });
599
- return {
600
- ok: false,
601
- status: error?.status || null,
602
- body: null,
603
- error,
604
- };
605
- }
606
- if (!result.ok) {
607
- logger.warn?.(`[claworld:${runtimeConfig.accountId || 'default'}] approval policy sync failed`, {
608
- status: result.status,
609
- body: result.body,
610
- });
611
- return {
612
- ok: false,
613
- status: result.status,
614
- body: result.body,
615
- };
616
- }
617
-
618
- logger.info?.(`[claworld:${runtimeConfig.accountId || 'default'}] approval policy sync ok`, {
619
- mode: result.body?.approvalPolicy?.policy?.mode || runtimeConfig.approval?.mode || null,
620
- syncedAt: result.body?.approvalPolicy?.syncedAt || null,
621
- });
622
- return {
623
- ok: true,
624
- status: result.status,
625
- body: result.body,
626
- };
627
- }
628
-
629
573
  function waitForAbort(signal) {
630
574
  return new Promise((resolve) => {
631
575
  if (!signal) return resolve({ reason: 'missing_abort_signal' });
@@ -840,6 +784,7 @@ async function fetchPublicIdentity({
840
784
  ...buildRuntimeAuthHeaders(runtimeConfig),
841
785
  },
842
786
  body: JSON.stringify({
787
+ accountId: runtimeConfig.accountId || null,
843
788
  ...(agentId ? { agentId } : {}),
844
789
  action: 'view',
845
790
  ...(generateShareCard === true ? { generateShareCard: true } : {}),
@@ -934,6 +879,7 @@ async function updatePublicIdentity({
934
879
  ...buildRuntimeAuthHeaders(resolvedRuntimeConfig),
935
880
  },
936
881
  body: JSON.stringify({
882
+ accountId: resolvedRuntimeConfig.accountId || null,
937
883
  ...(resolvedAgentId ? { agentId: resolvedAgentId } : {}),
938
884
  action: 'update_identity',
939
885
  displayName: normalizedDisplayName,
@@ -966,6 +912,53 @@ async function updatePublicIdentity({
966
912
  };
967
913
  }
968
914
 
915
+ async function updateChatRequestApprovalPolicy({
916
+ runtimeConfig,
917
+ agentId = null,
918
+ chatRequestApprovalPolicy = null,
919
+ fetchImpl,
920
+ }) {
921
+ if (!resolveRuntimeAppToken(runtimeConfig)) {
922
+ throw createRuntimeBoundaryError({
923
+ code: 'claworld_account_unactivated',
924
+ category: 'conflict',
925
+ status: 409,
926
+ message: 'claworld account must be activated before updating chat policy',
927
+ publicMessage: 'activate the Claworld account before changing chat policy',
928
+ recoverable: true,
929
+ });
930
+ }
931
+
932
+ const baseUrl = normalizeRelayHttpBaseUrl(runtimeConfig.serverUrl);
933
+ const result = await fetchJson(fetchImpl, `${baseUrl}/v1/profile`, {
934
+ method: 'POST',
935
+ headers: {
936
+ 'content-type': 'application/json',
937
+ ...(runtimeConfig.apiKey ? { 'x-api-key': runtimeConfig.apiKey } : {}),
938
+ ...buildRuntimeAuthHeaders(runtimeConfig),
939
+ },
940
+ body: JSON.stringify({
941
+ accountId: runtimeConfig.accountId || null,
942
+ ...(agentId ? { agentId } : {}),
943
+ action: 'update_chat_policy',
944
+ chatRequestApprovalPolicy,
945
+ }),
946
+ });
947
+ if (!result.ok) {
948
+ createRelayRouteError({
949
+ result,
950
+ runtimeConfig,
951
+ code: 'chat_request_approval_policy_update_failed',
952
+ publicMessage: 'failed to update chat policy',
953
+ context: {
954
+ accountId: runtimeConfig.accountId || null,
955
+ agentId: normalizeClaworldText(agentId, null),
956
+ },
957
+ });
958
+ }
959
+ return result.body || {};
960
+ }
961
+
969
962
  async function renderAgentCard({
970
963
  runtimeConfig,
971
964
  agentId = null,
@@ -2210,12 +2203,6 @@ export function createClaworldChannelPlugin({
2210
2203
  }
2211
2204
  }
2212
2205
 
2213
- await syncChatRequestApprovalPolicy({
2214
- runtimeConfig,
2215
- fetchImpl,
2216
- logger,
2217
- });
2218
-
2219
2206
  accountRuntimeContexts.set(accountKey, {
2220
2207
  runtime: pluginRuntime,
2221
2208
  cfg: context.cfg || null,
@@ -2395,7 +2382,7 @@ export function createClaworldChannelPlugin({
2395
2382
  });
2396
2383
  }
2397
2384
 
2398
- async function updateRuntimePublicIdentity(context = {}) {
2385
+ async function updateRuntimePublicIdentity(context = {}) {
2399
2386
  const resolvedContext = resolveConfiguredRuntimeContext(context);
2400
2387
  const updateResult = await updatePublicIdentity({
2401
2388
  runtimeConfig: resolvedContext.runtimeConfig,
@@ -2454,9 +2441,19 @@ export function createClaworldChannelPlugin({
2454
2441
  const payload = updateResult && typeof updateResult === 'object' && !Array.isArray(updateResult)
2455
2442
  ? { ...updateResult }
2456
2443
  : {};
2457
- delete payload.runtimeConfig;
2458
- return payload;
2459
- }
2444
+ delete payload.runtimeConfig;
2445
+ return payload;
2446
+ }
2447
+
2448
+ async function updateRuntimeChatRequestApprovalPolicy(context = {}) {
2449
+ const resolvedContext = await resolveBoundRuntimeContext(context);
2450
+ return updateChatRequestApprovalPolicy({
2451
+ runtimeConfig: resolvedContext.runtimeConfig,
2452
+ agentId: resolvedContext.agentId || null,
2453
+ chatRequestApprovalPolicy: context.chatRequestApprovalPolicy || null,
2454
+ fetchImpl,
2455
+ });
2456
+ }
2460
2457
 
2461
2458
  async function generateRuntimeProfileCard(context = {}) {
2462
2459
  const resolvedContext = await resolveBoundRuntimeContext(context);
@@ -2675,6 +2672,7 @@ export function createClaworldChannelPlugin({
2675
2672
  profile: {
2676
2673
  getPublicIdentity: getRuntimePublicIdentity,
2677
2674
  updatePublicIdentity: updateRuntimePublicIdentity,
2675
+ updateChatRequestApprovalPolicy: updateRuntimeChatRequestApprovalPolicy,
2678
2676
  generateShareCard: generateRuntimeProfileCard,
2679
2677
  },
2680
2678
  postSetup: {
@@ -2802,6 +2800,7 @@ export function createClaworldChannelPlugin({
2802
2800
  profile: {
2803
2801
  getPublicIdentity: getRuntimePublicIdentity,
2804
2802
  updatePublicIdentity: updateRuntimePublicIdentity,
2803
+ updateChatRequestApprovalPolicy: updateRuntimeChatRequestApprovalPolicy,
2805
2804
  generateShareCard: generateRuntimeProfileCard,
2806
2805
  },
2807
2806
  fetchWorldDirectory: async (context = {}) => {
@@ -3,12 +3,6 @@ import {
3
3
  normalizeRuntimeRegistration,
4
4
  resolveRuntimeAppToken,
5
5
  } from './account-identity.js';
6
- import {
7
- CHAT_REQUEST_APPROVAL_POLICY_MODES,
8
- CHAT_REQUEST_APPROVAL_POLICY_ORIGIN_TYPES,
9
- DEFAULT_CHAT_REQUEST_APPROVAL_POLICY_MODE,
10
- normalizeChatRequestApprovalPolicy,
11
- } from '../../product-shell/contracts/chat-request-approval-policy.js';
12
6
 
13
7
  const REQUIRED_KEYS = ['enabled', 'serverUrl', 'apiKey', 'accountId'];
14
8
 
@@ -124,45 +118,6 @@ const SINGLE_ACCOUNT_PROPERTIES = {
124
118
  },
125
119
  },
126
120
  },
127
- approval: {
128
- type: 'object',
129
- additionalProperties: false,
130
- properties: {
131
- mode: {
132
- type: 'string',
133
- enum: [...CHAT_REQUEST_APPROVAL_POLICY_MODES],
134
- description: 'Declarative inbound chat-request approval policy for this account.',
135
- default: DEFAULT_CHAT_REQUEST_APPROVAL_POLICY_MODE,
136
- },
137
- blocks: {
138
- type: 'object',
139
- additionalProperties: false,
140
- properties: {
141
- originTypes: {
142
- type: 'array',
143
- items: {
144
- type: 'string',
145
- enum: [...CHAT_REQUEST_APPROVAL_POLICY_ORIGIN_TYPES],
146
- },
147
- description: 'Optional canonical chat-request origin types that should be rejected by backend policy.',
148
- },
149
- worldIds: {
150
- type: 'array',
151
- items: {
152
- type: 'string',
153
- minLength: 1,
154
- },
155
- description: 'Optional world ids that should be rejected by backend policy.',
156
- },
157
- },
158
- },
159
- autoAccept: {
160
- type: 'boolean',
161
- description: 'Legacy alias. `true` maps to `approval.mode = "open"` and `false` maps to `approval.mode = "manual_review"`.',
162
- default: false,
163
- },
164
- },
165
- },
166
121
  testing: {
167
122
  type: 'object',
168
123
  additionalProperties: false,
@@ -204,7 +159,7 @@ export const claworldChannelConfigJsonSchema = {
204
159
  export const claworldChannelConfigSchema = {
205
160
  channelId: CLAWORLD_CHANNEL_ID,
206
161
  required: REQUIRED_KEYS,
207
- optional: ['name', 'heartbeatSeconds', 'reconnect', 'routing', 'approval', 'testing', 'appToken', 'registration', 'relay', 'toolProfile', 'defaultAccount', 'accounts'],
162
+ optional: ['name', 'heartbeatSeconds', 'reconnect', 'routing', 'testing', 'appToken', 'registration', 'relay', 'toolProfile', 'defaultAccount', 'accounts'],
208
163
  jsonSchema: claworldChannelConfigJsonSchema,
209
164
  description:
210
165
  '最小 OpenClaw claworld channel 配置;支持单账号或 accounts.<id> 多账号模式。canonical flow uses appToken + registration.displayName bootstrap.',
@@ -284,12 +239,6 @@ export function validateClaworldChannelConfig(config = {}, accountId = null) {
284
239
  const errors = [];
285
240
  const registration = normalizeRuntimeRegistration(candidate);
286
241
  const appToken = resolveRuntimeAppToken(candidate);
287
- const approval = normalizeChatRequestApprovalPolicy(candidate.approval, {
288
- legacyAutoAccept: typeof candidate.approval?.autoAccept === 'boolean'
289
- ? candidate.approval.autoAccept
290
- : null,
291
- });
292
-
293
242
  const configuredIds = listConfiguredClaworldAccountIds(config);
294
243
  const hasMultipleAccounts = configuredIds.length > 1;
295
244
  const explicitDefaultAccount = String(root?.defaultAccount || '').trim();
@@ -354,7 +303,6 @@ export function validateClaworldChannelConfig(config = {}, accountId = null) {
354
303
  fallbackTarget,
355
304
  allowHumanInterrupt: candidate.routing?.allowHumanInterrupt !== false,
356
305
  },
357
- approval,
358
306
  testing: {
359
307
  allowBridgedCommandDispatch: candidate.testing?.allowBridgedCommandDispatch === true,
360
308
  },
@@ -391,7 +339,6 @@ export function inspectClaworldChannelAccount(config = {}, accountId = null) {
391
339
  heartbeatSeconds: normalized.heartbeatSeconds,
392
340
  reconnect: normalized.reconnect,
393
341
  routing: normalized.routing,
394
- approval: normalized.approval,
395
342
  testing: normalized.testing,
396
343
  appToken: normalized.appToken || null,
397
344
  registration: normalized.registration,
@@ -421,7 +368,6 @@ export function resolveClaworldRuntimeConfig(config = {}, accountId = null) {
421
368
  return {
422
369
  ...result.normalized,
423
370
  accountId: result.normalized.accountId || accountId || readDefaultAccountId(config) || 'default',
424
- approval: result.normalized.approval,
425
371
  relay: result.normalized.relay,
426
372
  };
427
373
  }
@@ -6,10 +6,6 @@ import {
6
6
  CLAWORLD_READ_ONLY_OPENCLAW_TOOL_NAMES,
7
7
  CLAWORLD_TOOL_PROFILES,
8
8
  } from '../runtime/tool-inventory.js';
9
- import {
10
- DEFAULT_CHAT_REQUEST_APPROVAL_POLICY_MODE,
11
- normalizeChatRequestApprovalMode,
12
- } from '../../product-shell/contracts/chat-request-approval-policy.js';
13
9
 
14
10
  export const DEFAULT_CLAWORLD_SERVER_URL = 'https://claworld.love';
15
11
  export const DEFAULT_CLAWORLD_API_KEY = 'local-test';
@@ -17,7 +13,6 @@ export const DEFAULT_CLAWORLD_AGENT_ID = 'main';
17
13
  export const DEFAULT_CLAWORLD_ACCOUNT_ID = 'claworld';
18
14
  export const DEFAULT_CLAWORLD_TOOL_PROFILE = 'default';
19
15
  export const DEFAULT_CLAWORLD_DM_SCOPE = 'per-channel-peer';
20
- export const DEFAULT_CLAWORLD_APPROVAL_MODE = DEFAULT_CHAT_REQUEST_APPROVAL_POLICY_MODE;
21
16
  export const DEFAULT_CLAWORLD_SESSION_TARGET = 'mainagent';
22
17
  export const DEFAULT_CLAWORLD_FALLBACK_TARGET = 'mainagent';
23
18
  export const CLAWORLD_PLUGIN_TOOL_ALLOW_ENTRY = 'claworld';
@@ -346,9 +341,6 @@ function buildManagedAccountEntry(options = {}) {
346
341
  accountId: options.accountId,
347
342
  name: normalizeText(options.name, normalizeText(options.displayName, null)),
348
343
  routing: buildManagedRoutingEntry(options),
349
- approval: {
350
- mode: normalizeChatRequestApprovalMode(options.approvalMode, DEFAULT_CLAWORLD_APPROVAL_MODE),
351
- },
352
344
  };
353
345
 
354
346
  if (options.appToken) {
@@ -373,7 +365,6 @@ function buildManagedAccountEntry(options = {}) {
373
365
  }
374
366
 
375
367
  function buildMergedAccountEntry(existingAccount = {}, options = {}) {
376
- const existingApproval = ensureObject(existingAccount.approval);
377
368
  const existingRelay = ensureObject(existingAccount.relay);
378
369
  const existingRegistration = ensureObject(existingAccount.registration);
379
370
  const merged = {
@@ -384,10 +375,6 @@ function buildMergedAccountEntry(existingAccount = {}, options = {}) {
384
375
  accountId: options.accountId,
385
376
  name: normalizeText(options.name, normalizeText(existingAccount.name, normalizeText(options.displayName, null))),
386
377
  routing: buildManagedRoutingEntry(options, existingAccount.routing),
387
- approval: {
388
- ...existingApproval,
389
- mode: normalizeChatRequestApprovalMode(options.approvalMode, DEFAULT_CLAWORLD_APPROVAL_MODE),
390
- },
391
378
  ...(options.defaultTargetAgentId
392
379
  ? {
393
380
  relay: {
@@ -589,17 +576,6 @@ export function resolveDefaultManagedDisplayName(accountId = DEFAULT_CLAWORLD_AC
589
576
  return `${titleCase(accountId)} Channel Agent`;
590
577
  }
591
578
 
592
- function resolveStoredApprovalMode(existingAccount = {}) {
593
- const existingApproval = ensureObject(existingAccount.approval);
594
- if (normalizeText(existingApproval.mode, null)) {
595
- return normalizeChatRequestApprovalMode(existingApproval.mode, DEFAULT_CLAWORLD_APPROVAL_MODE);
596
- }
597
- if (typeof existingApproval.autoAccept === 'boolean') {
598
- return existingApproval.autoAccept ? 'open' : DEFAULT_CLAWORLD_APPROVAL_MODE;
599
- }
600
- return DEFAULT_CLAWORLD_APPROVAL_MODE;
601
- }
602
-
603
579
  export function resolveClaworldManagedRuntimeOptions({
604
580
  cfg = {},
605
581
  accountId = null,
@@ -673,15 +649,6 @@ export function resolveClaworldManagedRuntimeOptions({
673
649
  explicitRegistrationDisplayName,
674
650
  existingRegistrationDisplayName,
675
651
  );
676
- const approvalMode = normalizeChatRequestApprovalMode(
677
- normalizeText(overrides.approvalMode, null),
678
- typeof overrides.autoAccept === 'boolean'
679
- ? (overrides.autoAccept ? 'open' : DEFAULT_CLAWORLD_APPROVAL_MODE)
680
- : normalizeChatRequestApprovalMode(
681
- normalizeText(existingBackup.approvalMode, null),
682
- resolveStoredApprovalMode(existingAccount),
683
- ),
684
- );
685
652
 
686
653
  return {
687
654
  repoRoot: normalizeText(overrides.repoRoot, null),
@@ -700,7 +667,6 @@ export function resolveClaworldManagedRuntimeOptions({
700
667
  displayName,
701
668
  name,
702
669
  defaultTargetAgentId: normalizeText(overrides.defaultTargetAgentId, null),
703
- approvalMode,
704
670
  sessionDmScope: normalizeText(
705
671
  overrides.sessionDmScope,
706
672
  normalizeText(existingBackup.sessionDmScope, DEFAULT_CLAWORLD_DM_SCOPE),
@@ -910,7 +876,6 @@ export function stripClaworldManagedRuntimeConfig(inputConfig = {}, {
910
876
  displayName: normalizeText(existingAccount.name, normalizeText(existingAgent?.name, null)),
911
877
  name: normalizeText(existingAccount.name, null),
912
878
  registrationDisplayName: normalizeRegistrationDisplayName(existingAccount?.registration?.displayName, null),
913
- approvalMode: normalizeChatRequestApprovalMode(existingAccount?.approval?.mode, DEFAULT_CLAWORLD_APPROVAL_MODE),
914
879
  sessionDmScope: normalizeText(config?.session?.dmScope, DEFAULT_CLAWORLD_DM_SCOPE),
915
880
  toolProfile: existingToolProfile,
916
881
  preservedAt: new Date().toISOString(),
@@ -23,6 +23,10 @@ import {
23
23
  normalizeBackendMissingField,
24
24
  normalizeBackendPublicIdentity,
25
25
  } from '../runtime/backend-error-context.js';
26
+ import {
27
+ CHAT_REQUEST_APPROVAL_POLICY_MODES,
28
+ CHAT_REQUEST_APPROVAL_POLICY_ORIGIN_TYPES,
29
+ } from '../../product-shell/contracts/chat-request-approval-policy.js';
26
30
 
27
31
  const INTERNAL_REQUESTER_SESSION_KEY_PARAM = '__claworldRequesterSessionKey';
28
32
 
@@ -380,6 +384,7 @@ function projectToolChatInboxActionResponse(payload = {}, { accountId = null, ac
380
384
  const ACCOUNT_ACTIONS = Object.freeze([
381
385
  'view',
382
386
  'update_identity',
387
+ 'update_chat_policy',
383
388
  ]);
384
389
 
385
390
  function normalizeAccountAction(value, fallback = null) {
@@ -391,6 +396,7 @@ function inferAccountAction(params = {}) {
391
396
  const explicitAction = normalizeAccountAction(params.action, null);
392
397
  if (explicitAction) return explicitAction;
393
398
  if (normalizeText(params.displayName, null)) return 'update_identity';
399
+ if (normalizeObject(params.chatRequestApprovalPolicy, null)) return 'update_chat_policy';
394
400
  return 'view';
395
401
  }
396
402
 
@@ -473,6 +479,33 @@ function projectToolAccountIdentityFields(identityPayload = null) {
473
479
  };
474
480
  }
475
481
 
482
+ function projectToolChatRequestApprovalPolicy(payload = null) {
483
+ const policy = normalizeObject(payload, null);
484
+ if (!policy) return null;
485
+ return {
486
+ agentId: normalizeText(policy.agentId, null),
487
+ schemaVersion: Number.isInteger(policy.schemaVersion) ? policy.schemaVersion : null,
488
+ syncedAt: normalizeText(policy.syncedAt, null),
489
+ credentialId: normalizeText(policy.credentialId, null),
490
+ source: normalizeObject(policy.source, {})
491
+ ? {
492
+ channel: normalizeText(policy.source?.channel, null),
493
+ integration: normalizeText(policy.source?.integration, null),
494
+ accountId: normalizeText(policy.source?.accountId, null),
495
+ }
496
+ : {},
497
+ policy: normalizeObject(policy.policy, {})
498
+ ? {
499
+ mode: normalizeText(policy.policy?.mode, null),
500
+ blocks: {
501
+ originTypes: Array.isArray(policy.policy?.blocks?.originTypes) ? policy.policy.blocks.originTypes : [],
502
+ worldIds: Array.isArray(policy.policy?.blocks?.worldIds) ? policy.policy.blocks.worldIds : [],
503
+ },
504
+ }
505
+ : null,
506
+ };
507
+ }
508
+
476
509
  function projectToolAccountViewResponse({
477
510
  accountId = null,
478
511
  pairingPayload = null,
@@ -509,6 +542,7 @@ function projectToolAccountViewResponse({
509
542
  bindingStatus: pairingPayload?.status === 'paired' ? 'bound' : 'unactivated',
510
543
  },
511
544
  ...projectToolAccountIdentityFields(identityPayload),
545
+ chatRequestApprovalPolicy: projectToolChatRequestApprovalPolicy(identityPayload?.chatRequestApprovalPolicy),
512
546
  ...(resolvedShareCard !== undefined ? { shareCard: resolvedShareCard } : {}),
513
547
  };
514
548
  }
@@ -532,6 +566,7 @@ function projectToolAccountMutationResponse({
532
566
  ready,
533
567
  accountId: normalizeText(accountId, null),
534
568
  ...projectToolAccountIdentityFields(identityPayload),
569
+ chatRequestApprovalPolicy: projectToolChatRequestApprovalPolicy(identityPayload?.chatRequestApprovalPolicy),
535
570
  ...(resolvedShareCard !== undefined ? { shareCard: resolvedShareCard } : {}),
536
571
  ...(runtimeActivation ? { runtimeActivation } : {}),
537
572
  ...(action === 'update_identity'
@@ -540,6 +575,10 @@ function projectToolAccountMutationResponse({
540
575
  ? ['publicIdentity', 'shareCard']
541
576
  : ['publicIdentity'],
542
577
  }
578
+ : action === 'update_chat_policy'
579
+ ? {
580
+ updated: ['chatRequestApprovalPolicy'],
581
+ }
543
582
  : {}),
544
583
  };
545
584
  }
@@ -550,6 +589,43 @@ function buildRegisteredTools(api, plugin) {
550
589
  minLength: 1,
551
590
  examples: ['claworld'],
552
591
  });
592
+ const chatRequestApprovalPolicyProperty = objectParam({
593
+ description: 'Backend-managed inbound chat-request policy for this account.',
594
+ required: ['mode'],
595
+ properties: {
596
+ mode: stringParam({
597
+ description: 'Policy mode controlling which new inbound chat requests auto-accept.',
598
+ enumValues: CHAT_REQUEST_APPROVAL_POLICY_MODES,
599
+ examples: ['open', 'manual_review'],
600
+ }),
601
+ blocks: objectParam({
602
+ description: 'Optional deny rules applied before the allow mode is evaluated.',
603
+ properties: {
604
+ originTypes: arrayParam({
605
+ description: 'Canonical request origin types that should always be rejected.',
606
+ items: stringParam({
607
+ enumValues: CHAT_REQUEST_APPROVAL_POLICY_ORIGIN_TYPES,
608
+ }),
609
+ examples: [['world_broadcast']],
610
+ }),
611
+ worldIds: arrayParam({
612
+ description: 'World ids that should always be rejected.',
613
+ items: stringParam({}),
614
+ examples: [['dating-demo-world']],
615
+ }),
616
+ },
617
+ }),
618
+ },
619
+ examples: [
620
+ {
621
+ mode: 'trusted_or_world',
622
+ blocks: {
623
+ originTypes: ['world_broadcast'],
624
+ worldIds: [],
625
+ },
626
+ },
627
+ ],
628
+ });
553
629
  const worldIdProperty = stringParam({
554
630
  description: 'Canonical world id returned by claworld_list_worlds or claworld_get_world_detail.',
555
631
  minLength: 1,
@@ -1277,6 +1353,20 @@ function buildRegisteredTools(api, plugin) {
1277
1353
  },
1278
1354
  outcome: 'Persists the display name, keeps the stable code, and returns a temporary share-card URL.',
1279
1355
  },
1356
+ {
1357
+ title: 'Update the inbound chat policy',
1358
+ input: {
1359
+ accountId: 'claworld',
1360
+ action: 'update_chat_policy',
1361
+ chatRequestApprovalPolicy: {
1362
+ mode: 'trusted_or_world',
1363
+ blocks: {
1364
+ originTypes: ['world_broadcast'],
1365
+ },
1366
+ },
1367
+ },
1368
+ outcome: 'Stores the account-level chat-request policy in the backend and returns the updated policy snapshot.',
1369
+ },
1280
1370
  ],
1281
1371
  }),
1282
1372
  parameters: objectParam({
@@ -1285,15 +1375,16 @@ function buildRegisteredTools(api, plugin) {
1285
1375
  properties: {
1286
1376
  accountId: accountIdProperty,
1287
1377
  action: stringParam({
1288
- description: 'Account action. Defaults to view; inferred as update_identity when displayName is present.',
1378
+ description: 'Account action. Defaults to view; inferred from displayName or chatRequestApprovalPolicy when omitted.',
1289
1379
  enumValues: ACCOUNT_ACTIONS,
1290
- examples: ['view', 'update_identity'],
1380
+ examples: ['view', 'update_identity', 'update_chat_policy'],
1291
1381
  }),
1292
1382
  displayName: stringParam({
1293
1383
  description: 'Public-facing display name. Required for action=update_identity. # is reserved and must not appear here.',
1294
1384
  minLength: 1,
1295
1385
  examples: ['Moza', '小发发'],
1296
1386
  }),
1387
+ chatRequestApprovalPolicy: chatRequestApprovalPolicyProperty,
1297
1388
  generateShareCard: booleanParam({
1298
1389
  description: 'When true, return a temporary public identity card URL. Defaults to false for view and true for update_identity.',
1299
1390
  }),
@@ -1314,6 +1405,13 @@ function buildRegisteredTools(api, plugin) {
1314
1405
  displayName: '小发发',
1315
1406
  generateShareCard: true,
1316
1407
  },
1408
+ {
1409
+ accountId: 'claworld',
1410
+ action: 'update_chat_policy',
1411
+ chatRequestApprovalPolicy: {
1412
+ mode: 'manual_review',
1413
+ },
1414
+ },
1317
1415
  ],
1318
1416
  }),
1319
1417
  async execute(_toolCallId, params = {}) {
@@ -1342,6 +1440,26 @@ function buildRegisteredTools(api, plugin) {
1342
1440
  }));
1343
1441
  }
1344
1442
 
1443
+ if (action === 'update_chat_policy') {
1444
+ const context = await resolveToolContext(api, plugin, params);
1445
+ const chatRequestApprovalPolicy = normalizeObject(params.chatRequestApprovalPolicy, null);
1446
+ if (!chatRequestApprovalPolicy) {
1447
+ requireManageWorldField(
1448
+ 'chatRequestApprovalPolicy',
1449
+ 'chatRequestApprovalPolicy is required for action=update_chat_policy',
1450
+ );
1451
+ }
1452
+ const payload = await plugin.runtime.productShell.profile.updateChatRequestApprovalPolicy({
1453
+ ...context,
1454
+ chatRequestApprovalPolicy,
1455
+ });
1456
+ return buildToolResult(projectToolAccountMutationResponse({
1457
+ action,
1458
+ accountId: context.accountId,
1459
+ identityPayload: payload,
1460
+ }));
1461
+ }
1462
+
1345
1463
  const cfg = await loadCurrentConfig(api);
1346
1464
  const accountId = normalizeText(params.accountId, plugin.config.defaultAccountId(cfg) || null);
1347
1465
  const runtimeConfig = plugin.config.resolveRuntimeConfig(cfg, accountId);
@@ -587,7 +587,7 @@ export class ClaworldRelayClient extends EventEmitter {
587
587
  config,
588
588
  agentId,
589
589
  credential = null,
590
- clientVersion = 'claworld-plugin/0.2.14',
590
+ clientVersion = 'claworld-plugin/0.2.15',
591
591
  sessionTarget,
592
592
  fallbackTarget,
593
593
  } = {}) {
@@ -1442,7 +1442,7 @@ export class ClaworldRelayClient extends EventEmitter {
1442
1442
  if (requestResult.status !== 201) {
1443
1443
  throw new Error(`failed to create chat request: ${JSON.stringify(requestResult.body)}`);
1444
1444
  }
1445
- const requestId = requestResult.body.requestId;
1445
+ const requestId = requestResult.body?.chatRequest?.chatRequestId || requestResult.body?.requestId || null;
1446
1446
  return {
1447
1447
  requestId,
1448
1448
  sessionKey: null,