From f841d6ede5ea5ec34d34d94a45ed62d4474a54f4 Mon Sep 17 00:00:00 2001 From: carlos4s Date: Sun, 10 May 2026 13:17:29 +0000 Subject: [PATCH] doctor: exempt the live compatibility agent dir from orphan-dir warnings --- src/commands/doctor-state-integrity.test.ts | 40 +++++++++++++++++++++ src/commands/doctor-state-integrity.ts | 14 +++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/commands/doctor-state-integrity.test.ts b/src/commands/doctor-state-integrity.test.ts index 59f65231760..25d65c2a399 100644 --- a/src/commands/doctor-state-integrity.test.ts +++ b/src/commands/doctor-state-integrity.test.ts @@ -33,6 +33,8 @@ type EnvSnapshot = { OPENCLAW_HOME?: string; OPENCLAW_STATE_DIR?: string; OPENCLAW_OAUTH_DIR?: string; + OPENCLAW_AGENT_DIR?: string; + PI_CODING_AGENT_DIR?: string; }; function captureEnv(): EnvSnapshot { @@ -41,6 +43,8 @@ function captureEnv(): EnvSnapshot { OPENCLAW_HOME: process.env.OPENCLAW_HOME, OPENCLAW_STATE_DIR: process.env.OPENCLAW_STATE_DIR, OPENCLAW_OAUTH_DIR: process.env.OPENCLAW_OAUTH_DIR, + OPENCLAW_AGENT_DIR: process.env.OPENCLAW_AGENT_DIR, + PI_CODING_AGENT_DIR: process.env.PI_CODING_AGENT_DIR, }; } @@ -156,6 +160,8 @@ describe("doctor state integrity oauth dir checks", () => { process.env.OPENCLAW_HOME = tempHome; process.env.OPENCLAW_STATE_DIR = path.join(tempHome, ".openclaw"); delete process.env.OPENCLAW_OAUTH_DIR; + delete process.env.OPENCLAW_AGENT_DIR; + delete process.env.PI_CODING_AGENT_DIR; fs.mkdirSync(process.env.OPENCLAW_STATE_DIR, { recursive: true, mode: 0o700 }); noteMock.mockClear(); }); @@ -249,6 +255,40 @@ describe("doctor state integrity oauth dir checks", () => { expect(text).not.toContain("Examples:"); }); + it("does not warn when the live compatibility main agent dir is missing from agents.list", async () => { + createAgentDir("main"); + + const text = await runStateIntegrityText({ + agents: { + list: [{ id: "jeremiah", default: true }], + }, + }); + + expect(text).not.toContain("without a matching agents.list entry"); + expect(text).not.toContain("Examples:"); + }); + + it("does not warn when OPENCLAW_AGENT_DIR points at the live compatibility agent dir", async () => { + createAgentDir("legacy"); + const legacyAgentDir = path.join( + process.env.OPENCLAW_STATE_DIR ?? "", + "agents", + "legacy", + "agent", + ); + process.env.OPENCLAW_AGENT_DIR = legacyAgentDir; + process.env.PI_CODING_AGENT_DIR = legacyAgentDir; + + const text = await runStateIntegrityText({ + agents: { + list: [{ id: "main", default: true }], + }, + }); + + expect(text).not.toContain("without a matching agents.list entry"); + expect(text).not.toContain("Examples:"); + }); + it("warns about tombstoned subagent restart recovery sessions", async () => { const cfg: OpenClawConfig = {}; writeSessionStore(cfg, { diff --git a/src/commands/doctor-state-integrity.ts b/src/commands/doctor-state-integrity.ts index 27f0ab3ce60..97f0a8cd787 100644 --- a/src/commands/doctor-state-integrity.ts +++ b/src/commands/doctor-state-integrity.ts @@ -25,6 +25,7 @@ import { updateSessionStore } from "../config/sessions/store.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { resolveRequiredHomeDir } from "../infra/home-dir.js"; import { resolveMemoryBackendConfig } from "../memory-host-sdk/engine-storage.js"; +import { resolveOpenClawAgentDir } from "../plugin-sdk/agent-dir-compat.js"; import { listConfiguredChannelIdsForReadOnlyScope } from "../plugins/channel-plugin-ids.js"; import { normalizeAgentId } from "../routing/session-key.js"; import { parseAgentSessionKey } from "../sessions/session-key-utils.js"; @@ -90,6 +91,12 @@ function resolveComparableTranscriptPath(filePath: string): string { return tryResolveNativeRealPath(filePath) ?? path.resolve(filePath); } +function areComparablePathsEqual(leftPath: string, rightPath: string): boolean { + const leftRealPath = tryResolveNativeRealPath(leftPath); + const rightRealPath = tryResolveNativeRealPath(rightPath); + return leftRealPath !== null && leftRealPath === rightRealPath; +} + function isReachableConfiguredAgentDir(params: { agentsRoot: string; dirName: string; @@ -126,6 +133,7 @@ function listOrphanAgentDirs(cfg: OpenClawConfig, stateDir: string): OrphanAgent } const agentsRoot = path.join(stateDir, "agents"); + const liveCompatibilityAgentDir = resolveOpenClawAgentDir(); try { const entries = fs.readdirSync(agentsRoot, { withFileTypes: true }); return entries @@ -135,10 +143,14 @@ function listOrphanAgentDirs(cfg: OpenClawConfig, stateDir: string): OrphanAgent agentId: normalizeAgentId(entry.name), })) .filter(({ dirName, agentId }) => { - const hasNestedAgentDir = existsDir(path.join(agentsRoot, dirName, "agent")); + const nestedAgentDir = path.join(agentsRoot, dirName, "agent"); + const hasNestedAgentDir = existsDir(nestedAgentDir); if (!hasNestedAgentDir) { return false; } + if (areComparablePathsEqual(nestedAgentDir, liveCompatibilityAgentDir)) { + return false; + } if (!configuredIds.has(agentId)) { return true; }