mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:50:43 +00:00
fix(plugins): preload cli backend runtime owners
This commit is contained in:
@@ -82,10 +82,7 @@ Docs: https://docs.openclaw.ai
|
||||
- UI/Windows: quote resolved pnpm `.cmd` launcher paths before spawning UI install/build/test commands so Node installs under `C:\Program Files` no longer fail as `C:\Program`. Fixes #45275. Thanks @Kobevictor, @stoppieboy, and @iubns.
|
||||
- Codex/agent: translate `--thinking minimal` to `low` for modern Codex models (gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.2) at request build time so the first turn is accepted instead of paying a wasted call + retry-with-low fallback. Older Codex models still receive `minimal` directly. Fixes #71946. Thanks @hclsys.
|
||||
- Plugins/uninstall: remove tracked plugin files from their recorded managed extensions root even when the current state directory points somewhere else, so `openclaw plugins uninstall --force` does not leave the plugin discoverable. Thanks @shakkernerd.
|
||||
- Agents/runtime: add `agentRuntime.id` as the canonical config key, migrate
|
||||
legacy runtime-policy configs with `openclaw doctor --fix`, and route
|
||||
canonical Anthropic models through `claude-cli` without passing CLI backend
|
||||
aliases to embedded harness selection. Fixes #71957. Thanks @WolvenRA.
|
||||
- Agents/runtime: add `agentRuntime.id` as the canonical config key, migrate legacy runtime-policy configs with `openclaw doctor --fix`, route canonical Anthropic models through `claude-cli` without passing CLI backend aliases to embedded harness selection, and load CLI backend owner plugins before channel startup. Fixes #71957. Thanks @WolvenRA.
|
||||
- CLI/update: guard Windows scheduled-task stops by state and timeout so auto-update restart cannot hang indefinitely on `schtasks /End` before stale-listener cleanup. Fixes #69970. Thanks @yangswld and @sherlock-huang.
|
||||
- Windows install/Lobster: execute `pnpm.exe` directly when `npm_execpath` points at the native pnpm binary, add an installed-package fallback for the Lobster embedded runtime, and include the Lobster runner regression test in Windows CI. Fixes #69456. Thanks @igormf.
|
||||
- Gateway/install: refresh loaded gateway service installs when the current service embeds stale gateway auth instead of returning already-installed, avoiding LaunchAgent token-mismatch loops after token rotation. Fixes #70752. Thanks @hyspacex.
|
||||
|
||||
@@ -231,6 +231,9 @@ Prefer the narrowest metadata that already describes ownership. Use
|
||||
`providers`, `channels`, `commandAliases`, setup descriptors, or `contracts`
|
||||
when those fields express the relationship. Use `activation` for extra planner
|
||||
hints that cannot be represented by those ownership fields.
|
||||
Use top-level `cliBackends` for CLI runtime aliases such as `claude-cli`,
|
||||
`codex-cli`, or `google-gemini-cli`; `activation.onAgentHarnesses` is only for
|
||||
embedded agent harness ids that do not already have an ownership field.
|
||||
|
||||
This block is metadata only. It does not register runtime behavior, and it does
|
||||
not replace `register(...)`, `setupEntry`, or other runtime/plugin entrypoints.
|
||||
@@ -250,18 +253,21 @@ change correctness while legacy manifest ownership fallbacks still exist.
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Required | Type | What it means |
|
||||
| ---------------- | -------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
||||
| `onProviders` | No | `string[]` | Provider ids that should include this plugin in activation/load plans. |
|
||||
| `onCommands` | No | `string[]` | Command ids that should include this plugin in activation/load plans. |
|
||||
| `onChannels` | No | `string[]` | Channel ids that should include this plugin in activation/load plans. |
|
||||
| `onRoutes` | No | `string[]` | Route kinds that should include this plugin in activation/load plans. |
|
||||
| `onCapabilities` | No | `Array<"provider" \| "channel" \| "tool" \| "hook">` | Broad capability hints used by control-plane activation planning. Prefer narrower fields when possible. |
|
||||
| Field | Required | Type | What it means |
|
||||
| ------------------ | -------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `onProviders` | No | `string[]` | Provider ids that should include this plugin in activation/load plans. |
|
||||
| `onAgentHarnesses` | No | `string[]` | Embedded agent harness runtime ids that should include this plugin in activation/load plans. Use top-level `cliBackends` for CLI backend aliases. |
|
||||
| `onCommands` | No | `string[]` | Command ids that should include this plugin in activation/load plans. |
|
||||
| `onChannels` | No | `string[]` | Channel ids that should include this plugin in activation/load plans. |
|
||||
| `onRoutes` | No | `string[]` | Route kinds that should include this plugin in activation/load plans. |
|
||||
| `onCapabilities` | No | `Array<"provider" \| "channel" \| "tool" \| "hook">` | Broad capability hints used by control-plane activation planning. Prefer narrower fields when possible. |
|
||||
|
||||
Current live consumers:
|
||||
|
||||
- command-triggered CLI planning falls back to legacy
|
||||
`commandAliases[].cliCommand` or `commandAliases[].name`
|
||||
- agent-runtime startup planning uses `activation.onAgentHarnesses` for
|
||||
embedded harnesses and top-level `cliBackends[]` for CLI runtime aliases
|
||||
- channel-triggered setup/channel planning falls back to legacy `channels[]`
|
||||
ownership when explicit channel activation metadata is missing
|
||||
- provider-triggered setup/runtime planning falls back to legacy
|
||||
|
||||
@@ -312,7 +312,7 @@ describe("applyPluginAutoEnable core", () => {
|
||||
expect(result.config.plugins?.entries?.codex?.enabled).toBe(true);
|
||||
expect(result.changes).toEqual([
|
||||
"openai/gpt-5.5 model configured, enabled automatically.",
|
||||
"codex agent harness runtime configured, enabled automatically.",
|
||||
"codex agent runtime configured, enabled automatically.",
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -341,9 +341,38 @@ describe("applyPluginAutoEnable core", () => {
|
||||
});
|
||||
|
||||
expect(result.config.plugins?.entries?.codex?.enabled).toBe(true);
|
||||
expect(result.changes).toContain(
|
||||
"codex agent harness runtime configured, enabled automatically.",
|
||||
);
|
||||
expect(result.changes).toContain("codex agent runtime configured, enabled automatically.");
|
||||
});
|
||||
|
||||
it("auto-enables a CLI backend owner when an agent runtime is configured", () => {
|
||||
const result = applyPluginAutoEnable({
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
agentRuntime: {
|
||||
id: "claude-cli",
|
||||
fallback: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
allow: ["telegram"],
|
||||
},
|
||||
},
|
||||
env,
|
||||
manifestRegistry: makeRegistry([
|
||||
{
|
||||
id: "anthropic",
|
||||
channels: [],
|
||||
providers: ["anthropic"],
|
||||
cliBackends: ["claude-cli"],
|
||||
},
|
||||
]),
|
||||
});
|
||||
|
||||
expect(result.config.plugins?.entries?.anthropic?.enabled).toBe(true);
|
||||
expect(result.config.plugins?.allow).toEqual(["telegram", "anthropic"]);
|
||||
expect(result.changes).toContain("claude-cli agent runtime configured, enabled automatically.");
|
||||
});
|
||||
|
||||
it("auto-enables an opt-in plugin when an agent harness runtime is forced by env", () => {
|
||||
@@ -362,9 +391,7 @@ describe("applyPluginAutoEnable core", () => {
|
||||
});
|
||||
|
||||
expect(result.config.plugins?.entries?.codex?.enabled).toBe(true);
|
||||
expect(result.changes).toContain(
|
||||
"codex agent harness runtime configured, enabled automatically.",
|
||||
);
|
||||
expect(result.changes).toContain("codex agent runtime configured, enabled automatically.");
|
||||
});
|
||||
|
||||
it("skips auto-enable work for configs without channel or plugin-owned surfaces", () => {
|
||||
|
||||
@@ -113,7 +113,7 @@ function resolveAgentHarnessOwnerPluginIds(
|
||||
}
|
||||
return registry.plugins
|
||||
.filter((plugin) =>
|
||||
(plugin.activation?.onAgentHarnesses ?? []).some(
|
||||
[...(plugin.activation?.onAgentHarnesses ?? []), ...(plugin.cliBackends ?? [])].some(
|
||||
(entry) => normalizeOptionalLowercaseString(entry) === normalizedRuntime,
|
||||
),
|
||||
)
|
||||
@@ -476,7 +476,7 @@ export function resolvePluginAutoEnableCandidateReason(
|
||||
case "provider-model-configured":
|
||||
return `${candidate.modelRef} model configured`;
|
||||
case "agent-harness-runtime-configured":
|
||||
return `${candidate.runtime} agent harness runtime configured`;
|
||||
return `${candidate.runtime} agent runtime configured`;
|
||||
case "web-fetch-provider-selected":
|
||||
return `${candidate.providerId} web fetch provider selected`;
|
||||
case "plugin-web-search-configured":
|
||||
|
||||
@@ -64,6 +64,7 @@ export function makeRegistry(
|
||||
modelSupport?: { modelPrefixes?: string[]; modelPatterns?: string[] };
|
||||
contracts?: { webSearchProviders?: string[]; webFetchProviders?: string[]; tools?: string[] };
|
||||
providers?: string[];
|
||||
cliBackends?: string[];
|
||||
configSchema?: Record<string, unknown>;
|
||||
channelConfigs?: Record<string, { schema: Record<string, unknown>; preferOver?: string[] }>;
|
||||
}>,
|
||||
@@ -79,7 +80,7 @@ export function makeRegistry(
|
||||
configSchema: plugin.configSchema,
|
||||
channelConfigs: plugin.channelConfigs,
|
||||
providers: plugin.providers ?? [],
|
||||
cliBackends: [],
|
||||
cliBackends: plugin.cliBackends ?? [],
|
||||
skills: [],
|
||||
hooks: [],
|
||||
origin: "config" as const,
|
||||
|
||||
@@ -96,6 +96,30 @@ function createManifestRegistryFixture() {
|
||||
providers: ["demo-provider"],
|
||||
cliBackends: ["demo-cli"],
|
||||
},
|
||||
{
|
||||
id: "anthropic",
|
||||
channels: [],
|
||||
origin: "bundled",
|
||||
enabledByDefault: true,
|
||||
providers: ["anthropic"],
|
||||
cliBackends: ["claude-cli"],
|
||||
},
|
||||
{
|
||||
id: "openai",
|
||||
channels: [],
|
||||
origin: "bundled",
|
||||
enabledByDefault: true,
|
||||
providers: ["openai", "openai-codex"],
|
||||
cliBackends: ["codex-cli"],
|
||||
},
|
||||
{
|
||||
id: "google",
|
||||
channels: [],
|
||||
origin: "bundled",
|
||||
enabledByDefault: true,
|
||||
providers: ["google", "google-gemini-cli"],
|
||||
cliBackends: ["google-gemini-cli"],
|
||||
},
|
||||
{
|
||||
id: "codex",
|
||||
channels: [],
|
||||
@@ -672,6 +696,52 @@ describe("resolveGatewayStartupPluginIds", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("includes required CLI backend owner plugins when the default runtime is forced", () => {
|
||||
expectStartupPluginIdsCase({
|
||||
config: createStartupConfig({
|
||||
agentRuntimeId: "demo-cli",
|
||||
enabledPluginIds: ["demo-provider-plugin"],
|
||||
}),
|
||||
expected: ["demo-channel", "browser", "demo-provider-plugin"],
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
["claude-cli", "anthropic"],
|
||||
["codex-cli", "openai"],
|
||||
["google-gemini-cli", "google"],
|
||||
] as const)("includes the bundled %s CLI backend owner at startup", (runtime, pluginId) => {
|
||||
expectStartupPluginIdsCase({
|
||||
config: createStartupConfig({
|
||||
agentRuntimeId: runtime,
|
||||
}),
|
||||
expected: ["demo-channel", "browser", pluginId],
|
||||
});
|
||||
});
|
||||
|
||||
it("does not include required CLI backend owner plugins when they are explicitly disabled", () => {
|
||||
expectStartupPluginIdsCase({
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
agentRuntime: {
|
||||
id: "demo-cli",
|
||||
fallback: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
"demo-provider-plugin": {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig,
|
||||
expected: ["demo-channel", "browser"],
|
||||
});
|
||||
});
|
||||
|
||||
it("does not include required agent harness owner plugins when they are explicitly disabled", () => {
|
||||
expectStartupPluginIdsCase({
|
||||
config: {
|
||||
|
||||
@@ -205,7 +205,10 @@ function buildStartupInfo(record: PluginManifestRecord): InstalledPluginStartupI
|
||||
memory: hasKind(record.kind, "memory"),
|
||||
deferConfiguredChannelFullLoadUntilAfterListen:
|
||||
record.startupDeferConfiguredChannelFullLoadUntilAfterListen === true,
|
||||
agentHarnesses: sortUnique(record.activation?.onAgentHarnesses ?? []),
|
||||
agentHarnesses: sortUnique([
|
||||
...(record.activation?.onAgentHarnesses ?? []),
|
||||
...(record.cliBackends ?? []),
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user