fix(plugins): surface configured runtime plugin doctor warnings (#81674)

Fixes #81326.

Summary:
- Warn from `openclaw plugins doctor` when configured runtime owner plugins are missing, disabled, or blocked.
- Share configured-runtime plugin install mapping with `openclaw doctor --fix`, including ACP/acpx.
- Keep implicit OpenAI Codex preferences quiet to avoid false-positive plugin doctor warnings.

Verification:
- `pnpm test src/agents/harness-runtimes.test.ts src/cli/plugins-cli.list.test.ts src/commands/doctor/shared/missing-configured-plugin-install.test.ts -- --reporter=verbose`
- `pnpm exec oxfmt --check CHANGELOG.md src/agents/harness-runtimes.ts src/agents/harness-runtimes.test.ts src/cli/plugins-cli.runtime.ts src/cli/plugins-cli.list.test.ts src/commands/doctor/shared/configured-runtime-plugin-installs.ts src/commands/doctor/shared/missing-configured-plugin-install.ts`
- `pnpm build:plugin-sdk:dts`
- `codex-review --mode branch`
- Testbox-through-Crabbox `pnpm check:changed`: provider `blacksmith-testbox`, id `tbx_01krt8vte22m7ht6wfss4jkeaa`, Actions run https://github.com/openclaw/openclaw/actions/runs/25983150787, exit 0

Co-authored-by: Zavian Wang <36817799+Zavianx@users.noreply.github.com>
This commit is contained in:
Zavian Wang
2026-05-17 14:13:55 +08:00
committed by GitHub
parent 826c2f4517
commit 9a11e76458
7 changed files with 483 additions and 36 deletions

View File

@@ -12,6 +12,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- CLI/plugins: have `openclaw plugins doctor` warn when a configured runtime needs a missing owner plugin, sharing the same install mapping as `openclaw doctor --fix`. Fixes #81326. (#81674) Thanks @Zavianx.
- Agents/skills: apply the full effective tool policy pipeline to inline `command-dispatch: tool` skill dispatch before owner-only filtering, preserving configured allow, deny, sandbox, sender, group, and subagent restrictions. (#78525)
- Channel accounts: keep top-level default channel accounts visible when named accounts are added alongside default credential material, so mixed legacy/new account configs keep resolving `default` instead of silently dropping it.
- Codex/Telegram: synthesize native Codex tool progress from final turn snapshots so Telegram `/verbose` stays visible when command events arrive only at completion.

View File

@@ -20,6 +20,33 @@ describe("collectConfiguredAgentHarnessRuntimes", () => {
);
});
it("can ignore implicit OpenAI Codex runtime preferences", () => {
const config = {
agents: {
defaults: {
model: "openai/gpt-5.5",
models: {
"openai/gpt-5.4": {},
"anthropic/claude-opus-4-7": {
agentRuntime: { id: "codex" },
},
},
},
},
} as OpenClawConfig;
expect(
collectConfiguredAgentHarnessRuntimes(
config,
{},
{
includeEnvRuntime: false,
includeImplicitRuntimePreferences: false,
},
),
).toEqual(["codex"]);
});
it("requires Codex for selectable per-agent OpenAI models", () => {
const config = {
agents: {

View File

@@ -62,6 +62,7 @@ function parseConfiguredModelRef(
function resolveConfiguredModelHarnessRuntime(params: {
config: OpenClawConfig;
includeImplicitRuntimePreferences: boolean;
modelRef: string;
agentId?: string;
}): string | undefined {
@@ -75,6 +76,9 @@ function resolveConfiguredModelHarnessRuntime(params: {
modelId: parsed.modelId,
agentId: params.agentId,
});
if (!params.includeImplicitRuntimePreferences && policy.runtimeSource === "implicit") {
return undefined;
}
const runtime = normalizeRuntimeId(policy.runtime);
return runtime && runtime !== "auto" && runtime !== "pi" ? runtime : undefined;
}
@@ -115,10 +119,19 @@ function pushConfiguredModelRuntimeIds(config: OpenClawConfig, runtimes: Set<str
}
}
function pushConfiguredAgentModelRuntimeIds(config: OpenClawConfig, runtimes: Set<string>): void {
function pushConfiguredAgentModelRuntimeIds(
config: OpenClawConfig,
runtimes: Set<string>,
includeImplicitRuntimePreferences: boolean,
): void {
const pushModelRefs = (modelRefs: string[], agentId?: string) => {
for (const modelRef of modelRefs) {
const runtime = resolveConfiguredModelHarnessRuntime({ config, modelRef, agentId });
const runtime = resolveConfiguredModelHarnessRuntime({
config,
includeImplicitRuntimePreferences,
modelRef,
agentId,
});
if (runtime) {
runtimes.add(runtime);
}
@@ -169,6 +182,7 @@ function pushLegacyAgentRuntimeIds(config: OpenClawConfig, runtimes: Set<string>
export type ConfiguredAgentHarnessRuntimeOptions = {
includeEnvRuntime?: boolean;
includeImplicitRuntimePreferences?: boolean;
includeLegacyAgentRuntimes?: boolean;
};
@@ -179,6 +193,7 @@ export function collectConfiguredAgentHarnessRuntimes(
): string[] {
const runtimes = new Set<string>();
const includeEnvRuntime = options.includeEnvRuntime ?? true;
const includeImplicitRuntimePreferences = options.includeImplicitRuntimePreferences ?? true;
const includeLegacyAgentRuntimes = options.includeLegacyAgentRuntimes ?? true;
if (includeEnvRuntime) {
@@ -191,7 +206,7 @@ export function collectConfiguredAgentHarnessRuntimes(
if (includeLegacyAgentRuntimes) {
pushLegacyAgentRuntimeIds(config, runtimes);
}
pushConfiguredAgentModelRuntimeIds(config, runtimes);
pushConfiguredAgentModelRuntimeIds(config, runtimes, includeImplicitRuntimePreferences);
return [...runtimes].toSorted((left, right) => left.localeCompare(right));
}

View File

@@ -138,6 +138,263 @@ describe("plugins cli list", () => {
expect(output).not.toContain("No plugin issues detected.");
});
it("reports missing configured Codex runtime plugin in doctor output", async () => {
const sourceConfig = {
agents: {
defaults: {
models: {
"openai/gpt-5.5": {
agentRuntime: { id: "codex" },
},
},
},
},
};
loadConfig.mockReturnValue(sourceConfig);
readConfigFileSnapshot.mockResolvedValueOnce({
path: "/tmp/openclaw-config.json5",
exists: true,
raw: "{}",
parsed: sourceConfig,
resolved: sourceConfig,
sourceConfig,
runtimeConfig: sourceConfig,
config: sourceConfig,
valid: true,
hash: "mock",
issues: [],
warnings: [],
legacyIssues: [],
});
buildPluginDiagnosticsReport.mockReturnValue({
plugins: [],
diagnostics: [],
});
await runPluginsCommand(["plugins", "doctor"]);
const output = runtimeLogs.join("\n");
expect(output).toContain("Plugin configuration:");
expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
expect(output).toContain("openclaw doctor --fix");
expect(output).toContain("openclaw plugins install @openclaw/codex");
expect(output).toContain(
"No plugin install-tree issues detected; configuration warnings remain.",
);
expect(output).not.toContain("No plugin issues detected.");
});
it("reports missing configured ACPX runtime plugin in doctor output", async () => {
const sourceConfig = {
acp: {
backend: "acpx",
},
};
loadConfig.mockReturnValue(sourceConfig);
buildPluginDiagnosticsReport.mockReturnValue({
plugins: [],
diagnostics: [],
});
await runPluginsCommand(["plugins", "doctor"]);
const output = runtimeLogs.join("\n");
expect(output).toContain("Plugin configuration:");
expect(output).toContain('Configured runtime "acpx" requires the ACPX Runtime plugin');
expect(output).toContain("openclaw doctor --fix");
expect(output).toContain("openclaw plugins install @openclaw/acpx");
expect(output).not.toContain("No plugin issues detected.");
});
it("reports blocked configured ACPX runtime with ACP-specific guidance", async () => {
const sourceConfig = {
acp: {
backend: "acpx",
},
plugins: {
entries: {
acpx: { enabled: false },
},
},
};
loadConfig.mockReturnValue(sourceConfig);
buildPluginDiagnosticsReport.mockReturnValue({
plugins: [],
diagnostics: [],
});
await runPluginsCommand(["plugins", "doctor"]);
const output = runtimeLogs.join("\n");
expect(output).toContain('Configured runtime "acpx" requires the ACPX Runtime plugin');
expect(output).toContain("Set plugins.entries.acpx.enabled=true");
expect(output).toContain("disable ACP/acpx in acp config");
expect(output).not.toContain('runtime policy to "pi"');
expect(output).not.toContain("openclaw plugins install @openclaw/acpx");
expect(output).not.toContain("No plugin issues detected.");
});
it("reports disabled configured ACPX runtime with ACP-specific guidance", async () => {
const sourceConfig = {
acp: {
backend: "acpx",
},
};
loadConfig.mockReturnValue(sourceConfig);
buildPluginDiagnosticsReport.mockReturnValue({
plugins: [createPluginRecord({ id: "acpx", enabled: false, status: "disabled" })],
diagnostics: [],
});
await runPluginsCommand(["plugins", "doctor"]);
const output = runtimeLogs.join("\n");
expect(output).toContain('Configured runtime "acpx" requires the ACPX Runtime plugin');
expect(output).toContain('Enable the "acpx" plugin');
expect(output).toContain("disable ACP/acpx in acp config");
expect(output).not.toContain('runtime policy to "pi"');
expect(output).not.toContain("openclaw plugins install @openclaw/acpx");
expect(output).not.toContain("No plugin issues detected.");
});
it("does not report implicit OpenAI Codex preference as configured runtime", async () => {
const sourceConfig = {
agents: {
defaults: {
model: "openai/gpt-5.5",
},
},
};
loadConfig.mockReturnValue(sourceConfig);
buildPluginDiagnosticsReport.mockReturnValue({
plugins: [],
diagnostics: [],
});
await runPluginsCommand(["plugins", "doctor"]);
const output = runtimeLogs.join("\n");
expect(output).not.toContain('Configured runtime "codex"');
expect(output).toContain("No plugin issues detected.");
});
it("does not report configured Codex runtime when the plugin is enabled", async () => {
const sourceConfig = {
agents: {
defaults: {
models: {
"openai/gpt-5.5": {
agentRuntime: { id: "codex" },
},
},
},
},
};
loadConfig.mockReturnValue(sourceConfig);
buildPluginDiagnosticsReport.mockReturnValue({
plugins: [createPluginRecord({ id: "codex" })],
diagnostics: [],
});
await runPluginsCommand(["plugins", "doctor"]);
expect(runtimeLogs).toContain("No plugin issues detected.");
});
it("reports configured Codex runtime when the plugin record is disabled", async () => {
const sourceConfig = {
agents: {
defaults: {
models: {
"openai/gpt-5.5": {
agentRuntime: { id: "codex" },
},
},
},
},
};
loadConfig.mockReturnValue(sourceConfig);
buildPluginDiagnosticsReport.mockReturnValue({
plugins: [createPluginRecord({ id: "codex", enabled: false, status: "disabled" })],
diagnostics: [],
});
await runPluginsCommand(["plugins", "doctor"]);
const output = runtimeLogs.join("\n");
expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
expect(output).toContain('but "codex" is disabled');
expect(output).toContain('Enable the "codex" plugin');
expect(output).not.toContain("openclaw plugins install @openclaw/codex");
expect(output).not.toContain("No plugin issues detected.");
});
it("reports blocked configured Codex runtime without install advice", async () => {
const sourceConfig = {
plugins: {
deny: ["codex"],
},
agents: {
defaults: {
models: {
"openai/gpt-5.5": {
agentRuntime: { id: "codex" },
},
},
},
},
};
loadConfig.mockReturnValue(sourceConfig);
buildPluginDiagnosticsReport.mockReturnValue({
plugins: [],
diagnostics: [],
});
await runPluginsCommand(["plugins", "doctor"]);
const output = runtimeLogs.join("\n");
expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
expect(output).toContain('but "codex" is blocked by plugin configuration');
expect(output).toContain('Remove "codex" from plugins.deny');
expect(output).not.toContain('Run "openclaw doctor --fix" to install');
expect(output).not.toContain("openclaw plugins install @openclaw/codex");
expect(output).not.toContain("No plugin issues detected.");
});
it("reports disabled configured Codex runtime entry without install advice", async () => {
const sourceConfig = {
plugins: {
entries: {
codex: { enabled: false },
},
},
agents: {
defaults: {
models: {
"openai/gpt-5.5": {
agentRuntime: { id: "codex" },
},
},
},
},
};
loadConfig.mockReturnValue(sourceConfig);
buildPluginDiagnosticsReport.mockReturnValue({
plugins: [],
diagnostics: [],
});
await runPluginsCommand(["plugins", "doctor"]);
const output = runtimeLogs.join("\n");
expect(output).toContain('Configured runtime "codex" requires the Codex plugin');
expect(output).toContain('but "codex" is blocked by plugin configuration');
expect(output).toContain("Set plugins.entries.codex.enabled=true");
expect(output).not.toContain('Run "openclaw doctor --fix" to install');
expect(output).not.toContain("openclaw plugins install @openclaw/codex");
expect(output).not.toContain("No plugin issues detected.");
});
it("reports config-selected plugin source shadowing in doctor output", async () => {
buildPluginDiagnosticsReport.mockReturnValue({
plugins: [

View File

@@ -1,3 +1,7 @@
import {
collectConfiguredRuntimePluginIds,
resolveConfiguredRuntimePluginInstallCandidate,
} from "../commands/doctor/shared/configured-runtime-plugin-installs.js";
import {
assertConfigWriteAllowedInCurrentMode,
getRuntimeConfig,
@@ -67,6 +71,99 @@ function isErroredConfigSelectedShadowDiagnostic(params: {
);
}
function formatConfiguredRuntimePluginInstallSpec(params: {
clawhubSpec?: string;
defaultChoice?: string;
npmSpec?: string;
pluginId: string;
}): string {
const clawhubSpec = params.clawhubSpec?.trim();
const npmSpec = params.npmSpec?.trim();
if (clawhubSpec && params.defaultChoice !== "npm") {
return clawhubSpec;
}
return npmSpec ?? clawhubSpec ?? params.pluginId;
}
function pluginIdListIncludes(list: readonly string[] | undefined, pluginId: string): boolean {
return Array.isArray(list) && list.some((entry) => entry.trim() === pluginId);
}
function formatBlockedRuntimePluginGuidance(params: {
cfg: OpenClawConfig;
pluginId: string;
}): string | undefined {
const pluginId = params.pluginId;
const alternative =
pluginId === "acpx" ? "disable ACP/acpx in acp config" : 'change the runtime policy to "pi"';
if (params.cfg.plugins?.enabled === false) {
return `Enable plugin loading and the "${pluginId}" plugin, or ${alternative}.`;
}
if (pluginIdListIncludes(params.cfg.plugins?.deny, pluginId)) {
return `Remove "${pluginId}" from plugins.deny and enable the "${pluginId}" plugin, or ${alternative}.`;
}
if (params.cfg.plugins?.entries?.[pluginId]?.enabled === false) {
return `Set plugins.entries.${pluginId}.enabled=true or remove that disabled entry, or ${alternative}.`;
}
return undefined;
}
function formatDisabledRuntimePluginGuidance(params: {
cfg: OpenClawConfig;
pluginId: string;
}): string {
const allow = params.cfg.plugins?.allow;
const alternative =
params.pluginId === "acpx"
? "disable ACP/acpx in acp config"
: 'change the runtime policy to "pi"';
if (Array.isArray(allow) && allow.length > 0 && !allow.includes(params.pluginId)) {
return `Add "${params.pluginId}" to plugins.allow and enable the plugin, or ${alternative}.`;
}
return `Enable the "${params.pluginId}" plugin, or ${alternative}.`;
}
function collectConfiguredRuntimePluginWarnings(params: {
cfg: OpenClawConfig;
env: NodeJS.ProcessEnv;
plugins: readonly { enabled?: boolean; id: string; status?: string }[];
}): string[] {
const enabledPluginIds = new Set(
params.plugins
.filter((plugin) => plugin.enabled !== false && plugin.status !== "disabled")
.map((plugin) => plugin.id),
);
return collectConfiguredRuntimePluginIds(params.cfg, params.env, {
includeEnvRuntime: false,
includeImplicitRuntimePreferences: false,
includeLegacyAgentRuntimes: false,
}).flatMap((runtimeId) => {
const candidate = resolveConfiguredRuntimePluginInstallCandidate(runtimeId);
if (!candidate || enabledPluginIds.has(runtimeId)) {
return [];
}
const disabledPluginRecord = params.plugins.find((plugin) => plugin.id === runtimeId);
const blockedGuidance = formatBlockedRuntimePluginGuidance({
cfg: params.cfg,
pluginId: runtimeId,
});
if (blockedGuidance) {
return [
`- Configured runtime "${runtimeId}" requires the ${candidate.label} plugin, but "${runtimeId}" is blocked by plugin configuration. ${blockedGuidance}`,
];
}
if (disabledPluginRecord) {
return [
`- Configured runtime "${runtimeId}" requires the ${candidate.label} plugin, but "${runtimeId}" is disabled. ${formatDisabledRuntimePluginGuidance({ cfg: params.cfg, pluginId: runtimeId })}`,
];
}
const installSpec = formatConfiguredRuntimePluginInstallSpec(candidate);
return [
`- Configured runtime "${runtimeId}" requires the ${candidate.label} plugin, but no enabled "${runtimeId}" plugin was found. Run "openclaw doctor --fix" to install ${installSpec}, or install it manually with "openclaw plugins install ${installSpec}".`,
];
});
}
export async function runPluginsEnableCommand(id: string): Promise<void> {
assertConfigWriteAllowedInCurrentMode();
@@ -240,10 +337,16 @@ export async function runPluginsDoctorCommand(): Promise<void> {
doctorFixCommand: "openclaw doctor --fix",
autoRepairBlocked: isStalePluginAutoRepairBlocked(sourceCfg ?? cfg, process.env),
});
const configuredRuntimePluginWarnings = collectConfiguredRuntimePluginWarnings({
cfg: sourceCfg ?? cfg,
env: process.env,
plugins: report.plugins,
});
const hasInstallTreeIssues =
errors.length > 0 || diags.length > 0 || shadowed.length > 0 || compatibility.length > 0;
const pluginConfigWarnings = [...stalePluginConfigWarnings, ...configuredRuntimePluginWarnings];
if (!hasInstallTreeIssues && stalePluginConfigWarnings.length === 0) {
if (!hasInstallTreeIssues && pluginConfigWarnings.length === 0) {
defaultRuntime.log("No plugin issues detected.");
return;
}
@@ -301,14 +404,14 @@ export async function runPluginsDoctorCommand(): Promise<void> {
lines.push(`- ${formatPluginCompatibilityNotice(notice)} [${marker}]`);
}
}
if (stalePluginConfigWarnings.length > 0) {
if (pluginConfigWarnings.length > 0) {
if (lines.length > 0) {
lines.push("");
}
lines.push(theme.warn("Plugin configuration:"));
lines.push(...stalePluginConfigWarnings);
lines.push(...pluginConfigWarnings);
}
if (!hasInstallTreeIssues && stalePluginConfigWarnings.length > 0) {
if (!hasInstallTreeIssues && pluginConfigWarnings.length > 0) {
if (lines.length > 0) {
lines.push("");
}

View File

@@ -0,0 +1,67 @@
import {
collectConfiguredAgentHarnessRuntimes,
type ConfiguredAgentHarnessRuntimeOptions,
} from "../../../agents/harness-runtimes.js";
import type { OpenClawConfig } from "../../../config/types.openclaw.js";
import type { PluginPackageInstall } from "../../../plugins/manifest.js";
export type ConfiguredRuntimePluginInstallCandidate = {
pluginId: string;
label: string;
npmSpec?: string;
clawhubSpec?: string;
trustedSourceLinkedOfficialInstall?: boolean;
defaultChoice?: PluginPackageInstall["defaultChoice"];
};
export const CONFIGURED_RUNTIME_PLUGIN_INSTALL_CANDIDATES: readonly ConfiguredRuntimePluginInstallCandidate[] =
[
{
pluginId: "acpx",
label: "ACPX Runtime",
npmSpec: "@openclaw/acpx",
trustedSourceLinkedOfficialInstall: true,
},
// Runtime-only configs do not have a provider/channel integration catalog entry.
{
pluginId: "codex",
label: "Codex",
npmSpec: "@openclaw/codex",
trustedSourceLinkedOfficialInstall: true,
},
];
export function resolveConfiguredRuntimePluginInstallCandidate(
runtimeId: string,
): ConfiguredRuntimePluginInstallCandidate | undefined {
return CONFIGURED_RUNTIME_PLUGIN_INSTALL_CANDIDATES.find(
(candidate) => candidate.pluginId === runtimeId,
);
}
function asRecord(value: unknown): Record<string, unknown> | undefined {
return value && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: undefined;
}
function acpxRuntimeIsConfigured(cfg: OpenClawConfig): boolean {
const acp = asRecord(cfg.acp);
const backend = typeof acp?.backend === "string" ? acp.backend.trim().toLowerCase() : "";
return (
(backend === "acpx" || acp?.enabled === true || asRecord(acp?.dispatch)?.enabled === true) &&
(!backend || backend === "acpx")
);
}
export function collectConfiguredRuntimePluginIds(
cfg: OpenClawConfig,
env: NodeJS.ProcessEnv,
options?: ConfiguredAgentHarnessRuntimeOptions,
): string[] {
const ids = new Set(collectConfiguredAgentHarnessRuntimes(cfg, env, options));
if (acpxRuntimeIsConfigured(cfg)) {
ids.add("acpx");
}
return [...ids].toSorted((left, right) => left.localeCompare(right));
}

View File

@@ -1,7 +1,6 @@
import { existsSync } from "node:fs";
import { rm } from "node:fs/promises";
import path from "node:path";
import { collectConfiguredAgentHarnessRuntimes } from "../../../agents/harness-runtimes.js";
import {
listExplicitlyDisabledChannelIdsForConfig,
listPotentialConfiguredChannelIds,
@@ -49,6 +48,10 @@ import { resolveWebSearchInstallCatalogEntry } from "../../../plugins/web-search
import { normalizeOptionalLowercaseString } from "../../../shared/string-coerce.js";
import { resolveUserPath } from "../../../utils.js";
import { VERSION } from "../../../version.js";
import {
collectConfiguredRuntimePluginIds,
CONFIGURED_RUNTIME_PLUGIN_INSTALL_CANDIDATES,
} from "./configured-runtime-plugin-installs.js";
import { asObjectRecord } from "./object.js";
import {
isLegacyPackageUpdateDoctorPass,
@@ -70,22 +73,6 @@ type BundledPluginPackageDescriptor = {
packageName?: string;
};
const RUNTIME_PLUGIN_INSTALL_CANDIDATES: readonly DownloadableInstallCandidate[] = [
{
pluginId: "acpx",
label: "ACPX Runtime",
npmSpec: "@openclaw/acpx",
trustedSourceLinkedOfficialInstall: true,
},
// Runtime-only configs do not have a provider/channel integration catalog entry.
{
pluginId: "codex",
label: "Codex",
npmSpec: "@openclaw/codex",
trustedSourceLinkedOfficialInstall: true,
},
];
const MISSING_CHANNEL_CONFIG_DESCRIPTOR_DIAGNOSTIC = "without channelConfigs metadata";
const REPAIRABLE_PACKAGE_ENTRY_DIAGNOSTIC_MARKERS = [
"extension entry escapes package directory",
@@ -123,7 +110,7 @@ function addConfiguredAgentRuntimePluginIds(
cfg: OpenClawConfig,
env?: NodeJS.ProcessEnv,
): void {
for (const runtime of collectConfiguredAgentHarnessRuntimes(cfg, env ?? process.env, {
for (const runtime of collectConfiguredRuntimePluginIds(cfg, env ?? process.env, {
includeEnvRuntime: false,
includeLegacyAgentRuntimes: false,
})) {
@@ -151,16 +138,6 @@ function collectConfiguredPluginIds(cfg: OpenClawConfig, env?: NodeJS.ProcessEnv
ids.add(installEntry.pluginId);
}
}
const acp = asObjectRecord(cfg.acp);
const acpBackend = typeof acp?.backend === "string" ? acp.backend.trim().toLowerCase() : "";
if (
(acpBackend === "acpx" ||
acp?.enabled === true ||
asObjectRecord(acp?.dispatch)?.enabled === true) &&
(!acpBackend || acpBackend === "acpx")
) {
ids.add("acpx");
}
addConfiguredAgentRuntimePluginIds(ids, cfg, env);
return ids;
}
@@ -364,7 +341,7 @@ function collectDownloadableInstallCandidates(params: {
});
}
for (const entry of RUNTIME_PLUGIN_INSTALL_CANDIDATES) {
for (const entry of CONFIGURED_RUNTIME_PLUGIN_INSTALL_CANDIDATES) {
if (!configuredPluginIds.has(entry.pluginId) && !params.missingPluginIds.has(entry.pluginId)) {
continue;
}