refactor(config): retire redundant settings (#113174)

* refactor(config): retire redundant settings

* style: apply current formatter

* chore: update plugin sdk baseline

* fix: keep Codex tool caps context-aware

* chore: remove stale imports

* test: align WhatsApp QA debounce config

* fix(config): clean up retired config checks

* fix(ci): align config cleanup checks
This commit is contained in:
Peter Steinberger
2026-07-23 17:45:02 -07:00
committed by GitHub
parent 1603781bb0
commit bb657eec93
99 changed files with 796 additions and 1445 deletions
@@ -9,7 +9,6 @@ import { LEGACY_SECRETREF_ENV_MARKER_PREFIX } from "../config/types.secrets.js";
import { migrateLegacySecretRefEnvMarkers } from "../secrets/legacy-secretref-env-marker.js";
import { readConfigMachineState } from "../state/config-machine-state.js";
import { CORE_HEALTH_CHECKS } from "./doctor-core-checks.js";
import "./doctor-tool-result-cap-advice.js";
import { resolveDoctorContributionHealthChecks } from "./doctor-health-contributions.js";
import {
createDoctorHealthContribution,
@@ -1865,7 +1864,6 @@ describe("doctor health contributions", () => {
expect(contributionIds).toContain("core/doctor/device-pairing");
expect(contributionIds).toContain("core/doctor/channel-plugin-blockers");
expect(contributionIds).toContain("core/doctor/channel-preview-warnings");
expect(contributionIds).toContain("core/doctor/tool-result-cap");
expect(contributionIds).toContain("core/doctor/systemd-linger");
expect(contributionChecks.map((check) => check.id)).toEqual(contributionIds);
});
@@ -1935,60 +1933,6 @@ describe("doctor health contributions", () => {
expect(mocks.readSystemdUserLingerStatus).not.toHaveBeenCalled();
});
it("keeps tool result cap opt-in for default lint selection", async () => {
const contributionChecks = await resolveDoctorContributionHealthChecks();
const toolResultCapCheck = contributionChecks.find(
(check) => check.id === "core/doctor/tool-result-cap",
);
expect(toolResultCapCheck).toMatchObject({ defaultEnabled: false });
expect(toolResultCapCheck).toBeDefined();
const ctx = {
cfg: {
agents: {
defaults: { contextLimits: { toolResultMaxChars: 16_000 } },
},
},
mode: "lint",
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
} as const;
const detect = vi.fn(async () => [
{
checkId: "core/doctor/tool-result-cap",
severity: "warning" as const,
message: "Configured tool result cap overrides the model-window default.",
path: "agents.defaults.contextLimits.toolResultMaxChars",
},
]);
// This case owns lint selection; the real cap detector is exercised below.
const checks = [{ ...toolResultCapCheck!, detect }];
await expect(runDoctorLintChecks(ctx, { checks })).resolves.toMatchObject({
checksRun: 0,
checksSkipped: 1,
});
expect(detect).not.toHaveBeenCalled();
await expect(
runDoctorLintChecks(ctx, { checks, includeAllChecks: true }),
).resolves.toMatchObject({
checksRun: 1,
checksSkipped: 0,
findings: [
expect.objectContaining({
checkId: "core/doctor/tool-result-cap",
path: "agents.defaults.contextLimits.toolResultMaxChars",
}),
],
});
await expect(
runDoctorLintChecks(ctx, { checks, onlyIds: ["core/doctor/tool-result-cap"] }),
).resolves.toMatchObject({
checksRun: 1,
checksSkipped: 0,
});
expect(detect).toHaveBeenCalledTimes(2);
});
it("keeps stale plugin-runtime symlinks opt-in for structured lint selection", async () => {
const contributionChecks = await resolveDoctorContributionHealthChecks();
const check = contributionChecks.find(
@@ -2036,56 +1980,6 @@ describe("doctor health contributions", () => {
expect(mocks.collectStalePluginRuntimeSymlinkHealthFindings).toHaveBeenCalledTimes(1);
});
it("reports agent findings for inherited default tool result caps", async () => {
const contributionChecks = await resolveDoctorContributionHealthChecks();
const toolResultCapCheck = contributionChecks.find(
(check) => check.id === "core/doctor/tool-result-cap",
);
expect(toolResultCapCheck).toBeDefined();
mocks.resolveAgentContextLimits.mockImplementation(
(cfg: { agents?: { defaults?: { contextLimits?: unknown } } }) =>
cfg.agents?.defaults?.contextLimits ?? {},
);
mocks.resolveDefaultModelForAgent.mockImplementation((...args: unknown[]) => {
const params = args[0] as { agentId?: string };
return params.agentId === "writer"
? { provider: "openai", model: "gpt-5.5" }
: { provider: "local", model: "tiny" };
});
mocks.findModelCatalogEntry.mockImplementation((...args: unknown[]) => {
const params = args[1] as { modelId?: string };
return params.modelId === "gpt-5.5" ? { contextTokens: 200_000 } : { contextTokens: 8_000 };
});
const ctx = {
cfg: {
agents: {
defaults: { contextLimits: { toolResultMaxChars: 16_000 } },
list: [{ id: "writer" }],
},
},
mode: "lint" as const,
runtime: { log: vi.fn(), error: vi.fn(), exit: vi.fn() },
};
await expect(
runDoctorLintChecks(ctx, {
checks: [toolResultCapCheck!],
onlyIds: ["core/doctor/tool-result-cap"],
}),
).resolves.toMatchObject({
checksRun: 1,
findings: expect.arrayContaining([
expect.objectContaining({
checkId: "core/doctor/tool-result-cap",
path: "agents.defaults.contextLimits.toolResultMaxChars",
target: "agents.list.writer",
}),
]),
});
});
it("keeps legacy plugin dependency lint opt-in and read-only", async () => {
const previousStateDir = process.env.OPENCLAW_STATE_DIR;
const tempDir = fs.mkdtempSync(nodePath.join(os.tmpdir(), "openclaw-legacy-plugin-deps-lint-"));
-174
View File
@@ -86,7 +86,6 @@ const loadDoctorCoreChecksModule = async () => await import("./doctor-core-check
const loadDoctorStateIntegrityModule = async () =>
await import("../commands/doctor-state-integrity.js");
const loadHealthCheckRegistryModule = async () => await import("./health-check-registry.js");
const loadCatalogLookupModule = async () => await import("../agents/model-catalog.js");
const loadPreparedModelCatalogModule = async () =>
await import("../agents/prepared-model-catalog.js");
const loadModelSelectionModule = async () => await import("../agents/model-selection.js");
@@ -842,167 +841,6 @@ async function runHooksModelHealth(ctx: DoctorHealthFlowContext): Promise<void>
}
}
type ToolResultCapTarget = {
agentId?: string;
configuredCap?: number;
path?: string;
scopeLabel: string;
target?: string;
};
async function collectToolResultCapFindings(
cfg: OpenClawConfig,
): Promise<readonly HealthFinding[]> {
const { resolveAgentContextLimits } = await loadAgentScopeModule();
const { normalizeAgentId } = await import("../routing/session-key.js");
const targets: ToolResultCapTarget[] = [];
const defaultsConfiguredCap = cfg.agents?.defaults?.contextLimits?.toolResultMaxChars;
if (defaultsConfiguredCap !== undefined) {
targets.push({
configuredCap: defaultsConfiguredCap,
path: "agents.defaults.contextLimits.toolResultMaxChars",
scopeLabel: "defaults",
target: "agents.defaults",
});
}
for (const entry of cfg.agents?.list ?? []) {
const normalizedAgentId = normalizeAgentId(entry.id);
if (
!normalizedAgentId ||
(defaultsConfiguredCap === undefined && entry.contextLimits?.toolResultMaxChars === undefined)
) {
continue;
}
targets.push({
agentId: normalizedAgentId,
configuredCap: resolveAgentContextLimits(cfg, normalizedAgentId)?.toolResultMaxChars,
path:
entry.contextLimits?.toolResultMaxChars === undefined
? "agents.defaults.contextLimits.toolResultMaxChars"
: `agents.list.${normalizedAgentId}.contextLimits.toolResultMaxChars`,
scopeLabel: `agent "${normalizedAgentId}"`,
target: `agents.list.${normalizedAgentId}`,
});
}
if (targets.length === 0) {
return [];
}
const { collectToolResultCapDoctorIssues, toolResultCapDoctorIssueToHealthFinding } =
await import("./doctor-tool-result-cap-advice.js");
return collectToolResultCapTargetAdvice({
cfg,
readOnlyCatalog: true,
targets,
}).then((entries) =>
entries.flatMap((entry) =>
collectToolResultCapDoctorIssues(entry).map(toolResultCapDoctorIssueToHealthFinding),
),
);
}
async function collectToolResultCapTargetAdvice(params: {
cfg: OpenClawConfig;
readOnlyCatalog?: boolean;
targets: readonly ToolResultCapTarget[];
}): Promise<
Array<{
contextWindowTokens: number;
modelKey: string;
configuredCap?: number;
deep?: boolean;
path?: string;
scopeLabel?: string;
target?: string;
}>
> {
const { DEFAULT_CONTEXT_TOKENS } = await loadAgentDefaultsModule();
const { findModelCatalogEntry } = await loadCatalogLookupModule();
const { loadPreparedModelCatalog } = await loadPreparedModelCatalogModule();
const { resolveContextWindowInfo } = await import("../agents/context-window-guard.js");
const { resolveDefaultModelForAgent, modelKey } = await loadModelSelectionModule();
const catalog = await loadPreparedModelCatalog({
config: params.cfg,
...(params.readOnlyCatalog ? { readOnly: true } : {}),
});
return params.targets.map((target) => {
const modelRef = resolveDefaultModelForAgent({
cfg: params.cfg,
agentId: target.agentId,
});
const entry = findModelCatalogEntry(catalog, {
provider: modelRef.provider,
modelId: modelRef.model,
});
const contextWindow = resolveContextWindowInfo({
cfg: params.cfg,
provider: modelRef.provider,
modelId: modelRef.model,
modelContextTokens: entry?.contextTokens,
modelContextWindow: entry?.contextWindow,
defaultTokens: DEFAULT_CONTEXT_TOKENS,
});
return {
contextWindowTokens: contextWindow.tokens,
modelKey: modelKey(modelRef.provider, modelRef.model),
configuredCap: target.configuredCap,
path: target.path,
scopeLabel: target.scopeLabel,
target: target.target,
};
});
}
async function runToolResultCapHealth(ctx: DoctorHealthFlowContext): Promise<void> {
const { resolveAgentContextLimits } = await loadAgentScopeModule();
const { normalizeAgentId } = await import("../routing/session-key.js");
const targets: ToolResultCapTarget[] = [];
const defaultsConfiguredCap = ctx.cfg.agents?.defaults?.contextLimits?.toolResultMaxChars;
if (ctx.options.deep === true || defaultsConfiguredCap !== undefined) {
targets.push({
configuredCap: defaultsConfiguredCap,
scopeLabel: "defaults",
});
}
for (const entry of ctx.cfg.agents?.list ?? []) {
const normalizedAgentId = normalizeAgentId(entry.id);
if (
!normalizedAgentId ||
(ctx.options.deep !== true &&
defaultsConfiguredCap === undefined &&
entry.contextLimits?.toolResultMaxChars === undefined)
) {
continue;
}
targets.push({
agentId: normalizedAgentId,
configuredCap: resolveAgentContextLimits(ctx.cfg, normalizedAgentId)?.toolResultMaxChars,
scopeLabel: `agent "${normalizedAgentId}"`,
});
}
if (targets.length === 0) {
return;
}
const { buildToolResultCapDoctorAdvice } = await import("./doctor-tool-result-cap-advice.js");
const { note } = await loadNoteModule();
const entries = await collectToolResultCapTargetAdvice({
cfg: ctx.cfg,
targets,
});
const lines = entries.flatMap((entry) =>
buildToolResultCapDoctorAdvice({
...entry,
deep: ctx.options.deep === true,
}),
);
if (lines.length > 0) {
note(lines.join("\n"), "Tool result cap");
}
}
async function runSystemdLingerHealth(ctx: DoctorHealthFlowContext): Promise<void> {
if (
ctx.options.nonInteractive === true ||
@@ -2095,18 +1933,6 @@ function resolveDoctorHealthContributions(): DoctorHealthContribution[] {
healthCheckIds: ["core/doctor/hooks-model"],
run: runHooksModelHealth,
}),
createDoctorHealthContribution({
id: "doctor:tool-result-cap",
label: "Tool result cap",
healthChecks: {
id: "core/doctor/tool-result-cap",
description:
"Detect explicit toolResultMaxChars settings that fight model-window defaults.",
defaultEnabled: false,
detect: async (ctx) => collectToolResultCapFindings(ctx.cfg),
},
run: runToolResultCapHealth,
}),
createDoctorHealthContribution({
id: "doctor:provider-catalog-projection",
label: "Provider catalog projection",
@@ -1,80 +0,0 @@
// Tool result cap advice tests cover doctor guidance for capped tool output.
import { expectDefined } from "@openclaw/normalization-core";
import { describe, expect, it } from "vitest";
import {
buildToolResultCapDoctorAdvice,
collectToolResultCapDoctorIssues,
toolResultCapDoctorIssueToHealthFinding,
} from "./doctor-tool-result-cap-advice.js";
describe("buildToolResultCapDoctorAdvice", () => {
it("stays quiet for unset config outside deep doctor output", () => {
expect(
buildToolResultCapDoctorAdvice({
contextWindowTokens: 200_000,
modelKey: "openai/gpt-5.5",
}),
).toEqual([]);
});
it("shows the effective auto cap in deep doctor output", () => {
expect(
buildToolResultCapDoctorAdvice({
contextWindowTokens: 200_000,
modelKey: "openai/gpt-5.5",
deep: true,
}),
).toEqual([
'- primary model "openai/gpt-5.5" context window 200,000 tokens; live tool-result cap 64,000 chars (auto)',
]);
});
it("warns when an explicit cap keeps a large model on the old 16K ceiling", () => {
expect(
buildToolResultCapDoctorAdvice({
contextWindowTokens: 200_000,
modelKey: "openai/gpt-5.5",
configuredCap: 16_000,
scopeLabel: 'agent "writer"',
}),
).toEqual([
'- agent "writer": configured toolResultMaxChars is 16,000 chars; unset it to use the 64,000 char auto cap for "openai/gpt-5.5".',
]);
});
it("warns when an explicit cap is above the runtime context-share ceiling", () => {
expect(
buildToolResultCapDoctorAdvice({
contextWindowTokens: 8_000,
modelKey: "local/tiny",
configuredCap: 20_000,
}),
).toEqual([
"- configured toolResultMaxChars is 20,000 chars, but this model can use at most 9,600 chars per live tool result; lower it or unset it.",
]);
});
it("maps cap advice issues to structured health findings", () => {
const [issue] = collectToolResultCapDoctorIssues({
contextWindowTokens: 200_000,
modelKey: "openai/gpt-5.5",
configuredCap: 16_000,
path: "agents.writer.contextLimits.toolResultMaxChars",
scopeLabel: 'agent "writer"',
target: "agents.writer",
});
expect(
toolResultCapDoctorIssueToHealthFinding(expectDefined(issue, "issue test invariant")),
).toEqual({
checkId: "core/doctor/tool-result-cap",
severity: "warning",
message:
'agent "writer": configured toolResultMaxChars is 16,000 chars; unset it to use the 64,000 char auto cap for "openai/gpt-5.5".',
path: "agents.writer.contextLimits.toolResultMaxChars",
target: "agents.writer",
requirement: "configured-below-auto-cap",
fixHint: "Lower or unset agents.writer.contextLimits.toolResultMaxChars.",
});
});
});
-162
View File
@@ -1,162 +0,0 @@
// Tool result cap advice helpers format doctor guidance for capped outputs.
import {
calculateMaxToolResultCharsWithCap,
resolveAutoLiveToolResultMaxChars,
} from "../agents/embedded-agent-runner/tool-result-truncation.js";
import type { HealthFinding } from "./health-checks.js";
const TOOL_RESULT_CAP_CHECK_ID = "core/doctor/tool-result-cap";
type ToolResultCapDoctorIssue = {
kind: "configured-above-runtime-ceiling" | "configured-below-auto-cap";
contextWindowTokens: number;
modelKey: string;
configuredCap: number;
runtimeCeiling?: number;
autoEffectiveCap?: number;
path?: string;
scopeLabel?: string;
target?: string;
};
// Doctor advice for explicit live tool-result caps that fight model-window defaults.
type ToolResultCapDoctorAdviceParams = {
contextWindowTokens: number;
modelKey: string;
configuredCap?: number;
deep?: boolean;
path?: string;
scopeLabel?: string;
target?: string;
};
function formatNumber(value: number): string {
return String(Math.max(0, Math.floor(value))).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function formatIssueMessage(issue: ToolResultCapDoctorIssue): string {
const prefix = issue.scopeLabel ? `${issue.scopeLabel}: ` : "";
if (issue.kind === "configured-above-runtime-ceiling") {
return `${prefix}configured toolResultMaxChars is ${formatNumber(
issue.configuredCap,
)} chars, but this model can use at most ${formatNumber(
issue.runtimeCeiling ?? 0,
)} chars per live tool result; lower it or unset it.`;
}
return `${prefix}configured toolResultMaxChars is ${formatNumber(
issue.configuredCap,
)} chars; unset it to use the ${formatNumber(issue.autoEffectiveCap ?? 0)} char auto cap for "${
issue.modelKey
}".`;
}
export function collectToolResultCapDoctorIssues(
params: ToolResultCapDoctorAdviceParams,
): ToolResultCapDoctorIssue[] {
if (!Number.isFinite(params.contextWindowTokens) || params.contextWindowTokens <= 0) {
return [];
}
const configuredCap =
typeof params.configuredCap === "number" && Number.isFinite(params.configuredCap)
? Math.floor(params.configuredCap)
: undefined;
if (configuredCap === undefined) {
return [];
}
const autoCap = resolveAutoLiveToolResultMaxChars(params.contextWindowTokens);
const runtimeCeiling = calculateMaxToolResultCharsWithCap(
params.contextWindowTokens,
Number.MAX_SAFE_INTEGER,
);
const effectiveCap = calculateMaxToolResultCharsWithCap(
params.contextWindowTokens,
configuredCap,
);
const autoEffectiveCap = calculateMaxToolResultCharsWithCap(params.contextWindowTokens, autoCap);
if (configuredCap > runtimeCeiling) {
return [
{
kind: "configured-above-runtime-ceiling",
contextWindowTokens: params.contextWindowTokens,
modelKey: params.modelKey,
configuredCap,
runtimeCeiling,
path: params.path,
scopeLabel: params.scopeLabel,
target: params.target,
},
];
}
if (effectiveCap < autoEffectiveCap) {
return [
{
kind: "configured-below-auto-cap",
contextWindowTokens: params.contextWindowTokens,
modelKey: params.modelKey,
configuredCap,
autoEffectiveCap,
path: params.path,
scopeLabel: params.scopeLabel,
target: params.target,
},
];
}
return [];
}
export function toolResultCapDoctorIssueToHealthFinding(
issue: ToolResultCapDoctorIssue,
): HealthFinding {
return {
checkId: TOOL_RESULT_CAP_CHECK_ID,
severity: "warning",
message: formatIssueMessage(issue),
...(issue.path ? { path: issue.path } : {}),
...(issue.target ? { target: issue.target } : {}),
requirement: issue.kind,
fixHint: issue.path ? `Lower or unset ${issue.path}.` : "Lower or unset toolResultMaxChars.",
};
}
/** Builds human-readable doctor lines for stale or ineffective toolResultMaxChars settings. */
export function buildToolResultCapDoctorAdvice(params: ToolResultCapDoctorAdviceParams): string[] {
if (!Number.isFinite(params.contextWindowTokens) || params.contextWindowTokens <= 0) {
return [];
}
const autoCap = resolveAutoLiveToolResultMaxChars(params.contextWindowTokens);
const configuredCap =
typeof params.configuredCap === "number" && Number.isFinite(params.configuredCap)
? Math.floor(params.configuredCap)
: undefined;
const configuredSource = configuredCap !== undefined;
const requestedCap = configuredCap ?? autoCap;
const effectiveCap = calculateMaxToolResultCharsWithCap(params.contextWindowTokens, requestedCap);
const lines: string[] = [];
const prefix = params.scopeLabel ? `${params.scopeLabel}: ` : "";
// Deep mode always shows the effective cap, even when no warning is needed.
if (params.deep) {
lines.push(
`- ${prefix}primary model "${params.modelKey}" context window ${formatNumber(
params.contextWindowTokens,
)} tokens; live tool-result cap ${formatNumber(effectiveCap)} chars (${
configuredSource ? "explicit" : "auto"
})`,
);
}
if (configuredCap === undefined) {
return lines;
}
lines.push(
...collectToolResultCapDoctorIssues(params).map((issue) => `- ${formatIssueMessage(issue)}`),
);
return lines;
}