From 1497425b8dbb4477f9cafda927a531e84e11afdc Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 27 Apr 2026 11:12:49 -0700 Subject: [PATCH] fix(gateway): trim startup config imports --- CHANGELOG.md | 3 + scripts/bench-gateway-startup.ts | 57 ++++++++++++++++++- src/gateway/auth-resolve.ts | 2 +- src/gateway/auth.ts | 2 +- src/gateway/config-reload.ts | 15 ++--- src/gateway/embeddings-http.ts | 2 +- src/gateway/exec-approval-ios-push.ts | 2 +- src/gateway/http-auth-utils.ts | 2 +- src/gateway/http-utils.ts | 2 +- src/gateway/mcp-http.ts | 2 +- src/gateway/models-http.ts | 2 +- src/gateway/server-chat.ts | 2 +- src/gateway/server-cron.ts | 2 +- src/gateway/server-http.ts | 2 +- src/gateway/server-methods/nodes.ts | 2 +- src/gateway/server-model-catalog.ts | 13 ++--- src/gateway/server-node-events.runtime.ts | 2 +- src/gateway/server-runtime-config.ts | 2 +- src/gateway/server-session-key.ts | 2 +- src/gateway/server-startup-config.ts | 22 +++---- src/gateway/server.impl.ts | 10 ++-- src/gateway/server.ts | 2 +- src/gateway/server/health-state.ts | 3 +- src/gateway/server/hooks.ts | 2 +- .../server/ws-connection/message-handler.ts | 2 +- src/gateway/session-kill-http.ts | 2 +- src/gateway/session-reset-service.ts | 2 +- src/gateway/session-transcript-key.ts | 2 +- src/gateway/session-utils.ts | 2 +- src/gateway/sessions-history-http.ts | 2 +- src/gateway/startup-auth.ts | 9 +-- 31 files changed, 111 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba98ad91fe2..71d91b10b11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,12 @@ Docs: https://docs.openclaw.ai - Channels/QQBot: add full group chat support (history tracking, @-mention gating, activation modes, per-group config, FIFO message queue with deliver debounce), C2C `stream_messages` streaming with a `StreamingController` lifecycle manager, unified `sendMedia` with chunked upload for large files, and refactor the engine into pipeline stages, focused outbound submodules, builtin slash-command modules, and explicit DI ports via `createEngineAdapters()`. (#70624) Thanks @cxyhhhhh. - Gateway/runtime: reuse the current plugin metadata snapshot for provider discovery so repeated model-provider discovery avoids rebuilding plugin manifest metadata. Thanks @shakkernerd. - Gateway/startup: pass the plugin metadata snapshot from config validation into plugin bootstrap so startup reuses one manifest product instead of rebuilding plugin metadata. Thanks @shakkernerd. +- ACP/runtime: add an opt-in bundled Coven backend extension that routes ACP coding sessions through a local Coven daemon when `acp.backend="coven"`, while preserving the existing ACPX backend as the default fallback path. Thanks @BunsDev. ### Fixes +- Gateway/startup: keep hot Gateway boot paths on leaf config imports and add max-RSS reporting to the gateway startup bench so low-memory startup regressions are visible before release. Thanks @vincentkoc. +- ACP/runtime: harden the opt-in Coven backend with workspace-confined launch paths, home-expanded Coven socket config, bounded socket responses, sanitized daemon output, and controlled polling failure handling. Thanks @BunsDev. - Agents/LSP: terminate bundled stdio LSP process trees during runtime disposal and Gateway shutdown, so nested children such as `tsserver` do not survive stop or restart. Fixes #72357. Thanks @ai-hpc and @bittoby. ## 2026.4.26 diff --git a/scripts/bench-gateway-startup.ts b/scripts/bench-gateway-startup.ts index 98f59703725..998d383ad67 100644 --- a/scripts/bench-gateway-startup.ts +++ b/scripts/bench-gateway-startup.ts @@ -1,4 +1,4 @@ -import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process"; +import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from "node:child_process"; import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { request } from "node:http"; import { createServer } from "node:net"; @@ -23,6 +23,7 @@ type GatewaySample = { exitCode: number | null; firstOutputMs: number | null; healthz: ProbeResult; + maxRssMb: number | null; outputTail: string; readyLogMs: number | null; readyz: ProbeResult; @@ -45,6 +46,7 @@ type CaseResult = { summary: { firstOutputMs: SummaryStats | null; healthzMs: SummaryStats | null; + maxRssMb: SummaryStats | null; readyLogMs: SummaryStats | null; readyzMs: SummaryStats | null; startupTrace: Record; @@ -253,6 +255,11 @@ function summarizeCase(benchCase: GatewayBenchCase, samples: GatewaySample[]): C .map((sample) => sample.healthz.ms) .filter((value): value is number => typeof value === "number"), ), + maxRssMb: summarizeNumbers( + samples + .map((sample) => sample.maxRssMb) + .filter((value): value is number => typeof value === "number"), + ), readyLogMs: summarizeNumbers( samples .map((sample) => sample.readyLogMs) @@ -275,6 +282,13 @@ function formatMs(value: number | null): string { return `${value.toFixed(1)}ms`; } +function formatMb(value: number | null): string { + if (value == null) { + return "n/a"; + } + return `${value.toFixed(1)}MB`; +} + function formatStats(stats: SummaryStats | null): string { if (!stats) { return "n/a"; @@ -282,6 +296,13 @@ function formatStats(stats: SummaryStats | null): string { return `p50=${formatMs(stats.p50)} avg=${formatMs(stats.avg)} min=${formatMs(stats.min)} max=${formatMs(stats.max)}`; } +function formatMemoryStats(stats: SummaryStats | null): string { + if (!stats) { + return "n/a"; + } + return `p50=${formatMb(stats.p50)} avg=${formatMb(stats.avg)} min=${formatMb(stats.min)} max=${formatMb(stats.max)}`; +} + async function getFreePort(): Promise { return new Promise((resolve, reject) => { const server = createServer(); @@ -400,6 +421,7 @@ function sanitizedEnv( OPENCLAW_GATEWAY_STARTUP_TRACE: "1", OPENCLAW_HOME: root, OPENCLAW_LOCAL_CHECK: "0", + OPENCLAW_NO_RESPAWN: "1", OPENCLAW_STATE_DIR: path.join(root, "state"), OPENCLAW_TEST_DISABLE_UPDATE_CHECK: "1", ...benchCase.env, @@ -475,6 +497,21 @@ function parseStartupTraceMetrics(raw: string): Array<{ key: string; value: numb return metrics; } +function readProcessRssMb(pid: number | undefined): number | null { + if (!pid || process.platform === "win32") { + return null; + } + const result = spawnSync("ps", ["-o", "rss=", "-p", String(pid)], { + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"], + }); + if (result.status !== 0) { + return null; + } + const rssKb = Number.parseInt(result.stdout.trim(), 10); + return Number.isFinite(rssKb) && rssKb > 0 ? rssKb / 1024 : null; +} + async function runGatewaySample(options: { benchCase: GatewayBenchCase; entry: string; @@ -489,6 +526,7 @@ async function runGatewaySample(options: { const startupTrace: Record = {}; const output: string[] = []; let firstOutputMs: number | null = null; + let maxRssMb: number | null = null; let readyLogMs: number | null = null; let childExited = false; @@ -510,6 +548,15 @@ async function runGatewaySample(options: { ], { cwd: process.cwd(), detached: process.platform !== "win32", env }, ); + const sampleRss = () => { + const rssMb = readProcessRssMb(child.pid); + if (rssMb != null) { + maxRssMb = maxRssMb == null ? rssMb : Math.max(maxRssMb, rssMb); + } + }; + sampleRss(); + const rssTimer = setInterval(sampleRss, 100); + rssTimer.unref?.(); const childExitPromise = new Promise<{ exitCode: number | null; signal: string | null }>( (resolve) => { child.once("exit", (exitCode, signal) => { @@ -555,6 +602,8 @@ async function runGatewaySample(options: { }), ]); const exit = await stopChild(child); + clearInterval(rssTimer); + sampleRss(); await childExitPromise.catch(() => null); rmSync(root, { force: true, maxRetries: 3, recursive: true, retryDelay: 100 }); @@ -562,6 +611,7 @@ async function runGatewaySample(options: { exitCode: exit.exitCode, firstOutputMs, healthz, + maxRssMb, outputTail: output.join("").split(/\r?\n/u).slice(-20).join("\n"), readyLogMs, readyz, @@ -588,11 +638,11 @@ async function runCase(options: { if (index >= options.warmup) { samples.push(sample); console.log( - `[gateway-startup-bench] ${options.benchCase.id} run ${samples.length}/${options.runs}: healthz=${formatMs(sample.healthz.ms)} readyz=${formatMs(sample.readyz.ms)} readyLog=${formatMs(sample.readyLogMs)}`, + `[gateway-startup-bench] ${options.benchCase.id} run ${samples.length}/${options.runs}: healthz=${formatMs(sample.healthz.ms)} readyz=${formatMs(sample.readyz.ms)} readyLog=${formatMs(sample.readyLogMs)} rss=${formatMb(sample.maxRssMb)}`, ); } else { console.log( - `[gateway-startup-bench] ${options.benchCase.id} warmup ${index + 1}/${options.warmup}: healthz=${formatMs(sample.healthz.ms)} readyz=${formatMs(sample.readyz.ms)}`, + `[gateway-startup-bench] ${options.benchCase.id} warmup ${index + 1}/${options.warmup}: healthz=${formatMs(sample.healthz.ms)} readyz=${formatMs(sample.readyz.ms)} rss=${formatMb(sample.maxRssMb)}`, ); } } @@ -605,6 +655,7 @@ function printResult(result: CaseResult): void { console.log(` /healthz: ${formatStats(result.summary.healthzMs)}`); console.log(` ready log: ${formatStats(result.summary.readyLogMs)}`); console.log(` /readyz: ${formatStats(result.summary.readyzMs)}`); + console.log(` max RSS: ${formatMemoryStats(result.summary.maxRssMb)}`); const trace = Object.entries(result.summary.startupTrace) .filter(([name]) => !name.endsWith(".total")) .toSorted((a, b) => (b[1].avg ?? 0) - (a[1].avg ?? 0)) diff --git a/src/gateway/auth-resolve.ts b/src/gateway/auth-resolve.ts index d1b2b911712..cf15c879179 100644 --- a/src/gateway/auth-resolve.ts +++ b/src/gateway/auth-resolve.ts @@ -2,7 +2,7 @@ import type { GatewayAuthConfig, GatewayTailscaleMode, GatewayTrustedProxyConfig, -} from "../config/config.js"; +} from "../config/types.gateway.js"; import { resolveSecretInputRef } from "../config/types.secrets.js"; import { resolveGatewayCredentialsFromValues } from "./credentials.js"; diff --git a/src/gateway/auth.ts b/src/gateway/auth.ts index 2ba4fbedc49..11af88b12b6 100644 --- a/src/gateway/auth.ts +++ b/src/gateway/auth.ts @@ -1,5 +1,5 @@ import type { IncomingMessage } from "node:http"; -import type { GatewayAuthConfig, GatewayTrustedProxyConfig } from "../config/config.js"; +import type { GatewayAuthConfig, GatewayTrustedProxyConfig } from "../config/types.gateway.js"; import { readTailscaleWhoisIdentity, type TailscaleWhoisIdentity } from "../infra/tailscale.js"; import { safeEqualSecret } from "../security/secret-equal.js"; import { diff --git a/src/gateway/config-reload.ts b/src/gateway/config-reload.ts index ecb0660028f..38d6bc6be64 100644 --- a/src/gateway/config-reload.ts +++ b/src/gateway/config-reload.ts @@ -1,17 +1,12 @@ import { isDeepStrictEqual } from "node:util"; import chokidar from "chokidar"; import { bumpSkillsSnapshotVersion } from "../agents/skills/refresh-state.js"; -import type { - OpenClawConfig, - ConfigFileSnapshot, - ConfigWriteNotification, - GatewayReloadMode, -} from "../config/config.js"; -import { - resolveConfigWriteFollowUp, - shouldAttemptLastKnownGoodRecovery, -} from "../config/config.js"; +import type { ConfigWriteNotification } from "../config/io.js"; import { formatConfigIssueLines } from "../config/issue-format.js"; +import { shouldAttemptLastKnownGoodRecovery } from "../config/recovery-policy.js"; +import { resolveConfigWriteFollowUp } from "../config/runtime-snapshot.js"; +import type { GatewayReloadMode } from "../config/types.gateway.js"; +import type { ConfigFileSnapshot, OpenClawConfig } from "../config/types.openclaw.js"; import { isPlainObject } from "../utils.js"; import { buildGatewayReloadPlan, diff --git a/src/gateway/embeddings-http.ts b/src/gateway/embeddings-http.ts index d3af3746f81..2d56c671cbd 100644 --- a/src/gateway/embeddings-http.ts +++ b/src/gateway/embeddings-http.ts @@ -2,7 +2,7 @@ import { Buffer } from "node:buffer"; import type { IncomingMessage, ServerResponse } from "node:http"; import { resolveAgentDir } from "../agents/agent-scope.js"; import { resolveMemorySearchConfig } from "../agents/memory-search.js"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { formatErrorMessage } from "../infra/errors.js"; import { logWarn } from "../logger.js"; diff --git a/src/gateway/exec-approval-ios-push.ts b/src/gateway/exec-approval-ios-push.ts index 1f925d66d5b..abe58ab494f 100644 --- a/src/gateway/exec-approval-ios-push.ts +++ b/src/gateway/exec-approval-ios-push.ts @@ -1,4 +1,4 @@ -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import { hasEffectivePairedDeviceRole, listDevicePairing, diff --git a/src/gateway/http-auth-utils.ts b/src/gateway/http-auth-utils.ts index 2578f40c32f..47002cb3df9 100644 --- a/src/gateway/http-auth-utils.ts +++ b/src/gateway/http-auth-utils.ts @@ -1,5 +1,5 @@ import type { IncomingMessage, ServerResponse } from "node:http"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { normalizeLowercaseStringOrEmpty, diff --git a/src/gateway/http-utils.ts b/src/gateway/http-utils.ts index 0a217a99a2d..678373ea0dc 100644 --- a/src/gateway/http-utils.ts +++ b/src/gateway/http-utils.ts @@ -7,7 +7,7 @@ import { parseModelRef, resolveDefaultModelForAgent, } from "../agents/model-selection.js"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import { buildAgentMainSessionKey, normalizeAgentId } from "../routing/session-key.js"; import { normalizeLowercaseStringOrEmpty, diff --git a/src/gateway/mcp-http.ts b/src/gateway/mcp-http.ts index 22db91df3ef..d2cc9900931 100644 --- a/src/gateway/mcp-http.ts +++ b/src/gateway/mcp-http.ts @@ -4,7 +4,7 @@ import { type IncomingMessage, type ServerResponse, } from "node:http"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import { isTruthyEnvValue } from "../infra/env.js"; import { formatErrorMessage } from "../infra/errors.js"; import { logDebug, logWarn } from "../logger.js"; diff --git a/src/gateway/models-http.ts b/src/gateway/models-http.ts index 3478d94f43d..b34d11a51be 100644 --- a/src/gateway/models-http.ts +++ b/src/gateway/models-http.ts @@ -1,6 +1,6 @@ import type { IncomingMessage, ServerResponse } from "node:http"; import { listAgentIds, resolveDefaultAgentId } from "../agents/agent-scope.js"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import type { AuthRateLimiter } from "./auth-rate-limit.js"; import type { ResolvedGatewayAuth } from "./auth.js"; import { sendInvalidRequest, sendJson, sendMethodNotAllowed } from "./http-common.js"; diff --git a/src/gateway/server-chat.ts b/src/gateway/server-chat.ts index f79aa231ad9..ef390114ef0 100644 --- a/src/gateway/server-chat.ts +++ b/src/gateway/server-chat.ts @@ -1,6 +1,6 @@ import { DEFAULT_HEARTBEAT_ACK_MAX_CHARS, stripHeartbeatToken } from "../auto-reply/heartbeat.js"; import { normalizeVerboseLevel } from "../auto-reply/thinking.js"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import { type AgentEventPayload, getAgentRunContext } from "../infra/agent-events.js"; import { detectErrorKind, type ErrorKind } from "../infra/errors.js"; import { resolveHeartbeatVisibility } from "../infra/heartbeat-visibility.js"; diff --git a/src/gateway/server-cron.ts b/src/gateway/server-cron.ts index a9487c7699f..cb03603dd57 100644 --- a/src/gateway/server-cron.ts +++ b/src/gateway/server-cron.ts @@ -1,7 +1,7 @@ import { resolveDefaultAgentId } from "../agents/agent-scope.js"; import { cleanupBrowserSessionsForLifecycleEnd } from "../browser-lifecycle-cleanup.js"; import type { CliDeps } from "../cli/deps.types.js"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import { canonicalizeMainSessionAlias, resolveAgentIdFromSessionKey, diff --git a/src/gateway/server-http.ts b/src/gateway/server-http.ts index 26d64999074..cf9cd84a10c 100644 --- a/src/gateway/server-http.ts +++ b/src/gateway/server-http.ts @@ -15,7 +15,7 @@ import { } from "../canvas-host/a2ui.js"; import type { CanvasHostHandler } from "../canvas-host/server.js"; import { resolveBundledChannelGatewayAuthBypassPaths } from "../channels/plugins/gateway-auth-bypass.js"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { createDiagnosticTraceContext, diff --git a/src/gateway/server-methods/nodes.ts b/src/gateway/server-methods/nodes.ts index 4d7e5b72a05..1f85ea6e8f2 100644 --- a/src/gateway/server-methods/nodes.ts +++ b/src/gateway/server-methods/nodes.ts @@ -1,5 +1,5 @@ import { randomUUID } from "node:crypto"; -import { getRuntimeConfig } from "../../config/config.js"; +import { getRuntimeConfig } from "../../config/io.js"; import type { OpenClawConfig } from "../../config/types.openclaw.js"; import { listDevicePairing } from "../../infra/device-pairing.js"; import { formatErrorMessage } from "../../infra/errors.js"; diff --git a/src/gateway/server-model-catalog.ts b/src/gateway/server-model-catalog.ts index 00d622beec9..3f4780f14df 100644 --- a/src/gateway/server-model-catalog.ts +++ b/src/gateway/server-model-catalog.ts @@ -1,21 +1,18 @@ -import { - loadModelCatalog, - type ModelCatalogEntry, - resetModelCatalogCacheForTest, -} from "../agents/model-catalog.js"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; -export type GatewayModelChoice = ModelCatalogEntry; +export type GatewayModelChoice = import("../agents/model-catalog.js").ModelCatalogEntry; // Test-only escape hatch: model catalog is cached at module scope for the // process lifetime, which is fine for the real gateway daemon, but makes // isolated unit tests harder. Keep this intentionally obscure. -export function __resetModelCatalogCacheForTest() { +export async function __resetModelCatalogCacheForTest(): Promise { + const { resetModelCatalogCacheForTest } = await import("../agents/model-catalog.js"); resetModelCatalogCacheForTest(); } export async function loadGatewayModelCatalog(params?: { getConfig?: () => ReturnType; }): Promise { + const { loadModelCatalog } = await import("../agents/model-catalog.js"); return await loadModelCatalog({ config: (params?.getConfig ?? getRuntimeConfig)() }); } diff --git a/src/gateway/server-node-events.runtime.ts b/src/gateway/server-node-events.runtime.ts index cb0c49204c8..01697810d6a 100644 --- a/src/gateway/server-node-events.runtime.ts +++ b/src/gateway/server-node-events.runtime.ts @@ -3,7 +3,7 @@ export { sanitizeInboundSystemTags } from "../auto-reply/reply/inbound-text.js"; export { normalizeChannelId } from "../channels/plugins/index.js"; export { createOutboundSendDeps } from "../cli/outbound-send-deps.js"; export { agentCommandFromIngress } from "../commands/agent.js"; -export { getRuntimeConfig } from "../config/config.js"; +export { getRuntimeConfig } from "../config/io.js"; export { updateSessionStore } from "../config/sessions.js"; export { loadOrCreateDeviceIdentity } from "../infra/device-identity.js"; export { requestHeartbeatNow } from "../infra/heartbeat-wake.js"; diff --git a/src/gateway/server-runtime-config.ts b/src/gateway/server-runtime-config.ts index 5122a0c7198..0d395e11549 100644 --- a/src/gateway/server-runtime-config.ts +++ b/src/gateway/server-runtime-config.ts @@ -2,7 +2,7 @@ import type { GatewayAuthConfig, GatewayBindMode, GatewayTailscaleConfig, -} from "../config/config.js"; +} from "../config/types.gateway.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { assertGatewayAuthConfigured, diff --git a/src/gateway/server-session-key.ts b/src/gateway/server-session-key.ts index d30d651da22..648244cea8f 100644 --- a/src/gateway/server-session-key.ts +++ b/src/gateway/server-session-key.ts @@ -1,4 +1,4 @@ -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import type { SessionEntry } from "../config/sessions.js"; import { getAgentRunContext, registerAgentRunContext } from "../infra/agent-events.js"; import { toAgentRequestSessionKey } from "../routing/session-key.js"; diff --git a/src/gateway/server-startup-config.ts b/src/gateway/server-startup-config.ts index a6c642e30a1..e7957e71167 100644 --- a/src/gateway/server-startup-config.ts +++ b/src/gateway/server-startup-config.ts @@ -1,22 +1,22 @@ import { formatCliCommand } from "../cli/command-format.js"; import { - type ConfigFileSnapshot, - type GatewayAuthConfig, - type GatewayTailscaleConfig, - type OpenClawConfig, - applyConfigOverrides, - isNixMode, readConfigFileSnapshotWithPluginMetadata, recoverConfigFromLastKnownGood, recoverConfigFromJsonRootSuffix, - replaceConfigFile, - isPluginLocalInvalidConfigSnapshot, - shouldAttemptLastKnownGoodRecovery, - validateConfigObjectWithPlugins, -} from "../config/config.js"; +} from "../config/io.js"; import { formatConfigIssueLines } from "../config/issue-format.js"; import { asResolvedSourceConfig, materializeRuntimeConfig } from "../config/materialize.js"; +import { replaceConfigFile } from "../config/mutate.js"; +import { isNixMode } from "../config/paths.js"; import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js"; +import { + isPluginLocalInvalidConfigSnapshot, + shouldAttemptLastKnownGoodRecovery, +} from "../config/recovery-policy.js"; +import { applyConfigOverrides } from "../config/runtime-overrides.js"; +import type { GatewayAuthConfig, GatewayTailscaleConfig } from "../config/types.gateway.js"; +import type { ConfigFileSnapshot, OpenClawConfig } from "../config/types.openclaw.js"; +import { validateConfigObjectWithPlugins } from "../config/validation.js"; import { isTruthyEnvValue } from "../infra/env.js"; import type { PluginMetadataSnapshot } from "../plugins/plugin-metadata-snapshot.js"; import { diff --git a/src/gateway/server.impl.ts b/src/gateway/server.impl.ts index 29cebd5f17e..d724c6a6ed9 100644 --- a/src/gateway/server.impl.ts +++ b/src/gateway/server.impl.ts @@ -6,18 +6,18 @@ import { type ChannelId, listChannelPlugins } from "../channels/plugins/index.js import { createDefaultDeps } from "../cli/deps.js"; import { isRestartEnabled } from "../config/commands.flags.js"; import { - type OpenClawConfig, - applyConfigOverrides, getRuntimeConfig, - isNixMode, promoteConfigSnapshotToLastKnownGood, readConfigFileSnapshot, recoverConfigFromLastKnownGood, registerConfigWriteListener, - replaceConfigFile, -} from "../config/config.js"; +} from "../config/io.js"; +import { replaceConfigFile } from "../config/mutate.js"; +import { isNixMode } from "../config/paths.js"; import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js"; +import { applyConfigOverrides } from "../config/runtime-overrides.js"; import { resolveMainSessionKey } from "../config/sessions.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; import { clearAgentRunContext } from "../infra/agent-events.js"; import { isDiagnosticsEnabled, diff --git a/src/gateway/server.ts b/src/gateway/server.ts index c4a00b3c97b..dd8dfa90efc 100644 --- a/src/gateway/server.ts +++ b/src/gateway/server.ts @@ -13,5 +13,5 @@ export async function startGatewayServer( export async function __resetModelCatalogCacheForTest(): Promise { const mod = await loadServerImpl(); - mod.__resetModelCatalogCacheForTest(); + await mod.__resetModelCatalogCacheForTest(); } diff --git a/src/gateway/server/health-state.ts b/src/gateway/server/health-state.ts index 30891e17a18..b213faa8fc2 100644 --- a/src/gateway/server/health-state.ts +++ b/src/gateway/server/health-state.ts @@ -1,6 +1,7 @@ import { resolveDefaultAgentId } from "../../agents/agent-scope.js"; import { getHealthSnapshot, type HealthSummary } from "../../commands/health.js"; -import { getRuntimeConfig, STATE_DIR, createConfigIO } from "../../config/config.js"; +import { createConfigIO, getRuntimeConfig } from "../../config/io.js"; +import { STATE_DIR } from "../../config/paths.js"; import { resolveMainSessionKey } from "../../config/sessions.js"; import { listSystemPresence } from "../../infra/system-presence.js"; import { getUpdateAvailable } from "../../infra/update-startup.js"; diff --git a/src/gateway/server/hooks.ts b/src/gateway/server/hooks.ts index 9d9767a2f24..c49d43c56af 100644 --- a/src/gateway/server/hooks.ts +++ b/src/gateway/server/hooks.ts @@ -1,7 +1,7 @@ import { randomUUID } from "node:crypto"; import { sanitizeInboundSystemTags } from "../../auto-reply/reply/inbound-text.js"; import type { CliDeps } from "../../cli/deps.types.js"; -import { getRuntimeConfig } from "../../config/config.js"; +import { getRuntimeConfig } from "../../config/io.js"; import { resolveMainSessionKeyFromConfig } from "../../config/sessions.js"; import type { CronJob } from "../../cron/types.js"; import { requestHeartbeatNow } from "../../infra/heartbeat-wake.js"; diff --git a/src/gateway/server/ws-connection/message-handler.ts b/src/gateway/server/ws-connection/message-handler.ts index f5d85d7b982..aa59e55aa5a 100644 --- a/src/gateway/server/ws-connection/message-handler.ts +++ b/src/gateway/server/ws-connection/message-handler.ts @@ -1,7 +1,7 @@ import type { IncomingMessage } from "node:http"; import os from "node:os"; import type { RawData, WebSocket } from "ws"; -import { getRuntimeConfig } from "../../../config/config.js"; +import { getRuntimeConfig } from "../../../config/io.js"; import { getBoundDeviceBootstrapProfile, getDeviceBootstrapTokenProfile, diff --git a/src/gateway/session-kill-http.ts b/src/gateway/session-kill-http.ts index 737d9a891f7..48c37d34df3 100644 --- a/src/gateway/session-kill-http.ts +++ b/src/gateway/session-kill-http.ts @@ -5,7 +5,7 @@ import { resolveSubagentController, } from "../agents/subagent-control.js"; import { getLatestSubagentRunByChildSessionKey } from "../agents/subagent-registry.js"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import { normalizeOptionalString } from "../shared/string-coerce.js"; import type { AuthRateLimiter } from "./auth-rate-limit.js"; import { isLocalDirectRequest, type ResolvedGatewayAuth } from "./auth.js"; diff --git a/src/gateway/session-reset-service.ts b/src/gateway/session-reset-service.ts index cc89b1a9a78..25c13e4445e 100644 --- a/src/gateway/session-reset-service.ts +++ b/src/gateway/session-reset-service.ts @@ -15,7 +15,7 @@ import { buildSessionStartHookPayload, } from "../auto-reply/reply/session-hooks.js"; import { clearSessionResetRuntimeState } from "../auto-reply/reply/session-reset-cleanup.js"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import { snapshotSessionOrigin, type SessionEntry, diff --git a/src/gateway/session-transcript-key.ts b/src/gateway/session-transcript-key.ts index a0476fa5873..a92bd048b04 100644 --- a/src/gateway/session-transcript-key.ts +++ b/src/gateway/session-transcript-key.ts @@ -1,6 +1,6 @@ import fs from "node:fs"; import path from "node:path"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import type { SessionEntry } from "../config/sessions/types.js"; import type { OpenClawConfig } from "../config/types.openclaw.js"; import { normalizeAgentId } from "../routing/session-key.js"; diff --git a/src/gateway/session-utils.ts b/src/gateway/session-utils.ts index 0a588fd2b79..212b9cf1e29 100644 --- a/src/gateway/session-utils.ts +++ b/src/gateway/session-utils.ts @@ -34,7 +34,7 @@ import { shouldKeepSubagentRunChildLink, } from "../agents/subagent-run-liveness.js"; import { listThinkingLevelOptions } from "../auto-reply/thinking.js"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import { resolveAgentModelFallbackValues } from "../config/model-input.js"; import { resolveStateDir } from "../config/paths.js"; import { diff --git a/src/gateway/sessions-history-http.ts b/src/gateway/sessions-history-http.ts index 17251a8394a..e75a0262837 100644 --- a/src/gateway/sessions-history-http.ts +++ b/src/gateway/sessions-history-http.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import type { IncomingMessage, ServerResponse } from "node:http"; import path from "node:path"; -import { getRuntimeConfig } from "../config/config.js"; +import { getRuntimeConfig } from "../config/io.js"; import { loadSessionStore } from "../config/sessions.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; import { onSessionTranscriptUpdate } from "../sessions/transcript-events.js"; diff --git a/src/gateway/startup-auth.ts b/src/gateway/startup-auth.ts index b3dbfedd394..20fa35b67f2 100644 --- a/src/gateway/startup-auth.ts +++ b/src/gateway/startup-auth.ts @@ -1,10 +1,7 @@ import crypto from "node:crypto"; -import type { - GatewayAuthConfig, - GatewayTailscaleConfig, - OpenClawConfig, -} from "../config/config.js"; -import { replaceConfigFile } from "../config/config.js"; +import { replaceConfigFile } from "../config/mutate.js"; +import type { GatewayAuthConfig, GatewayTailscaleConfig } from "../config/types.gateway.js"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; import { normalizeOptionalString } from "../shared/string-coerce.js"; import { hasConfiguredGatewayAuthSecretInput,