mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 15:47:28 +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.
|
- 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.
|
- 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.
|
- 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
|
- 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.
|
||||||
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.
|
|
||||||
- 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.
|
- 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.
|
- 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.
|
- 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`
|
`providers`, `channels`, `commandAliases`, setup descriptors, or `contracts`
|
||||||
when those fields express the relationship. Use `activation` for extra planner
|
when those fields express the relationship. Use `activation` for extra planner
|
||||||
hints that cannot be represented by those ownership fields.
|
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
|
This block is metadata only. It does not register runtime behavior, and it does
|
||||||
not replace `register(...)`, `setupEntry`, or other runtime/plugin entrypoints.
|
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 |
|
| Field | Required | Type | What it means |
|
||||||
| ---------------- | -------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
| ------------------ | -------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `onProviders` | No | `string[]` | Provider ids that should include this plugin in activation/load plans. |
|
| `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. |
|
| `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. |
|
||||||
| `onChannels` | No | `string[]` | Channel ids that should include this plugin in activation/load plans. |
|
| `onCommands` | No | `string[]` | Command ids that should include this plugin in activation/load plans. |
|
||||||
| `onRoutes` | No | `string[]` | Route kinds that should include this plugin in activation/load plans. |
|
| `onChannels` | No | `string[]` | Channel ids 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. |
|
| `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:
|
Current live consumers:
|
||||||
|
|
||||||
- command-triggered CLI planning falls back to legacy
|
- command-triggered CLI planning falls back to legacy
|
||||||
`commandAliases[].cliCommand` or `commandAliases[].name`
|
`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[]`
|
- channel-triggered setup/channel planning falls back to legacy `channels[]`
|
||||||
ownership when explicit channel activation metadata is missing
|
ownership when explicit channel activation metadata is missing
|
||||||
- provider-triggered setup/runtime planning falls back to legacy
|
- 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.config.plugins?.entries?.codex?.enabled).toBe(true);
|
||||||
expect(result.changes).toEqual([
|
expect(result.changes).toEqual([
|
||||||
"openai/gpt-5.5 model configured, enabled automatically.",
|
"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.config.plugins?.entries?.codex?.enabled).toBe(true);
|
||||||
expect(result.changes).toContain(
|
expect(result.changes).toContain("codex agent runtime configured, enabled automatically.");
|
||||||
"codex agent harness 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", () => {
|
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.config.plugins?.entries?.codex?.enabled).toBe(true);
|
||||||
expect(result.changes).toContain(
|
expect(result.changes).toContain("codex agent runtime configured, enabled automatically.");
|
||||||
"codex agent harness runtime configured, enabled automatically.",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("skips auto-enable work for configs without channel or plugin-owned surfaces", () => {
|
it("skips auto-enable work for configs without channel or plugin-owned surfaces", () => {
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ function resolveAgentHarnessOwnerPluginIds(
|
|||||||
}
|
}
|
||||||
return registry.plugins
|
return registry.plugins
|
||||||
.filter((plugin) =>
|
.filter((plugin) =>
|
||||||
(plugin.activation?.onAgentHarnesses ?? []).some(
|
[...(plugin.activation?.onAgentHarnesses ?? []), ...(plugin.cliBackends ?? [])].some(
|
||||||
(entry) => normalizeOptionalLowercaseString(entry) === normalizedRuntime,
|
(entry) => normalizeOptionalLowercaseString(entry) === normalizedRuntime,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -476,7 +476,7 @@ export function resolvePluginAutoEnableCandidateReason(
|
|||||||
case "provider-model-configured":
|
case "provider-model-configured":
|
||||||
return `${candidate.modelRef} model configured`;
|
return `${candidate.modelRef} model configured`;
|
||||||
case "agent-harness-runtime-configured":
|
case "agent-harness-runtime-configured":
|
||||||
return `${candidate.runtime} agent harness runtime configured`;
|
return `${candidate.runtime} agent runtime configured`;
|
||||||
case "web-fetch-provider-selected":
|
case "web-fetch-provider-selected":
|
||||||
return `${candidate.providerId} web fetch provider selected`;
|
return `${candidate.providerId} web fetch provider selected`;
|
||||||
case "plugin-web-search-configured":
|
case "plugin-web-search-configured":
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export function makeRegistry(
|
|||||||
modelSupport?: { modelPrefixes?: string[]; modelPatterns?: string[] };
|
modelSupport?: { modelPrefixes?: string[]; modelPatterns?: string[] };
|
||||||
contracts?: { webSearchProviders?: string[]; webFetchProviders?: string[]; tools?: string[] };
|
contracts?: { webSearchProviders?: string[]; webFetchProviders?: string[]; tools?: string[] };
|
||||||
providers?: string[];
|
providers?: string[];
|
||||||
|
cliBackends?: string[];
|
||||||
configSchema?: Record<string, unknown>;
|
configSchema?: Record<string, unknown>;
|
||||||
channelConfigs?: Record<string, { schema: Record<string, unknown>; preferOver?: string[] }>;
|
channelConfigs?: Record<string, { schema: Record<string, unknown>; preferOver?: string[] }>;
|
||||||
}>,
|
}>,
|
||||||
@@ -79,7 +80,7 @@ export function makeRegistry(
|
|||||||
configSchema: plugin.configSchema,
|
configSchema: plugin.configSchema,
|
||||||
channelConfigs: plugin.channelConfigs,
|
channelConfigs: plugin.channelConfigs,
|
||||||
providers: plugin.providers ?? [],
|
providers: plugin.providers ?? [],
|
||||||
cliBackends: [],
|
cliBackends: plugin.cliBackends ?? [],
|
||||||
skills: [],
|
skills: [],
|
||||||
hooks: [],
|
hooks: [],
|
||||||
origin: "config" as const,
|
origin: "config" as const,
|
||||||
|
|||||||
@@ -96,6 +96,30 @@ function createManifestRegistryFixture() {
|
|||||||
providers: ["demo-provider"],
|
providers: ["demo-provider"],
|
||||||
cliBackends: ["demo-cli"],
|
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",
|
id: "codex",
|
||||||
channels: [],
|
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", () => {
|
it("does not include required agent harness owner plugins when they are explicitly disabled", () => {
|
||||||
expectStartupPluginIdsCase({
|
expectStartupPluginIdsCase({
|
||||||
config: {
|
config: {
|
||||||
|
|||||||
@@ -205,7 +205,10 @@ function buildStartupInfo(record: PluginManifestRecord): InstalledPluginStartupI
|
|||||||
memory: hasKind(record.kind, "memory"),
|
memory: hasKind(record.kind, "memory"),
|
||||||
deferConfiguredChannelFullLoadUntilAfterListen:
|
deferConfiguredChannelFullLoadUntilAfterListen:
|
||||||
record.startupDeferConfiguredChannelFullLoadUntilAfterListen === true,
|
record.startupDeferConfiguredChannelFullLoadUntilAfterListen === true,
|
||||||
agentHarnesses: sortUnique(record.activation?.onAgentHarnesses ?? []),
|
agentHarnesses: sortUnique([
|
||||||
|
...(record.activation?.onAgentHarnesses ?? []),
|
||||||
|
...(record.cliBackends ?? []),
|
||||||
|
]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user