updated subagent logic

This commit is contained in:
2026-02-27 19:53:12 +10:00
parent 154b863f78
commit cedade141b
2 changed files with 22 additions and 1 deletions
+8
View File
@@ -30,11 +30,19 @@
- All Chinese text in the initial directory selection page translated to English - All Chinese text in the initial directory selection page translated to English
- Includes: title, description, labels, placeholders, error messages, button text - Includes: title, description, labels, placeholders, error messages, button text
### Bug Fixes
**Agent tree: sub-agents not grouped under main (old config format)**
- **Symptom**: On machines with openclaw.json created before OCM v0.6 multi-bot support, all sub-agents displayed as independent cards instead of nested under main
- **Root cause**: Old-format bindings lack `accountId` field in sub-agent entries. The tree builder relied on `parentAccountId` to match sub-agents to roots, but old bindings only had `channel` + `peer` — no `accountId`. So `parentAccountId` was null and no parent match was found
- **Fix**: Fallback inference — if a sub-agent has a peer binding but no `accountId`, automatically infer `parentAccountId` from main's binding (`accountId`) or fall back to `'default'`
### Technical Notes ### Technical Notes
- **CPU usage measurement:** Two `os.cpus()` snapshots 200ms apart, calculating idle-to-total ratio across all cores - **CPU usage measurement:** Two `os.cpus()` snapshots 200ms apart, calculating idle-to-total ratio across all cores
- **Gauge rendering:** Pure SVG arcs with `stroke-dasharray` animation, no external libraries. 270° arc (gap at bottom), colour transitions via `gaugeColor()` function - **Gauge rendering:** Pure SVG arcs with `stroke-dasharray` animation, no external libraries. 270° arc (gap at bottom), colour transitions via `gaugeColor()` function
- **allowFrom auto-config:** `POST /api/agents` now accepts optional `telegramUserId` field (numeric string), appends to `channels.telegram.allowFrom[]` if not already present - **allowFrom auto-config:** `POST /api/agents` now accepts optional `telegramUserId` field (numeric string), appends to `channels.telegram.allowFrom[]` if not already present
- **Old-config compatibility:** Sub-agent bindings without `accountId` are now auto-assigned to the main agent's bot, maintaining backward compatibility with configs created before multi-bot support
--- ---
+14 -1
View File
@@ -365,7 +365,20 @@ async function handleApi(req, res, urlObj, body) {
const accountId = botBinding?.match?.accountId || (isMain ? mainInferredAccountId : null); const accountId = botBinding?.match?.accountId || (isMain ? mainInferredAccountId : null);
// For sub-agents (with peer match), find which accountId they belong to // 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 parentBinding = bindings.find(b => b.agentId === a.id && b.match?.accountId);
const parentAccountId = parentBinding?.match?.accountId || null; // Fallback: if sub-agent has peer binding but no accountId, AND there's only one bot (old single-bot config),
// infer parentAccountId from main. If multiple bots exist, leave as orphan (can't guess which bot it belongs to).
let parentAccountId = parentBinding?.match?.accountId || null;
if (!parentAccountId && !hasOwnBot) {
const hasPeerBinding = bindings.find(b => b.agentId === a.id && b.match?.peer);
if (hasPeerBinding) {
const rootBindings = bindings.filter(b => b.match?.accountId && !b.match?.peer);
const rootCount = rootBindings.length + (list.some(x => x.id === 'main') && !rootBindings.some(b => b.agentId === 'main') ? 1 : 0);
if (rootCount <= 1) {
const mainBinding = rootBindings.find(b => b.agentId === 'main') || rootBindings[0];
parentAccountId = mainBinding?.match?.accountId || mainInferredAccountId || 'default';
}
}
}
// Workspace: explicit per-agent, or defaults.workspace for main // Workspace: explicit per-agent, or defaults.workspace for main
const workspace = a.workspace || (isMain ? defaultWorkspace : null); const workspace = a.workspace || (isMain ? defaultWorkspace : null);
return { ...a, workspace, groupId, requireMention: groupId ? (groups[groupId]?.requireMention ?? true) : null, return { ...a, workspace, groupId, requireMention: groupId ? (groups[groupId]?.requireMention ?? true) : null,