diff --git a/src/config/io.context.ts b/src/config/io.context.ts index ec68a716601a..e17b5dcf8d2f 100644 --- a/src/config/io.context.ts +++ b/src/config/io.context.ts @@ -40,7 +40,6 @@ import { formatConfigIssueSummary } from "./issue-format.js"; import { migrateLegacyContextBudgetConfig } from "./legacy.context-budget.js"; import { inheritLegacyDefaultAgentId } from "./legacy.default-agent-owner.js"; import { migratePersistedImplicitMainRoster } from "./legacy.roster.js"; -import { materializeRuntimeConfig } from "./materialize.js"; import { copyConfigResolutionFacts } from "./resolution-facts.js"; import { applyConfigOverrides } from "./runtime-overrides.js"; import { resolveShellEnvExpectedKeys } from "./shell-env-expected-keys.js"; @@ -275,14 +274,3 @@ export function createConfigIoContext(options: ConfigIoFactoryOptions = {}): Con export function resolveModelIdNormalizationPolicies(snapshot: PluginMetadataSnapshot | undefined) { return snapshot ? collectManifestModelIdNormalizationPolicies(snapshot.plugins) : undefined; } - -export function materializeConfigForLoad( - _context: ConfigIoContext, - config: OpenClawConfig, - _effectiveConfigRaw: unknown, - manifestRegistry: PluginManifestRegistry | undefined, -): OpenClawConfig { - return materializeRuntimeConfig(config, "load", { - manifestRegistry, - }); -} diff --git a/src/config/io.load.ts b/src/config/io.load.ts index 92ad5742581c..cea2dca4c761 100644 --- a/src/config/io.load.ts +++ b/src/config/io.load.ts @@ -1,7 +1,6 @@ import { formatErrorMessage } from "../infra/errors.js"; import { DuplicateAgentDirError, findDuplicateAgentDirs } from "./agent-dirs.js"; import type { ConfigIoContext } from "./io.context.js"; -import { materializeConfigForLoad } from "./io.context.js"; import { throwInvalidConfig } from "./io.invalid-config.js"; import { maybeRecoverSuspiciousConfigReadSync } from "./io.observe-recovery.js"; import { @@ -22,6 +21,7 @@ import { warnOnConfigMiskeys, } from "./io.warnings.js"; import { migrateLegacyContextBudgetConfig, migratePersistedImplicitMainRoster } from "./legacy.js"; +import { materializeRuntimeConfig } from "./materialize.js"; import type { OpenClawConfig } from "./types.js"; import { validateConfigObjectWithPlugins } from "./validation.js"; @@ -40,12 +40,7 @@ export function loadConfigFromContext( // same runtime defaults an empty {} config gets, or out-of-box behavior // (compaction safeguard, session/cron defaults) silently diverges. return context.finalizeLoadedRuntimeConfig( - materializeConfigForLoad( - context, - coerceConfig(migratePersistedImplicitMainRoster({}).config), - {}, - undefined, - ), + materializeRuntimeConfig(coerceConfig(migratePersistedImplicitMainRoster({}).config)), ); } const raw = deps.fs.readFileSync(configPath, "utf-8"); @@ -151,12 +146,9 @@ export function loadConfigFromContext( return loadConfigFromContext(context, { skipSuspiciousRecovery: true }); } } - const cfg = materializeConfigForLoad( - context, - validated.config, - effectiveConfigRaw, - pluginMetadata.getManifestRegistry(), - ); + const cfg = materializeRuntimeConfig(validated.config, { + manifestRegistry: pluginMetadata.getManifestRegistry(), + }); context.observeLoadConfigSnapshot( createConfigFileSnapshot({ path: configPath, diff --git a/src/config/io.snapshot.ts b/src/config/io.snapshot.ts index 22b482277dee..67cdf26d8023 100644 --- a/src/config/io.snapshot.ts +++ b/src/config/io.snapshot.ts @@ -74,7 +74,7 @@ export async function readConfigFileSnapshotInternal( // Missing config is the fresh-install default path: materialize the // same runtime defaults an existing empty {} config gets, so snapshot // consumers see identical out-of-box behavior either way. - runtimeConfig: materializeRuntimeConfig(config, "snapshot", { + runtimeConfig: materializeRuntimeConfig(config, { manifestRegistry: context.options.pluginValidation === "core-only" ? { plugins: [] } : undefined, }), @@ -295,7 +295,7 @@ export async function readConfigFileSnapshotInternal( } } const snapshotConfig = await deps.measure("config.snapshot.read.materialize", () => - materializeRuntimeConfig(validated.config, "snapshot", { + materializeRuntimeConfig(validated.config, { manifestRegistry: pluginMetadata.getSnapshot()?.manifestRegistry ?? (context.options.pluginValidation === "core-only" ? { plugins: [] } : undefined), @@ -454,7 +454,7 @@ export async function readBestEffortConfigSnapshotFromContext( } return { config: context.finalizeLoadedRuntimeConfig( - materializeRuntimeConfig(result.snapshot.sourceConfig, "load", { + materializeRuntimeConfig(result.snapshot.sourceConfig, { manifestRegistry: result.pluginMetadataSnapshot?.manifestRegistry, }), ), diff --git a/src/config/materialize.test.ts b/src/config/materialize.test.ts deleted file mode 100644 index e25afcf83f5b..000000000000 --- a/src/config/materialize.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -// Verifies snapshot/load materialization parity for prepared-runtime config matching. -import { describe, expect, it } from "vitest"; -import { materializeRuntimeConfig } from "./materialize.js"; -import type { OpenClawConfig } from "./types.openclaw.js"; - -describe("materializeRuntimeConfig", () => { - it("materializes snapshot and load identically when defaults must be injected", () => { - // A compaction block without a mode is the shape that forces default injection. - const config: OpenClawConfig = { - agents: { - defaults: { - compaction: {}, - }, - }, - }; - - const snapshot = materializeRuntimeConfig(config, "snapshot"); - const load = materializeRuntimeConfig(config, "load"); - - expect(snapshot.agents?.defaults?.compaction?.mode).toBeDefined(); - expect(snapshot).toEqual(load); - }); - - it("keeps an explicit compaction mode unchanged in both modes", () => { - const config: OpenClawConfig = { - agents: { - defaults: { - compaction: { mode: "safeguard" }, - }, - }, - }; - - expect(materializeRuntimeConfig(config, "snapshot").agents?.defaults?.compaction?.mode).toBe( - "safeguard", - ); - expect(materializeRuntimeConfig(config, "load").agents?.defaults?.compaction?.mode).toBe( - "safeguard", - ); - }); -}); diff --git a/src/config/materialize.ts b/src/config/materialize.ts index 22be388e05ff..9fd2a77f8f65 100644 --- a/src/config/materialize.ts +++ b/src/config/materialize.ts @@ -19,9 +19,6 @@ import type { OpenClawConfig, ResolvedSourceConfig, RuntimeConfig } from "./type // Snapshot and load must materialize identically: prepared-runtime exact-config // resolution compares the startup-published (snapshot) config against the reply-path // (load) config, and any divergence permanently fails that resolve for affected configs. -// The mode parameter documents the call site; a per-mode defaults profile existed -// until its last divergent ("missing") caller was removed and only invited drift. -type ConfigMaterializationMode = "load" | "snapshot"; export function asResolvedSourceConfig(config: OpenClawConfig): ResolvedSourceConfig { return config as ResolvedSourceConfig; @@ -33,7 +30,6 @@ export function asRuntimeConfig(config: OpenClawConfig): RuntimeConfig { export function materializeRuntimeConfig( config: OpenClawConfig, - _mode: ConfigMaterializationMode, options: { manifestRegistry?: Pick; loadManifestRegistry?: () => Pick | undefined; diff --git a/src/config/redact-snapshot.test.ts b/src/config/redact-snapshot.test.ts index 443a4adbe53f..47c23e019a3d 100644 --- a/src/config/redact-snapshot.test.ts +++ b/src/config/redact-snapshot.test.ts @@ -658,7 +658,7 @@ describe("redactConfigSnapshot", () => { }, } satisfies OpenClawConfig; const raw = JSON.stringify(sourceConfig); - const runtimeConfig = materializeRuntimeConfig(structuredClone(sourceConfig), "snapshot"); + const runtimeConfig = materializeRuntimeConfig(structuredClone(sourceConfig)); const snapshot = { ...makeSnapshot(sourceConfig, raw), config: runtimeConfig, diff --git a/src/config/validation-core.ts b/src/config/validation-core.ts index dc40945bec1c..df0db4f052e1 100644 --- a/src/config/validation-core.ts +++ b/src/config/validation-core.ts @@ -427,7 +427,7 @@ export function validateConfigObject( return { ok: true, config: attachAgentListProjection( - materializeRuntimeConfig(result.config, "snapshot", { + materializeRuntimeConfig(result.config, { manifestRegistry: opts?.manifestRegistry, }), ), diff --git a/src/config/validation.ts b/src/config/validation.ts index 60e420546c5c..431750cad05e 100644 --- a/src/config/validation.ts +++ b/src/config/validation.ts @@ -218,7 +218,7 @@ function validateConfigObjectWithPluginsBase( } } const config = opts.applyDefaults - ? materializeRuntimeConfig(parsedConfig, "snapshot", { + ? materializeRuntimeConfig(parsedConfig, { manifestRegistry: registryInfo?.registry ?? (opts.pluginValidation === "core-only" ? { plugins: [] } : undefined),