refactor(hooks): centralize live plugin config lookup

This commit is contained in:
Vincent Koc
2026-04-22 13:37:44 -07:00
parent ee63b9ee49
commit db5895fd2a
8 changed files with 81 additions and 36 deletions

View File

@@ -1,2 +1,2 @@
475eafc1885c69203ac690a0ef3c1d1ddf6879d904a6980cf5f172c29ed70868 plugin-sdk-api-baseline.json
906bb0b167b155d2f766bd13f720c8c6ed8f349769d39f57ff5070045b96ef4c plugin-sdk-api-baseline.jsonl
452cf5257df597bb0062c4478aca3afdbda6909fbaaf9ade214c27e8885935b1 plugin-sdk-api-baseline.json
30117bdbd814978ad04be54e80b72385cabb0c726de1abcbad319c9d0b3ed101 plugin-sdk-api-baseline.jsonl

View File

@@ -192,7 +192,7 @@ explicitly promotes one as public.
| `plugin-sdk/process-runtime` | Process exec helpers |
| `plugin-sdk/cli-runtime` | CLI formatting, wait, and version helpers |
| `plugin-sdk/gateway-runtime` | Gateway client and channel-status patch helpers |
| `plugin-sdk/config-runtime` | Config load/write helpers |
| `plugin-sdk/config-runtime` | Config load/write helpers and plugin-config lookup helpers |
| `plugin-sdk/telegram-command-config` | Telegram command-name/description normalization and duplicate/conflict checks, even when the bundled Telegram contract surface is unavailable |
| `plugin-sdk/text-autolink-runtime` | File-reference autolink detection without the broad text-runtime barrel |
| `plugin-sdk/approval-runtime` | Exec/plugin approval helpers, approval-capability builders, auth/profile helpers, native routing/runtime helpers |

View File

