From 6346ea2748ecdd1100827204683b965f9ec7bd50 Mon Sep 17 00:00:00 2001 From: Tao Date: Thu, 26 Feb 2026 22:04:28 +0000 Subject: [PATCH] fix: main agent always root in agent tree + accountId inference (v0.6.6) main agent in OpenClaw has no explicit binding - it's the default catch-all. Previous code required explicit binding with accountId && !peer to detect root agents, causing main and its sub-agents to appear as orphan independents. Now main is always treated as root, with accountId inferred from first unclaimed telegram account in channels.telegram.accounts. Co-Authored-By: Claude Opus 4.6 --- DEVLOG.md | 18 ++++++++++++++++-- openclaw-manager.js | 19 ++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/DEVLOG.md b/DEVLOG.md index ff17165..27a2e47 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -1,7 +1,21 @@ # OpenClaw Manager — 开发日志 -> 最后更新:2026-02-26 -> 当前版本:v0.6.5 +> 最后更新:2026-02-27 +> 当前版本:v0.6.6 + +--- + +## v0.6.6 更新日志(2026-02-27) + +### Bug Fixes + +**Agent tree: main agent not detected as root** +- **Symptom**: Sub-agents displayed as independent agents instead of under their parent. All agents appeared as orphan roots. +- **Root cause**: `main` agent in OpenClaw is the default catch-all and typically has NO explicit binding in `openclaw.json`. The `hasOwnBot` detection required an explicit binding with `accountId && !peer`, so `main` was classified as a non-root. Without `main` being a root, sub-agents' `parentAccountId` had no matching root to link to. +- **Fix**: + - `main` is now always treated as a root agent regardless of binding existence + - `main`'s `accountId` is inferred from `channels.telegram.accounts`: first unclaimed account (not explicitly bound to another agent), or first account as fallback + - Sub-agents with `parentAccountId` matching `main`'s inferred `accountId` now correctly appear under `main` in the tree --- diff --git a/openclaw-manager.js b/openclaw-manager.js index 4d9000f..3383b77 100644 --- a/openclaw-manager.js +++ b/openclaw-manager.js @@ -24,7 +24,7 @@ const SCRIPT_DIR = __dirname; const MANAGER_CONFIG = path.join(SCRIPT_DIR, 'manager-config.json'); let PORT = 3333; let HOST = '0.0.0.0'; -const APP_VERSION = '0.6.5'; +const APP_VERSION = '0.6.6'; // --port 参数 const portIdx = process.argv.indexOf('--port'); if (portIdx !== -1 && process.argv[portIdx + 1]) PORT = parseInt(process.argv[portIdx + 1]) || 3333; @@ -346,19 +346,28 @@ async function handleApi(req, res, urlObj, body) { const bindings = cfg.bindings || []; const groups = cfg.channels?.telegram?.groups || {}; const defaultWorkspace = defaults.workspace || null; + // Build set of accountIds explicitly claimed by non-main agents (via non-peer binding) + const telegramAccounts = Object.keys(cfg.channels?.telegram?.accounts || {}); + const claimedAccounts = new Set( + bindings.filter(b => b.agentId !== 'main' && b.match?.accountId && !b.match?.peer).map(b => b.match.accountId) + ); + // For main agent without explicit binding, infer its accountId from first unclaimed telegram account + const mainInferredAccountId = telegramAccounts.find(a => !claimedAccounts.has(a)) || telegramAccounts[0] || null; const enriched = list.map(a => { const binding = bindings.find(b => b.agentId === a.id && b.match?.peer?.kind === 'group'); const groupId = binding?.match?.peer?.id || null; const modelVal = a.model?.primary || (typeof a.model === 'string' ? a.model : null); - // Check if agent has its own bot account (binding with accountId but no peer, or accountId matching agent ID) + // Check if agent has its own bot account (binding with accountId but no peer) const botBinding = bindings.find(b => b.agentId === a.id && b.match?.accountId && !b.match?.peer); - const hasOwnBot = botBinding ? true : false; - const accountId = botBinding?.match?.accountId || null; + // 'main' is always a root agent even without explicit binding + const isMain = a.id === 'main'; + const hasOwnBot = botBinding ? true : isMain; + const accountId = botBinding?.match?.accountId || (isMain ? mainInferredAccountId : null); // For sub-agents (with peer match), find which accountId they belong to const parentBinding = bindings.find(b => b.agentId === a.id && b.match?.accountId); const parentAccountId = parentBinding?.match?.accountId || null; // Workspace: explicit per-agent, or defaults.workspace for main - const workspace = a.workspace || (a.id === 'main' ? defaultWorkspace : null); + const workspace = a.workspace || (isMain ? defaultWorkspace : null); return { ...a, workspace, groupId, requireMention: groupId ? (groups[groupId]?.requireMention ?? true) : null, effectiveModel: modelVal || defaults.model?.primary || '默认', hasOwnBot, accountId, parentAccountId }; });