@xfxstudio/claworld 2026.7.1-testing.2 → 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.
@@ -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';
@@ -32,6 +35,10 @@ import {
32
35
 
33
36
  const DELIVERY_VISIBILITY_RETRY_ATTEMPTS = 20;
34
37
  const DELIVERY_VISIBILITY_RETRY_DELAY_MS = 10;
38
+ const DEFAULT_RELAY_AUTH_TIMEOUT_MS = 30_000;
39
+ const DEFAULT_RECONNECT_BASE_DELAY_MS = 1_000;
40
+ const DEFAULT_RECONNECT_MAX_DELAY_MS = 60_000;
41
+ const DEFAULT_RECONNECT_JITTER_RATIO = 0.2;
35
42
 
36
43
  function isDeliveryVisibilityMiss(result = {}) {
37
44
  return Number(result?.status) === 404
@@ -53,6 +60,11 @@ export class ClaworldRelayClient extends EventEmitter {
53
60
  protocol = createRelayEventProtocol(),
54
61
  wsFactory = (url) => new WebSocket(url),
55
62
  httpFetch = globalThis.fetch,
63
+ authTimeoutMs = DEFAULT_RELAY_AUTH_TIMEOUT_MS,
64
+ reconnectBaseDelayMs = DEFAULT_RECONNECT_BASE_DELAY_MS,
65
+ reconnectMaxDelayMs = DEFAULT_RECONNECT_MAX_DELAY_MS,
66
+ reconnectJitterRatio = DEFAULT_RECONNECT_JITTER_RATIO,
67
+ random = Math.random,
56
68
  } = {}) {
57
69
  super();
58
70
  this.logger = logger;
@@ -61,6 +73,11 @@ export class ClaworldRelayClient extends EventEmitter {
61
73
  this.protocol = protocol;
62
74
  this.wsFactory = wsFactory;
63
75
  this.httpFetch = httpFetch;
76
+ this.authTimeoutMs = Math.max(1, Math.floor(Number(authTimeoutMs) || DEFAULT_RELAY_AUTH_TIMEOUT_MS));
77
+ this.reconnectBaseDelayMs = Math.max(1, Math.floor(Number(reconnectBaseDelayMs) || DEFAULT_RECONNECT_BASE_DELAY_MS));
78
+ this.reconnectMaxDelayMs = Math.max(this.reconnectBaseDelayMs, Math.floor(Number(reconnectMaxDelayMs) || DEFAULT_RECONNECT_MAX_DELAY_MS));
79
+ this.reconnectJitterRatio = Math.max(0, Math.min(1, Number(reconnectJitterRatio) || 0));
80
+ this.random = typeof random === 'function' ? random : Math.random;
64
81
  this.ws = null;
65
82
  this.events = [];
66
83
  this.heartbeatTimer = null;
@@ -103,9 +120,17 @@ export class ClaworldRelayClient extends EventEmitter {
103
120
  this.reconnectTimer = null;
104
121
  }
105
122
 
106
- resolveReconnectDelayMs() {
107
- const heartbeatSeconds = Number(this.runtimeConfig?.heartbeatSeconds || 1);
108
- return Math.min(5000, Math.max(500, Math.floor(heartbeatSeconds * 500)));
123
+ resolveReconnectDelayMs(attempt = this.reconnectAttempts + 1) {
124
+ const normalizedAttempt = Math.max(1, Math.floor(Number(attempt) || 1));
125
+ const exponent = Math.min(normalizedAttempt - 1, 10);
126
+ const baseDelayMs = Math.min(
127
+ this.reconnectMaxDelayMs,
128
+ this.reconnectBaseDelayMs * (2 ** exponent),
129
+ );
130
+ const jitterWindowMs = Math.floor(baseDelayMs * this.reconnectJitterRatio);
131
+ if (jitterWindowMs <= 0) return baseDelayMs;
132
+ const jitterMs = Math.floor(Math.max(0, Math.min(1, Number(this.random()) || 0)) * jitterWindowMs);
133
+ return Math.min(this.reconnectMaxDelayMs, baseDelayMs + jitterMs);
109
134
  }
110
135
 
111
136
  shouldAutoReconnect(disconnectInfo = null) {
@@ -285,9 +310,59 @@ export class ClaworldRelayClient extends EventEmitter {
285
310
  return await new Promise((resolve, reject) => {
286
311
  let settled = false;
287
312
  let suppressCloseHandler = false;
313
+ let authTimer = null;
288
314
  const ws = this.wsFactory(wsUrl);
289
315
  this.ws = ws;
290
316
 
317
+ const clearAuthTimer = () => {
318
+ if (authTimer) clearTimeout(authTimer);
319
+ authTimer = null;
320
+ };
321
+
322
+ const closeSocketAfterFailedAuth = (reason = 'auth_failed') => {
323
+ try {
324
+ if (this.ws === ws) this.ws = null;
325
+ if (ws.readyState !== 3) ws.close(1008, reason);
326
+ } catch {
327
+ // No-op.
328
+ }
329
+ };
330
+
331
+ const settleAuthResolve = (message) => {
332
+ if (settled) return;
333
+ settled = true;
334
+ clearAuthTimer();
335
+ resolve(message);
336
+ };
337
+
338
+ const settleAuthReject = (error, { suppressClose = false, closeReason = null } = {}) => {
339
+ if (settled) return;
340
+ settled = true;
341
+ clearAuthTimer();
342
+ if (suppressClose) suppressCloseHandler = true;
343
+ this.connectionState = 'error';
344
+ if (closeReason) closeSocketAfterFailedAuth(closeReason);
345
+ reject(error);
346
+ };
347
+
348
+ authTimer = setTimeout(() => {
349
+ const timeoutError = createRuntimeBoundaryError({
350
+ code: 'relay_auth_timeout',
351
+ category: 'transport',
352
+ status: 504,
353
+ message: `timed out waiting for relay authentication after ${this.authTimeoutMs}ms`,
354
+ publicMessage: 'relay authentication timed out',
355
+ recoverable: true,
356
+ context: this.buildBoundaryContext({
357
+ stage: 'auth',
358
+ timeoutMs: this.authTimeoutMs,
359
+ }),
360
+ });
361
+ timeoutError.reason = 'auth_timeout';
362
+ timeoutError.close = this.buildDisconnectInfo(null, 'auth_timeout', 'auth');
363
+ settleAuthReject(timeoutError, { suppressClose: true, closeReason: 'auth_timeout' });
364
+ }, this.authTimeoutMs);
365
+
291
366
  ws.on('open', () => {
292
367
  this.logger.info?.('[claworld:relay-client] websocket open, sending auth', {
293
368
  accountId: this.runtimeConfig.accountId,
@@ -295,13 +370,18 @@ export class ClaworldRelayClient extends EventEmitter {
295
370
  clientVersion,
296
371
  bridgeProtocol: this.protocol.version,
297
372
  });
298
- this.send({
299
- type: 'auth',
300
- agentId,
301
- credential,
302
- clientVersion,
303
- bridgeProtocol: this.protocol.version,
304
- });
373
+ try {
374
+ this.send({
375
+ type: 'auth',
376
+ agentId,
377
+ credential,
378
+ client: CLAWORLD_OPENCLAW_PLUGIN_CLIENT,
379
+ clientVersion,
380
+ bridgeProtocol: this.protocol.version,
381
+ });
382
+ } catch (error) {
383
+ settleAuthReject(error, { suppressClose: true, closeReason: 'auth_send_failed' });
384
+ }
305
385
  });
306
386
 
307
387
  ws.on('message', (buf) => {
@@ -316,9 +396,7 @@ export class ClaworldRelayClient extends EventEmitter {
316
396
  context: { stage: 'message_parse' },
317
397
  });
318
398
  if (!settled) {
319
- settled = true;
320
- this.connectionState = 'error';
321
- reject(normalized);
399
+ settleAuthReject(normalized, { suppressClose: true, closeReason: 'message_parse_failed' });
322
400
  }
323
401
  return;
324
402
  }
@@ -327,7 +405,6 @@ export class ClaworldRelayClient extends EventEmitter {
327
405
  this.emitRelayMessage(message, { sessionTarget, fallbackTarget });
328
406
 
329
407
  if (message.event === 'auth.ok' && !settled) {
330
- settled = true;
331
408
  this.connectionState = 'authenticated';
332
409
  this.reconnectAttempts = 0;
333
410
  this.logger.info?.('[claworld:relay-client] auth ok', {
@@ -335,13 +412,10 @@ export class ClaworldRelayClient extends EventEmitter {
335
412
  agentId,
336
413
  });
337
414
  this.startHeartbeatLoop();
338
- resolve(message);
415
+ settleAuthResolve(message);
339
416
  }
340
417
 
341
418
  if (message.event === 'error' && !settled && message.data?.code === 'unauthorized') {
342
- settled = true;
343
- suppressCloseHandler = true;
344
- this.connectionState = 'error';
345
419
  const authReason = message.data?.reason || message.data?.error || 'unauthorized';
346
420
  const authError = createRuntimeBoundaryError({
347
421
  code: message.data?.code || 'unauthorized',
@@ -364,7 +438,7 @@ export class ClaworldRelayClient extends EventEmitter {
364
438
  error: authError.message,
365
439
  code: authError.code,
366
440
  });
367
- reject(authError);
441
+ settleAuthReject(authError, { suppressClose: true, closeReason: authReason });
368
442
  }
369
443
  } catch (error) {
370
444
  const normalized = this.emitBoundaryError('[claworld:relay-client] relay message handling failed', error, {
@@ -377,10 +451,7 @@ export class ClaworldRelayClient extends EventEmitter {
377
451
  },
378
452
  });
379
453
  if (!settled) {
380
- settled = true;
381
- suppressCloseHandler = true;
382
- this.connectionState = 'error';
383
- reject(normalized);
454
+ settleAuthReject(normalized, { suppressClose: true, closeReason: 'message_handling_failed' });
384
455
  }
385
456
  }
386
457
  });
@@ -402,6 +473,7 @@ export class ClaworldRelayClient extends EventEmitter {
402
473
 
403
474
  if (!settled) {
404
475
  settled = true;
476
+ clearAuthTimer();
405
477
  this.connectionState = disconnectInfo.reason === 'duplicate_connection_replaced' ? 'replaced' : 'error';
406
478
  reject(this.createClosedBeforeAuthError(disconnectInfo));
407
479
  return;
@@ -430,11 +502,8 @@ export class ClaworldRelayClient extends EventEmitter {
430
502
  },
431
503
  });
432
504
  if (!settled) {
433
- settled = true;
434
- suppressCloseHandler = true;
435
- this.connectionState = 'error';
436
505
  normalized.reason = normalized.reason || normalized.code || normalized.message;
437
- reject(normalized);
506
+ settleAuthReject(normalized, { suppressClose: true, closeReason: 'connect_error' });
438
507
  }
439
508
  });
440
509
  });
@@ -448,8 +517,8 @@ export class ClaworldRelayClient extends EventEmitter {
448
517
  }
449
518
 
450
519
  this.clearReconnectTimer();
451
- const delayMs = this.resolveReconnectDelayMs();
452
520
  const attempt = this.reconnectAttempts + 1;
521
+ const delayMs = this.resolveReconnectDelayMs(attempt);
453
522
  this.reconnectAttempts = attempt;
454
523
  this.logger.warn?.('[claworld:relay-client] scheduling reconnect', {
455
524
  accountId: this.runtimeConfig?.accountId || null,
@@ -487,7 +556,7 @@ export class ClaworldRelayClient extends EventEmitter {
487
556
  config,
488
557
  agentId,
489
558
  credential = null,
490
- clientVersion = buildClaworldRelayClientVersion(),
559
+ clientVersion = CLAWORLD_PLUGIN_CURRENT_VERSION,
491
560
  sessionTarget,
492
561
  fallbackTarget,
493
562
  } = {}) {
@@ -793,7 +862,7 @@ export class ClaworldRelayClient extends EventEmitter {
793
862
  });
794
863
  }
795
864
 
796
- waitForAcceptedAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS } = {}) {
865
+ waitForAcceptedAck({ deliveryId, timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS, signal = null } = {}) {
797
866
  const normalizedDeliveryId = normalizeOptionalText(deliveryId);
798
867
  if (!normalizedDeliveryId) {
799
868
  return Promise.reject(createRuntimeBoundaryError({
@@ -805,6 +874,20 @@ export class ClaworldRelayClient extends EventEmitter {
805
874
  recoverable: true,
806
875
  }));
807
876
  }
877
+ if (signal?.aborted) {
878
+ return Promise.reject(createRuntimeBoundaryError({
879
+ code: 'relay_delivery_accept_ack_wait_cancelled',
880
+ category: 'transport',
881
+ status: 499,
882
+ message: `relay delivery acceptance acknowledgement wait cancelled for ${normalizedDeliveryId}`,
883
+ publicMessage: 'relay delivery acceptance acknowledgement wait cancelled',
884
+ recoverable: true,
885
+ context: this.buildBoundaryContext({
886
+ stage: 'delivery_accept_ack_wait',
887
+ deliveryId: normalizedDeliveryId,
888
+ }),
889
+ }));
890
+ }
808
891
 
809
892
  return new Promise((resolve, reject) => {
810
893
  let settled = false;
@@ -815,6 +898,7 @@ export class ClaworldRelayClient extends EventEmitter {
815
898
  this.off('delivery.accepted', onAccepted);
816
899
  this.off('disconnect', onDisconnect);
817
900
  this.off('close', onDisconnect);
901
+ signal?.removeEventListener('abort', onAbort);
818
902
  };
819
903
 
820
904
  const settleResolve = (value) => {
@@ -854,9 +938,25 @@ export class ClaworldRelayClient extends EventEmitter {
854
938
  }));
855
939
  };
856
940
 
941
+ const onAbort = () => {
942
+ settleReject(createRuntimeBoundaryError({
943
+ code: 'relay_delivery_accept_ack_wait_cancelled',
944
+ category: 'transport',
945
+ status: 499,
946
+ message: `relay delivery acceptance acknowledgement wait cancelled for ${normalizedDeliveryId}`,
947
+ publicMessage: 'relay delivery acceptance acknowledgement wait cancelled',
948
+ recoverable: true,
949
+ context: this.buildBoundaryContext({
950
+ stage: 'delivery_accept_ack_wait',
951
+ deliveryId: normalizedDeliveryId,
952
+ }),
953
+ }));
954
+ };
955
+
857
956
  this.on('delivery.accepted', onAccepted);
858
957
  this.on('disconnect', onDisconnect);
859
958
  this.on('close', onDisconnect);
959
+ signal?.addEventListener('abort', onAbort, { once: true });
860
960
 
861
961
  timeout = setTimeout(() => {
862
962
  settleReject(buildAcceptedAckTimeoutError({
@@ -1097,6 +1197,52 @@ export class ClaworldRelayClient extends EventEmitter {
1097
1197
  };
1098
1198
  }
1099
1199
 
1200
+ async submitAcceptedHttpFallback({
1201
+ deliveryId,
1202
+ sessionKey,
1203
+ source = 'runtime_dispatch',
1204
+ error = null,
1205
+ } = {}) {
1206
+ this.logger.warn?.('[claworld:relay-client] delivery acceptance websocket transport failed; attempting HTTP fallback', {
1207
+ accountId: this.runtimeConfig?.accountId || null,
1208
+ agentId: this.boundAgentId,
1209
+ deliveryId: normalizeOptionalText(deliveryId),
1210
+ sessionKey: normalizeOptionalText(sessionKey) || null,
1211
+ error: error?.message || String(error),
1212
+ });
1213
+
1214
+ const fallbackResult = await this.acceptDeliveryHttp({
1215
+ deliveryId,
1216
+ sessionKey,
1217
+ source,
1218
+ });
1219
+
1220
+ if (fallbackResult.status >= 200 && fallbackResult.status < 300) {
1221
+ return {
1222
+ ok: true,
1223
+ envelope: fallbackResult.envelope,
1224
+ ack: {
1225
+ event: 'delivery.accepted',
1226
+ data: fallbackResult.body,
1227
+ },
1228
+ transport: 'http',
1229
+ fallbackUsed: true,
1230
+ };
1231
+ }
1232
+
1233
+ throw buildReplyFallbackError({
1234
+ deliveryId: fallbackResult.envelope.deliveryId,
1235
+ status: fallbackResult.status,
1236
+ body: fallbackResult.body,
1237
+ context: this.buildBoundaryContext({
1238
+ stage: 'delivery_accept_fallback',
1239
+ deliveryId: fallbackResult.envelope.deliveryId,
1240
+ sessionKey: normalizeOptionalText(sessionKey) || null,
1241
+ fallbackFrom: error?.code || error?.message || null,
1242
+ }),
1243
+ });
1244
+ }
1245
+
1100
1246
  async sendReplyAndWaitForAck({
1101
1247
  deliveryId,
1102
1248
  sessionKey,
@@ -1190,15 +1336,40 @@ export class ClaworldRelayClient extends EventEmitter {
1190
1336
  timeoutMs = DEFAULT_REPLY_ACK_TIMEOUT_MS,
1191
1337
  httpFallback = true,
1192
1338
  } = {}) {
1339
+ if (httpFallback && (!this.ws || this.ws.readyState !== 1)) {
1340
+ return await this.submitAcceptedHttpFallback({
1341
+ deliveryId,
1342
+ sessionKey,
1343
+ source,
1344
+ error: this.buildWsNotConnectedError('delivery_accept_send'),
1345
+ });
1346
+ }
1347
+
1348
+ const ackAbortController = new AbortController();
1193
1349
  const ackPromise = this.waitForAcceptedAck({
1194
1350
  deliveryId,
1195
1351
  timeoutMs,
1352
+ signal: ackAbortController.signal,
1196
1353
  });
1197
- const envelope = this.sendAccepted({
1198
- deliveryId,
1199
- sessionKey,
1200
- source,
1201
- });
1354
+ let envelope;
1355
+
1356
+ try {
1357
+ envelope = this.sendAccepted({
1358
+ deliveryId,
1359
+ sessionKey,
1360
+ source,
1361
+ });
1362
+ } catch (error) {
1363
+ ackAbortController.abort();
1364
+ void ackPromise.catch(() => {});
1365
+ if (!httpFallback) throw error;
1366
+ return await this.submitAcceptedHttpFallback({
1367
+ deliveryId,
1368
+ sessionKey,
1369
+ source,
1370
+ error,
1371
+ });
1372
+ }
1202
1373
 
1203
1374
  try {
1204
1375
  const ack = await ackPromise;
@@ -1212,43 +1383,11 @@ export class ClaworldRelayClient extends EventEmitter {
1212
1383
  } catch (error) {
1213
1384
  if (!httpFallback) throw error;
1214
1385
 
1215
- this.logger.warn?.('[claworld:relay-client] delivery acceptance websocket acknowledgement failed; attempting HTTP fallback', {
1216
- accountId: this.runtimeConfig?.accountId || null,
1217
- agentId: this.boundAgentId,
1218
- deliveryId: envelope.deliveryId,
1219
- sessionKey: envelope.sessionKey,
1220
- error: error?.message || String(error),
1221
- });
1222
-
1223
- const fallbackResult = await this.acceptDeliveryHttp({
1386
+ return await this.submitAcceptedHttpFallback({
1224
1387
  deliveryId: envelope.deliveryId,
1225
1388
  sessionKey: envelope.sessionKey,
1226
1389
  source: envelope.source,
1227
- });
1228
-
1229
- if (fallbackResult.status >= 200 && fallbackResult.status < 300) {
1230
- return {
1231
- ok: true,
1232
- envelope,
1233
- ack: {
1234
- event: 'delivery.accepted',
1235
- data: fallbackResult.body,
1236
- },
1237
- transport: 'http',
1238
- fallbackUsed: true,
1239
- };
1240
- }
1241
-
1242
- throw buildReplyFallbackError({
1243
- deliveryId: envelope.deliveryId,
1244
- status: fallbackResult.status,
1245
- body: fallbackResult.body,
1246
- context: this.buildBoundaryContext({
1247
- stage: 'delivery_accept_fallback',
1248
- deliveryId: envelope.deliveryId,
1249
- sessionKey: envelope.sessionKey,
1250
- fallbackFrom: error?.code || error?.message || null,
1251
- }),
1390
+ error,
1252
1391
  });
1253
1392
  }
1254
1393
  }
@@ -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',