diff --git a/plugin/index.ts b/plugin/index.ts index 9e89996..ee9551f 100644 --- a/plugin/index.ts +++ b/plugin/index.ts @@ -128,7 +128,9 @@ function register(api: PluginAPI): void { /** Resolve agent ID from env, config, or fallback. */ function resolveAgentId(): string { if (process.env.AGENT_ID) return process.env.AGENT_ID; - const cfg = api.runtime?.config?.loadConfig?.(); + // Read from cached `api.config` first — see pushMetaToMonitor for why + // the deprecated `api.runtime?.config?.loadConfig?.()` path is heavy. + const cfg = (api as any).config ?? api.runtime?.config?.loadConfig?.(); return cfg?.agents?.list?.[0]?.id ?? cfg?.agents?.defaults?.id ?? 'unknown'; } @@ -184,6 +186,25 @@ function register(api: PluginAPI): void { * Push OpenClaw metadata to the Monitor bridge. * This enriches Monitor heartbeats with OpenClaw version/plugin/agent info. * Failures are non-fatal — Monitor continues to work without this data. + * + * IMPORTANT — read config from the cached `api.config` surface, NOT from + * the deprecated `api.runtime?.config?.loadConfig?.()` path. The + * deprecated path triggers a full plugin-metadata-snapshot rebuild on + * every call: realpathSync walks every plugin's package.json + manifest + * + source paths (lstats up the directory tree), `hashWatchedFiles` + * fingerprints all watched plugin files, and `discoverInDirectory` + * re-scans every `dist/extensions/` dir. On t2 with ~100 plugins + * each rebuild costs ~6-7s of CPU; with this push firing every 30s + * (default reportIntervalSec) the chronic baseline was ~22-25% gateway + * CPU even with zero agent activity (V8 profile 2026-05-27 08:14:00 60s: + * lstat 44.2%, statSync 6.9%, hashWatchedFiles via memo key 1.7%, all + * routed through readPersistedInstalledPluginIndexInstallRecordsSync -> + * discoverInDirectory). Switching to `api.config` reads from the + * already-loaded snapshot cache; the elsewhere-in-this-file pattern was + * already `api.config ?? api.runtime?.config?.loadConfig?.()`. + * + * Same fix is applied to `resolveAgentId` below — that's read once at + * gateway start so the impact is smaller, but it's the same anti-pattern. */ async function pushMetaToMonitor() { const bridgeClient = getBridgeClient(); @@ -191,7 +212,7 @@ function register(api: PluginAPI): void { let agentNames: string[] = []; try { - const cfg = api.runtime?.config?.loadConfig?.(); + const cfg = (api as any).config ?? api.runtime?.config?.loadConfig?.(); const agentsList = cfg?.agents?.list; if (Array.isArray(agentsList)) { agentNames = agentsList