diff --git a/src/infra/diagnostic-flags.test.ts b/src/infra/diagnostic-flags.test.ts index 7c4c3b0a62d..868b4c56c2f 100644 --- a/src/infra/diagnostic-flags.test.ts +++ b/src/infra/diagnostic-flags.test.ts @@ -18,17 +18,29 @@ describe("resolveDiagnosticFlags", () => { expect(resolveDiagnosticFlags(cfg, env)).toEqual(["telegram.http", "cache.*", "foo"]); }); - it("treats false-like env values as no extra flags", () => { + it("treats blank env values as no extra flags", () => { const cfg = { diagnostics: { flags: ["telegram.http"] }, } as OpenClawConfig; - for (const raw of ["0", "false", "off", "none", " "]) { + expect( + resolveDiagnosticFlags(cfg, { + OPENCLAW_DIAGNOSTICS: " ", + } as NodeJS.ProcessEnv), + ).toEqual(["telegram.http"]); + }); + + it("treats false-like env values as disable overrides", () => { + const cfg = { + diagnostics: { flags: ["telegram.http"] }, + } as OpenClawConfig; + + for (const raw of ["0", "false", "off", "none"]) { expect( resolveDiagnosticFlags(cfg, { OPENCLAW_DIAGNOSTICS: raw, } as NodeJS.ProcessEnv), - ).toEqual(["telegram.http"]); + ).toEqual([]); } }); }); diff --git a/src/infra/diagnostic-flags.ts b/src/infra/diagnostic-flags.ts index 36c8625fc2c..7e6dfda211e 100644 --- a/src/infra/diagnostic-flags.ts +++ b/src/infra/diagnostic-flags.ts @@ -3,25 +3,33 @@ import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js"; const DIAGNOSTICS_ENV = "OPENCLAW_DIAGNOSTICS"; -function parseEnvFlags(raw?: string): string[] { +type ParsedEnvFlags = { + flags: string[]; + disablesAll: boolean; +}; + +function parseEnvFlags(raw?: string): ParsedEnvFlags { if (!raw) { - return []; + return { flags: [], disablesAll: false }; } const trimmed = raw.trim(); const lowered = normalizeLowercaseStringOrEmpty(trimmed); if (!lowered) { - return []; + return { flags: [], disablesAll: false }; } if (["0", "false", "off", "none"].includes(lowered)) { - return []; + return { flags: [], disablesAll: true }; } if (["1", "true", "all", "*"].includes(lowered)) { - return ["*"]; + return { flags: ["*"], disablesAll: false }; } - return trimmed - .split(/[,\s]+/) - .map((value) => normalizeLowercaseStringOrEmpty(value)) - .filter(Boolean); + return { + flags: trimmed + .split(/[,\s]+/) + .map((value) => normalizeLowercaseStringOrEmpty(value)) + .filter(Boolean), + disablesAll: false, + }; } function uniqueFlags(flags: string[]): string[] { @@ -44,7 +52,10 @@ export function resolveDiagnosticFlags( ): string[] { const configFlags = Array.isArray(cfg?.diagnostics?.flags) ? cfg?.diagnostics?.flags : []; const envFlags = parseEnvFlags(env[DIAGNOSTICS_ENV]); - return uniqueFlags([...configFlags, ...envFlags]); + if (envFlags.disablesAll) { + return []; + } + return uniqueFlags([...configFlags, ...envFlags.flags]); } export function matchesDiagnosticFlag(flag: string, enabledFlags: string[]): boolean { diff --git a/src/infra/diagnostics-timeline.test.ts b/src/infra/diagnostics-timeline.test.ts index 7ba11252876..d391cdae2fa 100644 --- a/src/infra/diagnostics-timeline.test.ts +++ b/src/infra/diagnostics-timeline.test.ts @@ -86,6 +86,18 @@ describe("diagnostics timeline", () => { ).toBe(false); }); + it("lets false-like env diagnostics disable config-enabled timeline output", async () => { + const { env } = await createTimelineEnv(); + const configWithTimeline = { diagnostics: { flags: ["timeline"] } } as OpenClawConfig; + + expect( + isDiagnosticsTimelineEnabled({ + config: configWithTimeline, + env: { ...env, OPENCLAW_DIAGNOSTICS: "0" }, + }), + ).toBe(false); + }); + it("writes JSONL diagnostic events with the stable envelope", async () => { const { env, path } = await createTimelineEnv();