@@ -9,6 +9,7 @@ import {
resolveAgentWorkspaceDir,
} from "openclaw/plugin-sdk/agent-runtime";
import {
resolvePluginConfigObject,
resolveSessionStoreEntry,
updateSessionStore,
type OpenClawConfig,
@@ -573,14 +574,10 @@ function isActiveMemoryGloballyEnabled(cfg: OpenClawConfig): boolean {
if (entry?.enabled === false) {
return false;
}
const pluginConfig = asRecord(entry?.config);
const pluginConfig = resolvePluginConfigObject(cfg, "active-memory");
return pluginConfig?.enabled !== false;
}
function resolveActiveMemoryPluginConfigFromConfig(cfg: OpenClawConfig): unknown {
return asRecord(cfg.plugins?.entries?.["active-memory"])?.config;
}
function updateActiveMemoryGlobalEnabledInConfig(
cfg: OpenClawConfig,
enabled: boolean,
@@ -1887,7 +1884,7 @@ export default definePluginEntry({
warnDeprecatedModelFallbackPolicy(api.pluginConfig);
const refreshLiveConfigFromRuntime = () => {
const livePluginConfig =
resolveActiveMemoryPluginConfigFromConfig(api.runtime.config.loadConfig()) ??
resolvePluginConfigObject(api.runtime.config.loadConfig(), "active-memory") ??
api.pluginConfig;
config = normalizePluginConfig(livePluginConfig);
warnDeprecatedModelFallbackPolicy(livePluginConfig);

View File

@@ -10,6 +10,7 @@ import { randomUUID } from "node:crypto";
import type * as LanceDB from "@lancedb/lancedb";
import { Type } from "@sinclair/typebox";
import OpenAI from "openai";
import { resolvePluginConfigObject } from "openclaw/plugin-sdk/config-runtime";
import { ensureGlobalUndiciEnvProxyDispatcher } from "openclaw/plugin-sdk/runtime-env";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { definePluginEntry, type OpenClawPluginApi } from "./api.js";
@@ -311,9 +312,7 @@ export default definePluginEntry({
const embeddings = new Embeddings(apiKey, model, baseUrl, dimensions);
const resolveCurrentHookConfig = () => {
const runtimeConfig = api.runtime.config?.loadConfig?.();
const runtimePlugins = asRecord(asRecord(runtimeConfig)?.plugins);
const runtimeEntries = asRecord(runtimePlugins?.entries);
const runtimePluginConfig = asRecord(runtimeEntries?.["memory-lancedb"])?.config;
const runtimePluginConfig = resolvePluginConfigObject(runtimeConfig, "memory-lancedb");
if (!runtimePluginConfig) {
return cfg;
}

View File

@@ -1,3 +1,4 @@
import { resolvePluginConfigObject } from "openclaw/plugin-sdk/config-runtime";
import { definePluginEntry, resolveDefaultAgentId } from "./api.js";
import { resolveConfig } from "./src/config.js";
import { buildWorkshopGuidance } from "./src/prompt.js";
@@ -6,12 +7,6 @@ import { createProposalFromMessages } from "./src/signals.js";
import { createSkillWorkshopTool } from "./src/tool.js";
import { applyOrStoreProposal, createStoreForContext } from "./src/workshop.js";
function asRecord(value: unknown): Record<string, unknown> | undefined {
return value && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: undefined;
}
export default definePluginEntry({
id: "skill-workshop",
name: "Skill Workshop",
@@ -24,10 +19,8 @@ export default definePluginEntry({
}
const resolveCurrentConfig = () => {
const runtimeConfig = api.runtime.config?.loadConfig?.();
const runtimePlugins = asRecord(asRecord(runtimeConfig)?.plugins);
const runtimeEntries = asRecord(runtimePlugins?.entries);
const runtimePluginConfig =
asRecord(runtimeEntries?.["skill-workshop"])?.config ?? api.pluginConfig;
resolvePluginConfigObject(runtimeConfig, "skill-workshop") ?? api.pluginConfig;
return resolveConfig(runtimePluginConfig);
};

View File

@@ -1,3 +1,4 @@
import { resolvePluginConfigObject } from "openclaw/plugin-sdk/config-runtime";
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import {
definePluginEntry,
@@ -72,20 +73,6 @@ function resolveOwnershipAgent(config: OpenClawConfig): { id: string; name: stri
return { id, name };
}
function asRecord(value: unknown): Record<string, unknown> | undefined {
return value && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: undefined;
}
function resolveThreadOwnershipPluginConfigFromConfig(
config: OpenClawConfig,
): ThreadOwnershipConfig | undefined {
return asRecord(asRecord(config.plugins?.entries)?.["thread-ownership"])?.config as
| ThreadOwnershipConfig
| undefined;
}
export default definePluginEntry({
id: "thread-ownership",
name: "Thread Ownership",
@@ -94,7 +81,7 @@ export default definePluginEntry({
const resolveCurrentState = () => {
const currentConfig = api.runtime.config?.loadConfig?.() ?? api.config;
const pluginCfg =
resolveThreadOwnershipPluginConfigFromConfig(currentConfig) ||
resolvePluginConfigObject(currentConfig, "thread-ownership") ||
((api.pluginConfig ?? {}) as ThreadOwnershipConfig);
return {
currentConfig,

View File

@@ -0,0 +1,47 @@
import { describe, expect, it } from "vitest";
import { resolvePluginConfigObject, type OpenClawConfig } from "./config-runtime.js";
describe("resolvePluginConfigObject", () => {
it("returns the plugin config object for a configured plugin entry", () => {
const config = {
plugins: {
entries: {
"demo-plugin": {
enabled: true,
config: {
enabled: false,
mode: "strict",
},
},
},
},
} as OpenClawConfig;
expect(resolvePluginConfigObject(config, "demo-plugin")).toEqual({
enabled: false,
mode: "strict",
});
});
it("returns undefined for missing or non-object plugin configs", () => {
const config = {
plugins: {
entries: {
"demo-plugin": {
enabled: true,
config: "bad-shape",
},
"array-plugin": {
enabled: true,
config: ["bad-shape"],
},
},
},
} as OpenClawConfig;
expect(resolvePluginConfigObject(config, "missing-plugin")).toBeUndefined();
expect(resolvePluginConfigObject(config, "demo-plugin")).toBeUndefined();
expect(resolvePluginConfigObject(config, "array-plugin")).toBeUndefined();
expect(resolvePluginConfigObject(undefined, "demo-plugin")).toBeUndefined();
});
});

View File

@@ -12,6 +12,28 @@ export function requireRuntimeConfig(config: OpenClawConfig, context: string): O
);
}
export function resolvePluginConfigObject(
config: OpenClawConfig | undefined,
pluginId: string,
): Record<string, unknown> | undefined {
const plugins =
config?.plugins && typeof config.plugins === "object" && !Array.isArray(config.plugins)
? (config.plugins as Record<string, unknown>)
: undefined;
const entries =
plugins?.entries && typeof plugins.entries === "object" && !Array.isArray(plugins.entries)
? (plugins.entries as Record<string, unknown>)
: undefined;
const entry = entries?.[pluginId];
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
return undefined;
}
const pluginConfig = (entry as { config?: unknown }).config;
return pluginConfig && typeof pluginConfig === "object" && !Array.isArray(pluginConfig)
? (pluginConfig as Record<string, unknown>)
: undefined;
}
export { resolveDefaultAgentId } from "../agents/agent-scope.js";
export {
clearRuntimeConfigSnapshot,