@xfxstudio/claworld 2026.4.30-runtime-binding-fix.3 → 2026.4.30-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
@@ -4,7 +4,7 @@ Claworld channel plugin for OpenClaw.
4
4
 
5
5
  ## Host-Native Setup
6
6
 
7
- Install the published plugin package from npm:
7
+ Install the published plugin package:
8
8
 
9
9
  ```bash
10
10
  openclaw plugins install @xfxstudio/claworld
@@ -17,12 +17,6 @@ Then configure one Claworld channel account through the host:
17
17
  openclaw channels add --channel claworld
18
18
  ```
19
19
 
20
- For a non-default backend, pass the backend explicitly:
21
-
22
- ```bash
23
- openclaw channels add --channel claworld --account claworld --http-url <backend-url>
24
- ```
25
-
26
20
  Alternative first-run path:
27
21
 
28
22
  ```bash
@@ -31,8 +25,6 @@ openclaw onboard
31
25
 
32
26
  The setup flow only writes plugin-side config and binding.
33
27
  It does not require backend activation and it does not run an installer CLI.
34
- It also does not require a local source checkout. Do not look for the Claworld
35
- repo unless you are intentionally developing the plugin itself.
36
28
 
37
29
  ## First-Use Activation
38
30
 
@@ -63,3 +55,15 @@ Also re-run:
63
55
 
64
56
  - `claworld_manage_account(action=activate_account)` when public identity is still pending
65
57
  - `claworld_manage_account(action=view_account)` when binding/readiness still looks unhealthy after setup or initialization
58
+
59
+ ## Local Development
60
+
61
+ For a repo checkout, materialize the package root first:
62
+
63
+ ```bash
64
+ npm run build:plugin:package
65
+ openclaw plugins install /absolute/path/to/packages/openclaw-plugin
66
+ ```
67
+
68
+ If you change plugin code, rebuild the package root before reinstalling or
69
+ retesting it in a real host.
@@ -8,7 +8,7 @@
8
8
  ],
9
9
  "name": "Claworld Persona Relay",
10
10
  "description": "Claworld relay world channel plugin for OpenClaw.",
11
- "version": "2026.4.30-runtime-binding-fix.3",
11
+ "version": "2026.4.30-testing.1",
12
12
  "configSchema": {
13
13
  "type": "object",
14
14
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xfxstudio/claworld",
3
- "version": "2026.4.30-runtime-binding-fix.3",
3
+ "version": "2026.4.30-testing.1",
4
4
  "description": "Claworld channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -47,14 +47,6 @@ openclaw channels add --channel claworld --account claworld
47
47
  openclaw agents bind --agent main --bind claworld:claworld
48
48
  ```
49
49
 
50
- 如果测试环境要求连到非默认 backend,`channels add` 必须带上:
51
-
52
- ```bash
53
- openclaw channels add --channel claworld --account claworld --http-url <backend-url>
54
- ```
55
-
56
- 安装发布包不需要本地源码目录;不要去找 Claworld repo、不要跑 `npm run build:plugin:package`,除非用户明确说是在开发插件源码。
57
-
58
50
  ### 升级已安装插件
59
51
 
60
52
  已安装时,先看版本,再 update;不要用 install 覆盖现有目录。
@@ -1,6 +1,8 @@
1
1
  import { createKickoffBrief } from './relay/kickoff-text.js';
2
2
  import { normalizeAcceptedChatKickoffRecord } from './relay/kickoff-progress.js';
3
3
 
4
+ export const DEFAULT_CHAT_REQUEST_TTL_MS = 30 * 24 * 60 * 60 * 1000;
5
+
4
6
  function normalizeText(value, fallback = null) {
5
7
  if (value == null) return fallback;
6
8
  const normalized = String(value).trim();
@@ -2479,75 +2479,6 @@ export function createClaworldChannelPlugin({
2479
2479
  });
2480
2480
  }
2481
2481
 
