feat(plugins): warn on ignored setup runtime (#71253)

* feat(plugins): warn on ignored setup runtime

* fix(plugins): avoid fallback setup runtime diagnostics

* refactor(plugins): clarify setup runtime lookup
This commit is contained in:
Vincent Koc
2026-04-24 14:23:19 -07:00
committed by GitHub
parent 6e985a421d
commit 5b8bd6371c
4 changed files with 87 additions and 5 deletions

View File

@@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
- Plugin hooks: expose first-class run, message, sender, session, and trace correlation fields on message hook contexts and run lifecycle events. Thanks @vincentkoc.
- Plugins/setup: include `setup.providers[].envVars` in generic provider auth/env lookups and warn non-bundled plugins that still rely on deprecated `providerAuthEnvVars` compatibility metadata. Thanks @vincentkoc.
- Plugins/setup: surface manifest provider auth choices directly in provider setup flow before falling back to setup runtime or install-catalog choices. Thanks @vincentkoc.
- Plugins/setup: warn when descriptor-only setup plugins still ship ignored setup runtime entries, keeping `setup.requiresRuntime: false` semantics explicit without breaking existing metadata. Thanks @vincentkoc.
- TUI/dependencies: remove direct `cli-highlight` usage from the OpenClaw TUI code-block renderer, keeping themed code coloring without the extra root dependency. Thanks @vincentkoc.
- Diagnostics/OTEL: export run, model-call, and tool-execution diagnostic lifecycle events as OTEL spans without retaining live span state. Thanks @vincentkoc.
- Providers/Anthropic Vertex: move the Vertex SDK runtime behind the bundled provider plugin so core no longer owns that provider-specific dependency. Thanks @vincentkoc.

View File

@@ -337,9 +337,11 @@ on `setup.providers[].envVars`.
Set `requiresRuntime: false` only when those descriptors are sufficient for the
setup surface. OpenClaw treats explicit `false` as a descriptor-only contract
and will not execute `setup-api` for setup lookup. Omitted `requiresRuntime`
keeps legacy fallback behavior so existing plugins that added descriptors
without the flag do not break.
and will not execute `setup-api` or `openclaw.setupEntry` for setup lookup. If
a descriptor-only plugin still ships one of those setup runtime entries,
OpenClaw reports an additive diagnostic and continues ignoring it. Omitted
`requiresRuntime` keeps legacy fallback behavior so existing plugins that added
descriptors without the flag do not break.
Because setup lookup can execute plugin-owned `setup-api` code, normalized
`setup.providers[].id` and `setup.cliBackends[]` values must stay unique across

View File

@@ -373,6 +373,42 @@ describe("setup-registry getJiti", () => {
expect(resolvePluginSetupProvider({ provider: "openai", env: {} })).toBeUndefined();
expect(resolvePluginSetupCliBackend({ backend: "codex-cli", env: {} })).toBeUndefined();
expect(resolvePluginSetupRegistry({ env: {} })).toEqual({
providers: [],
cliBackends: [],
configMigrations: [],
autoEnableProbes: [],
diagnostics: [
expect.objectContaining({
pluginId: "openai",
code: "setup-descriptor-runtime-disabled",
}),
],
});
expect(mocks.createJiti).not.toHaveBeenCalled();
});
it("does not report descriptor-only diagnostics for bundled setup-api fallback paths", () => {
const parentDir = makeTempDir();
const pluginRoot = path.join(parentDir, "openai");
fs.mkdirSync(pluginRoot);
expect(fs.existsSync(path.join(process.cwd(), "extensions", "openai", "setup-api.ts"))).toBe(
true,
);
mocks.loadPluginManifestRegistry.mockReturnValue({
plugins: [
{
id: "workspace-openai",
rootDir: pluginRoot,
setup: {
providers: [{ id: "workspace-openai" }],
requiresRuntime: false,
},
},
],
diagnostics: [],
});
expect(resolvePluginSetupRegistry({ env: {} })).toEqual({
providers: [],
cliBackends: [],

View File

@@ -47,6 +47,7 @@ type SetupAutoEnableProbeEntry = {
};
export type PluginSetupRegistryDiagnosticCode =
| "setup-descriptor-runtime-disabled"
| "setup-descriptor-provider-missing-runtime"
| "setup-descriptor-provider-runtime-undeclared"
| "setup-descriptor-cli-backend-missing-runtime"
@@ -189,7 +190,10 @@ function buildSetupCliBackendCacheKey(params: {
});
}
function resolveSetupApiPath(rootDir: string): string | null {
function resolveSetupApiPath(
rootDir: string,
options?: { includeBundledSourceFallback?: boolean },
): string | null {
const orderedExtensions = RUNNING_FROM_BUILT_ARTIFACT
? SETUP_API_EXTENSIONS
: ([...SETUP_API_EXTENSIONS.slice(3), ...SETUP_API_EXTENSIONS.slice(0, 3)] as const);
@@ -209,6 +213,10 @@ function resolveSetupApiPath(rootDir: string): string | null {
return direct;
}
if (options?.includeBundledSourceFallback === false) {
return null;
}
const bundledExtensionDir = path.basename(rootDir);
const repoRootCandidates = [path.resolve(path.dirname(CURRENT_MODULE_PATH), "..", "..")];
for (const repoRoot of repoRootCandidates) {
@@ -283,6 +291,19 @@ function resolveRegister(mod: OpenClawPluginModule): {
return {};
}
function resolveLoadableSetupRuntimeSource(record: PluginManifestRecord): string | null {
return record.setupSource ?? resolveSetupApiPath(record.rootDir);
}
function resolveDeclaredSetupRuntimeSource(record: PluginManifestRecord): string | null {
return (
record.setupSource ??
resolveSetupApiPath(record.rootDir, {
includeBundledSourceFallback: false,
})
);
}
function resolveSetupRegistration(record: PluginManifestRecord): {
setupSource: string;
register: (api: ReturnType<typeof buildPluginApi>) => void | Promise<void>;
@@ -290,7 +311,7 @@ function resolveSetupRegistration(record: PluginManifestRecord): {
if (record.setup?.requiresRuntime === false) {
return null;
}
const setupSource = record.setupSource ?? resolveSetupApiPath(record.rootDir);
const setupSource = resolveLoadableSetupRuntimeSource(record);
if (!setupSource) {
return null;
}
@@ -399,6 +420,21 @@ function mapNormalizedIds(ids: readonly string[]): Map<string, string> {
return mapped;
}
function pushDescriptorRuntimeDisabledDiagnostic(params: {
record: PluginManifestRecord;
diagnostics: PluginSetupRegistryDiagnostic[];
}): void {
if (!resolveDeclaredSetupRuntimeSource(params.record)) {
return;
}
params.diagnostics.push({
pluginId: params.record.id,
code: "setup-descriptor-runtime-disabled",
message:
"setup.requiresRuntime is false, so OpenClaw ignored the plugin setup runtime entry. Remove setup-api/openclaw.setupEntry or set requiresRuntime true if setup lookup still needs plugin code.",
});
}
function pushSetupDescriptorDriftDiagnostics(params: {
record: PluginManifestRecord;
providers: readonly ProviderPlugin[];
@@ -504,6 +540,13 @@ export function resolvePluginSetupRegistry(params?: {
if (selectedPluginIds && !selectedPluginIds.has(record.id)) {
continue;
}
if (record.setup?.requiresRuntime === false) {
pushDescriptorRuntimeDisabledDiagnostic({
record,
diagnostics,
});
continue;
}
const setupRegistration = resolveSetupRegistration(record);
if (!setupRegistration) {
continue;