2482
- function resolveCachedAccountBinding({ runtimeConfig, accountId = null }) {
2483
- const normalizedRuntimeConfig = applyRuntimeIdentity(runtimeConfig);
2484
- const accountKey = resolveAccountBindingKey(normalizedRuntimeConfig, accountId || null);
2485
- const cachedBinding = accountBindingStates.get(accountKey)?.binding || null;
2486
- if (
2487
- !cachedBinding
2488
- || !cachedBinding.runtimeConfig?.serverUrl
2489
- || cachedBinding.runtimeConfig.serverUrl !== normalizedRuntimeConfig.serverUrl
2490
- ) {
2491
- return null;
2492
- }
2493
-
2494
- return {
2495
- ...cachedBinding,
2496
- runtimeConfig: mergeBoundRuntimeConfig(normalizedRuntimeConfig, cachedBinding.runtimeConfig),
2497
- bindingSource: cachedBinding.bindingSource || 'binding_cache',
2498
- };
2499
- }
2500
-
2501
- function commitRuntimeBinding({
2502
- context = {},
2503
- runtime = null,
2504
- runtimeSource = null,
2505
- accountId = null,
2506
- runtimeConfig = {},
2507
- bindingSource = 'configured_app_token',
2508
- relayAgent = null,
2509
- } = {}) {
2510
- const normalizedRuntimeConfig = applyRuntimeIdentity(runtimeConfig);
2511
- const normalizedAccountId = normalizeClaworldText(
2512
- normalizedRuntimeConfig.accountId,
2513
- normalizeClaworldText(accountId, normalizeClaworldText(context.accountId, 'default')),
2514
- );
2515
- const accountKey = resolveAccountBindingKey(normalizedRuntimeConfig, normalizedAccountId);
2516
- const runtimeResolution = runtimeSource
2517
- ? { runtime, runtimeSource }
2518
- : resolvePluginRuntimeCandidate(runtime || context.runtime || null);
2519
- const currentRuntimeContext = accountRuntimeContexts.get(accountKey)
2520
- || accountRuntimeContexts.get(normalizedAccountId)
2521
- || null;
2522
-
2523
- rememberAccountBinding({
2524
- runtimeConfig: normalizedRuntimeConfig,
2525
- accountId: normalizedAccountId,
2526
- bindingSource,
2527
- relayAgent,
2528
- });
2529
-
2530
- accountRuntimeContexts.set(accountKey, {
2531
- ...(currentRuntimeContext || {}),
2532
- runtime: currentRuntimeContext?.runtime || runtimeResolution.runtime || null,
2533
- runtimeSource: currentRuntimeContext?.runtimeSource || runtimeResolution.runtimeSource || null,
2534
- cfg: currentRuntimeContext?.cfg || context.cfg || null,
2535
- accountId: normalizedAccountId,
2536
- runtimeConfig: normalizedRuntimeConfig,
2537
- relayAgent: relayAgent || currentRuntimeContext?.relayAgent || null,
2538
- bindingSource,
2539
- deferredFailure: null,
2540
- deferredErrorMessage: null,
2541
- });
2542
-
2543
- return {
2544
- accountKey,
2545
- accountId: normalizedAccountId,
2546
- runtimeConfig: normalizedRuntimeConfig,
2547
- bindingSource,
2548
- };
2549
- }
2550
-
2551
2482
  async function ensureAccountRelayBinding({ runtimeConfig, accountId = null }) {
2552
2483
  const normalizedRuntimeConfig = applyRuntimeIdentity(runtimeConfig);
2553
2484
  const accountKey = resolveAccountBindingKey(normalizedRuntimeConfig, accountId || null);
@@ -2562,7 +2493,9 @@ export function createClaworldChannelPlugin({
2562
2493
  return {
2563
2494
  ...cachedBinding,
2564
2495
  runtimeConfig: mergeBoundRuntimeConfig(normalizedRuntimeConfig, cachedBinding.runtimeConfig),
2565
- bindingSource: cachedBinding.bindingSource || 'binding_cache',
2496
+ bindingSource: cachedBinding.bindingSource === 'configured_app_token'
2497
+ ? 'configured_app_token'
2498
+ : 'binding_cache',
2566
2499
  };
2567
2500
  }
2568
2501
 
@@ -2683,13 +2616,8 @@ export function createClaworldChannelPlugin({
2683
2616
  };
2684
2617
  }
2685
2618
 
2686
- const backupAgentId = normalizeClaworldText(
2687
- backup?.agentId,
2688
- normalizeClaworldText(runtimeConfig?.relay?.agentId, null),
2689
- );
2690
2619
  const restoredRuntimeConfig = applyRuntimeIdentity(runtimeConfig, {
2691
2620
  appToken: backupToken,
2692
- agentId: backupAgentId,
2693
2621
  });
2694
2622
 
2695
2623
  try {
@@ -2697,7 +2625,6 @@ export function createClaworldChannelPlugin({
2697
2625
  runtime,
2698
2626
  accountId,
2699
2627
  appToken: backupToken,
2700
- relayAgentId: backupAgentId,
2701
2628
  });
2702
2629
  } catch (error) {
2703
2630
  logger.warn?.(`[claworld:${accountId || 'default'}] failed to persist restored runtime appToken`, {
@@ -2725,89 +2652,16 @@ export function createClaworldChannelPlugin({
2725
2652
  accountId: runtimeConfig.accountId || accountId || null,
2726
2653
  runtimeConfig,
2727
2654
  agentId: context.agentId || runtimeConfig.relay?.agentId || null,
2728
- bindingSource: runtimeContext?.deferredFailure
2729
- ? 'runtime_context_deferred'
2730
- : (runtimeContext?.bindingSource || 'runtime_context'),
2731
- };
2732
- }
2733
-
2734
- async function resolveAccountViewRuntimeContext(context = {}) {
2735
- const configuredContext = resolveConfiguredRuntimeContext(context);
2736
- const cfg = configuredContext.cfg || {};
2737
- const accountId = configuredContext.accountId || null;
2738
- let runtimeConfig = configuredContext.runtimeConfig;
2739
- const runtimeResolution = resolvePluginRuntimeCandidate(
2740
- context.runtime || configuredContext.runtime || null,
2741
- );
2742
- let bindingSource = configuredContext.bindingSource || null;
2743
- let relayAgent = configuredContext.relayAgent || null;
2744
-
2745
- const runtimeContext = accountRuntimeContexts.get(
2746
- resolveAccountBindingKey(runtimeConfig, accountId || null),
2747
- ) || accountRuntimeContexts.get(accountId || 'default') || null;
2748
- if (runtimeContext?.runtimeConfig && !runtimeContext?.deferredFailure) {
2749
- runtimeConfig = mergeBoundRuntimeConfig(runtimeConfig, runtimeContext.runtimeConfig);
2750
- bindingSource = runtimeContext.bindingSource || bindingSource || 'runtime_context';
2751
- relayAgent = runtimeContext.relayAgent || relayAgent;
2752
- }
2753
-
2754
- const cachedBinding = resolveCachedAccountBinding({ runtimeConfig, accountId });
2755
- if (cachedBinding && resolveRuntimeAppToken(cachedBinding.runtimeConfig)) {
2756
- runtimeConfig = cachedBinding.runtimeConfig;
2757
- bindingSource = cachedBinding.bindingSource || bindingSource || 'binding_cache';
2758
- relayAgent = cachedBinding.relayAgent || relayAgent;
2759
- }
2760
-
2761
- if (!resolveRuntimeAppToken(runtimeConfig)) {
2762
- const restoredBinding = await maybeRestoreRuntimeAppToken({
2763
- runtime: runtimeResolution.runtime,
2764
- accountId,
2765
- runtimeConfig,
2766
- });
2767
- if (restoredBinding.restored) {
2768
- runtimeConfig = restoredBinding.runtimeConfig;
2769
- bindingSource = 'installer_state_backup';
2770
- logger.info?.(`[claworld:${accountId || 'default'}] restored runtime binding from installer state`, {
2771
- installerStatePath: restoredBinding.installerStatePath || null,
2772
- relayAgentId: runtimeConfig?.relay?.agentId || null,
2773
- });
2774
- commitRuntimeBinding({
2775
- context: configuredContext,
2776
- runtime: runtimeResolution.runtime,
2777
- runtimeSource: runtimeResolution.runtimeSource,
2778
- accountId,
2779
- runtimeConfig,
2780
- bindingSource,
2781
- relayAgent,
2782
- });
2783
- }
2784
- }
2785
-
2786
- if (resolveRuntimeAppToken(runtimeConfig) && (!bindingSource || bindingSource === 'runtime_context')) {
2787
- bindingSource = 'configured_app_token';
2788
- }
2789
-
2790
- return {
2791
- ...configuredContext,
2792
- cfg: runtimeContext?.cfg || cfg,
2793
- accountId: runtimeConfig.accountId || accountId || null,
2794
- runtimeConfig,
2795
- runtime: runtimeResolution.runtime,
2796
- runtimeSource: runtimeResolution.runtimeSource,
2797
- agentId: configuredContext.agentId || runtimeConfig.relay?.agentId || null,
2798
- bindingSource,
2799
- relayAgent,
2655
+ bindingSource: runtimeContext?.deferredFailure ? 'runtime_context_deferred' : 'runtime_context',
2800
2656
  };
2801
2657
  }
2802
2658
 
2803
2659
  async function resolveBoundRuntimeContext(context = {}) {
2804
- const configuredContext = await resolveAccountViewRuntimeContext(context);
2660
+ const configuredContext = resolveConfiguredRuntimeContext(context);
2805
2661
  const cfg = configuredContext.cfg || {};
2806
2662
  const accountId = configuredContext.accountId || null;
2807
2663
  let runtimeConfig = configuredContext.runtimeConfig;
2808
- const runtimeResolution = resolvePluginRuntimeCandidate(
2809
- context.runtime || configuredContext.runtime || null,
2810
- );
2664
+ const runtimeResolution = resolvePluginRuntimeCandidate(context.runtime || null);
2811
2665
  const restoredBinding = await maybeRestoreRuntimeAppToken({
2812
2666
  runtime: runtimeResolution.runtime,
2813
2667
  accountId,
@@ -2830,7 +2684,7 @@ export function createClaworldChannelPlugin({
2830
2684
  runtime: runtimeResolution.runtime,
2831
2685
  runtimeSource: runtimeResolution.runtimeSource,
2832
2686
  agentId: configuredContext.agentId || runtimeConfig.relay?.agentId || null,
2833
- bindingSource: configuredContext.bindingSource || runtimeContext.bindingSource || 'runtime_context',
2687
+ bindingSource: 'runtime_context',
2834
2688
  };
2835
2689
  }
2836
2690
  const binding = await ensureAccountRelayBinding({ runtimeConfig, accountId });
@@ -3167,7 +3021,7 @@ export function createClaworldChannelPlugin({
3167
3021
  }
3168
3022
 
3169
3023
  async function getRuntimePublicIdentity(context = {}) {
3170
- const resolvedContext = await resolveAccountViewRuntimeContext(context);
3024
+ const resolvedContext = resolveConfiguredRuntimeContext(context);
3171
3025
  return fetchPublicIdentity({
3172
3026
  runtimeConfig: resolvedContext.runtimeConfig,
3173
3027
  agentId: resolvedContext.agentId || null,
@@ -3178,10 +3032,7 @@ export function createClaworldChannelPlugin({
3178
3032
  }
3179
3033
 
3180
3034
  async function updateRuntimePublicIdentity(context = {}) {
3181
- const resolvedContext = await resolveAccountViewRuntimeContext(context);
3182
- const runtimeResolution = resolvePluginRuntimeCandidate(
3183
- context.runtime || resolvedContext.runtime || null,
3184
- );
3035
+ const resolvedContext = resolveConfiguredRuntimeContext(context);
3185
3036
  const updateResult = await updatePublicIdentity({
3186
3037
  runtimeConfig: resolvedContext.runtimeConfig,
3187
3038
  agentId: resolvedContext.agentId || null,
@@ -3222,6 +3073,7 @@ async function updateRuntimePublicIdentity(context = {}) {
3222
3073
  );
3223
3074
 
3224
3075
  if (shouldPersistRuntimeBinding) {
3076
+ const runtimeResolution = resolvePluginRuntimeCandidate(context.runtime || null);
3225
3077
  try {
3226
3078
  await persistRuntimeAppToken({
3227
3079
  runtime: runtimeResolution.runtime,
@@ -3235,19 +3087,23 @@ async function updateRuntimePublicIdentity(context = {}) {
3235
3087
  error: error?.message || String(error),
3236
3088
  });
3237
3089
  }
3238
- }
3239
3090
 
3240
- if (resolveRuntimeAppToken(boundRuntimeConfig) && nextAgentId) {
3241
- commitRuntimeBinding({
3242
- context: resolvedContext,
3243
- runtime: runtimeResolution.runtime,
3244
- runtimeSource: runtimeResolution.runtimeSource,
3091
+ rememberAccountBinding({
3245
3092
  runtimeConfig: boundRuntimeConfig,
3246
3093
  accountId: resolvedContext.accountId || boundRuntimeConfig.accountId || null,
3247
- bindingSource: runtimeActivation
3248
- ? 'activated_app_token'
3249
- : (resolvedContext.bindingSource || 'configured_app_token'),
3094
+ bindingSource: runtimeActivation ? 'activated_app_token' : 'configured_app_token',
3250
3095
  });
3096
+
3097
+ const accountKey = resolveAccountBindingKey(boundRuntimeConfig, resolvedContext.accountId || null);
3098
+ const currentRuntimeContext = accountRuntimeContexts.get(accountKey) || null;
3099
+ if (currentRuntimeContext) {
3100
+ accountRuntimeContexts.set(accountKey, {
3101
+ ...currentRuntimeContext,
3102
+ runtimeConfig: boundRuntimeConfig,
3103
+ deferredFailure: null,
3104
+ deferredErrorMessage: null,
3105
+ });
3106
+ }
3251
3107
  }
3252
3108
 
3253
3109
  const payload = updateResult && typeof updateResult === 'object' && !Array.isArray(updateResult)
@@ -3433,7 +3289,6 @@ async function generateRuntimeProfileCard(context = {}) {
3433
3289
  },
3434
3290
  helpers: {
3435
3291
  resolveToolRuntimeContext: resolveBoundRuntimeContext,
3436
- resolveToolAccountViewContext: resolveAccountViewRuntimeContext,
3437
3292
  pairing: {
3438
3293
  resolveAgentIdentity: async (context = {}) => resolveRelayAgentSummary({
3439
3294
  runtimeConfig: context.runtimeConfig || resolveClaworldRuntimeConfig(context.cfg || {}, context.accountId || null),
@@ -278,7 +278,6 @@ export async function resolveToolContext(
278
278
  }
279
279
  return {
280
280
  cfg,
281
- runtime: api?.runtime || null,
282
281
  accountId,
283
282
  runtimeConfig,
284
283
  agentId,
@@ -654,7 +653,19 @@ export function projectToolAccountViewResponse({
654
653
  identityPayload = null,
655
654
  } = {}) {
656
655
  const identityReady = identityPayload?.ready === true;
657
- const ready = pairingPayload?.status === 'paired' && identityReady;
656
+ const activationReady = pairingPayload?.status === 'paired';
657
+ const bindingReady = typeof pairingPayload?.bindingReady === 'boolean'
658
+ ? pairingPayload.bindingReady
659
+ : activationReady;
660
+ const bindingStatus = normalizeText(
661
+ pairingPayload?.bindingStatus,
662
+ activationReady
663
+ ? (bindingReady ? 'bound' : 'identity_unresolved')
664
+ : 'unactivated',
665
+ );
666
+ const ready = activationReady && identityReady;
667
+ const relayResolved = pairingPayload?.relayAgent?.resolved ?? null;
668
+ const relayOnline = pairingPayload?.relayAgent?.online ?? null;
658
669
  const resolvedShareCard = identityPayload && Object.prototype.hasOwnProperty.call(identityPayload, 'shareCard')
659
670
  ? projectToolShareCard(identityPayload.shareCard)
660
671
  : undefined;
@@ -669,7 +680,15 @@ export function projectToolAccountViewResponse({
669
680
  reason: normalizeText(pairingPayload?.reason, null),
670
681
  bindingSource: normalizeText(pairingPayload?.bindingSource, null),
671
682
  activation: {
672
- status: pairingPayload?.status === 'paired' ? 'ready' : 'pending',
683
+ status: activationReady ? 'ready' : 'pending',
684
+ },
685
+ diagnostics: {
686
+ toolReachable: true,
687
+ bindingReady,
688
+ bindingStatus,
689
+ publicIdentityReady: identityReady,
690
+ relayPresenceResolved: relayResolved,
691
+ relayOnline,
673
692
  },
674
693
  relay: {
675
694
  agentId: normalizeText(
@@ -679,9 +698,9 @@ export function projectToolAccountViewResponse({
679
698
  displayName: normalizeText(pairingPayload?.relayAgent?.displayName, null),
680
699
  discoverable: pairingPayload?.relayAgent?.discoverable ?? null,
681
700
  contactable: pairingPayload?.relayAgent?.contactable ?? null,
682
- online: pairingPayload?.relayAgent?.online ?? null,
683
- resolved: pairingPayload?.relayAgent?.resolved ?? null,
684
- bindingStatus: pairingPayload?.status === 'paired' ? 'bound' : 'unactivated',
701
+ online: relayOnline,
702
+ resolved: relayResolved,
703
+ bindingStatus,
685
704
  },
686
705
  profile: projectToolAccountProfile(identityPayload),
687
706
  ...projectToolAccountIdentityFields(identityPayload),
@@ -673,18 +673,7 @@ function createTerminalToolAdapters(api, plugin, internalTools) {
673
673
  ...context,
674
674
  agentId: normalizeText(params.agentId, context.agentId),
675
675
  });
676
- const responsePayload = action === 'get_profile'
677
- && params.generateShareCard === true
678
- && typeof plugin.runtime.productShell.profile?.generateShareCard === 'function'
679
- ? {
680
- ...payload,
681
- shareCard: await plugin.runtime.productShell.profile.generateShareCard({
682
- ...context,
683
- expiresInSeconds: params.expiresInSeconds ?? null,
684
- }),
685
- }
686
- : payload;
687
- return buildTerminalActionResult({ tool: publicProfileTool, action, payload: responsePayload });
676
+ return buildTerminalActionResult({ tool: publicProfileTool, action, payload });
688
677
  },
689
678
  },
690
679
  {
@@ -1969,34 +1958,26 @@ function buildRegisteredTools(api, plugin) {
1969
1958
 
1970
1959
  const cfg = await loadCurrentConfig(api);
1971
1960
  const accountId = normalizeText(params.accountId, plugin.config.defaultAccountId(cfg) || null);
1972
- const rawRuntimeConfig = plugin.config.resolveRuntimeConfig(cfg, accountId);
1973
- const viewContext = typeof plugin.helpers?.resolveToolAccountViewContext === 'function'
1974
- ? await plugin.helpers.resolveToolAccountViewContext({
1975
- cfg,
1976
- runtime: api?.runtime || null,
1977
- accountId,
1978
- runtimeConfig: rawRuntimeConfig,
1979
- agentId: normalizeText(params.agentId, rawRuntimeConfig.relay?.agentId || null),
1980
- })
1981
- : {
1982
- cfg,
1983
- runtime: api?.runtime || null,
1984
- accountId,
1985
- runtimeConfig: rawRuntimeConfig,
1986
- agentId: normalizeText(params.agentId, rawRuntimeConfig.relay?.agentId || null),
1987
- bindingSource: null,
1988
- };
1989
- const runtimeConfig = viewContext.runtimeConfig || rawRuntimeConfig;
1961
+ const runtimeConfig = plugin.config.resolveRuntimeConfig(cfg, accountId);
1990
1962
  const identityPayload = await plugin.runtime.productShell.profile.getPublicIdentity({
1991
- ...viewContext,
1992
- runtimeConfig,
1963
+ cfg,
1993
1964
  accountId,
1994
- agentId: normalizeText(viewContext.agentId, runtimeConfig.relay?.agentId || null),
1965
+ runtimeConfig,
1966
+ agentId: runtimeConfig.relay?.agentId || null,
1995
1967
  generateShareCard,
1996
1968
  expiresInSeconds: params.expiresInSeconds ?? null,
1997
1969
  });
1998
1970
  const pairedAgentId = identityPayload?.agentId || runtimeConfig.relay?.agentId || null;
1999
- const relayAgent = pairedAgentId
1971
+ const pairedRuntimeConfig = pairedAgentId
1972
+ ? {
1973
+ ...runtimeConfig,
1974
+ relay: {
1975
+ ...(runtimeConfig.relay && typeof runtimeConfig.relay === 'object' ? runtimeConfig.relay : {}),
1976
+ agentId: pairedAgentId,
1977
+ },
1978
+ }
1979
+ : runtimeConfig;
1980
+ const relayAgentFallback = pairedAgentId
2000
1981
  ? {
2001
1982
  agentId: pairedAgentId,
2002
1983
  displayName: normalizeText(
@@ -2017,23 +1998,39 @@ function buildRegisteredTools(api, plugin) {
2017
1998
  || runtimeConfig.relay?.appToken
2018
1999
  || runtimeConfig.relay?.credentialToken,
2019
2000
  );
2001
+ const activationReady = hasConfiguredAppToken;
2002
+ const bindingReady = activationReady && Boolean(pairedAgentId);
2003
+ const bindingStatus = activationReady
2004
+ ? (bindingReady ? 'bound' : 'identity_unresolved')
2005
+ : 'unactivated';
2006
+ let relayAgent = relayAgentFallback;
2007
+ if (hasConfiguredAppToken && pairedAgentId && typeof plugin.helpers?.pairing?.resolveAgentIdentity === 'function') {
2008
+ const resolvedRelayAgent = await plugin.helpers.pairing.resolveAgentIdentity({
2009
+ cfg,
2010
+ accountId,
2011
+ runtimeConfig: pairedRuntimeConfig,
2012
+ agentId: pairedAgentId,
2013
+ });
2014
+ if (resolvedRelayAgent && typeof resolvedRelayAgent === 'object') {
2015
+ relayAgent = {
2016
+ ...relayAgentFallback,
2017
+ ...resolvedRelayAgent,
2018
+ agentId: normalizeText(resolvedRelayAgent.agentId, pairedAgentId),
2019
+ displayName: normalizeText(resolvedRelayAgent.displayName, relayAgentFallback?.displayName ?? null),
2020
+ };
2021
+ }
2022
+ }
2020
2023
  const pairingPayload = {
2021
- status: hasConfiguredAppToken ? 'paired' : 'unpaired',
2022
- reason: hasConfiguredAppToken
2024
+ status: activationReady ? 'paired' : 'unpaired',
2025
+ bindingReady,
2026
+ bindingStatus,
2027
+ reason: activationReady
2023
2028
  ? (pairedAgentId ? null : 'missing_agent_id')
2024
2029
  : 'missing_app_token',
2025
- bindingSource: hasConfiguredAppToken
2026
- ? normalizeText(viewContext.bindingSource, 'configured_app_token')
2030
+ bindingSource: activationReady
2031
+ ? 'configured_app_token'
2027
2032
  : (runtimeConfig.registration?.enabled === true ? 'registration_pending' : 'unbound'),
2028
- runtimeConfig: pairedAgentId
2029
- ? {
2030
- ...runtimeConfig,
2031
- relay: {
2032
- ...(runtimeConfig.relay && typeof runtimeConfig.relay === 'object' ? runtimeConfig.relay : {}),
2033
- agentId: pairedAgentId,
2034
- },
2035
- }
2036
- : runtimeConfig,
2033
+ runtimeConfig: pairedRuntimeConfig,
2037
2034
  relayAgent,
2038
2035
  };
2039
2036
  return buildToolResult(projectToolAccountViewResponse({