mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 17:20:45 +00:00
perf(plugins): reuse active web provider registry
This commit is contained in:
@@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
- Active Memory: use the configured recall timeout as the blocking prompt-build hook budget by default and move cold-start setup grace behind explicit `setupGraceTimeoutMs` config, so the plugin no longer silently extends 15000 ms configs to 45000 ms on the main lane. Fixes #75843. Thanks @vishutdhar.
|
- Active Memory: use the configured recall timeout as the blocking prompt-build hook budget by default and move cold-start setup grace behind explicit `setupGraceTimeoutMs` config, so the plugin no longer silently extends 15000 ms configs to 45000 ms on the main lane. Fixes #75843. Thanks @vishutdhar.
|
||||||
|
- Plugins/web-provider: reuse the active gateway plugin registry for runtime web provider resolution after deriving the same candidate plugin ids as the loader path, avoiding a redundant `loadOpenClawPlugins` call on every request while preserving origin and scope filters. Fixes #75513. Thanks @jochen.
|
||||||
- Agents/sandbox: preserve existing workspace file modes when sandbox edits atomically replace files, so 0644 files do not collapse to 0600 after Write/Edit/apply_patch. Fixes #44077. Thanks @patosullivan.
|
- Agents/sandbox: preserve existing workspace file modes when sandbox edits atomically replace files, so 0644 files do not collapse to 0600 after Write/Edit/apply_patch. Fixes #44077. Thanks @patosullivan.
|
||||||
- Agents/models: keep legacy CLI runtime model refs such as `claude-cli/*` in the configured allowlist after canonical runtime migration, so cron `payload.model` overrides keep working. Fixes #75753. Thanks @RyanSandoval.
|
- Agents/models: keep legacy CLI runtime model refs such as `claude-cli/*` in the configured allowlist after canonical runtime migration, so cron `payload.model` overrides keep working. Fixes #75753. Thanks @RyanSandoval.
|
||||||
- Codex/app-server: restart the shared Codex app-server client once when it closes during startup thread resume, preserving the existing thread binding instead of retrying `thread/start` on a closed client. Thanks @vincentkoc.
|
- Codex/app-server: restart the shared Codex app-server client once when it closes during startup thread resume, preserving the existing thread binding instead of retrying `thread/start` on a closed client. Thanks @vincentkoc.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const mocks = vi.hoisted(() => ({
|
|||||||
resolveCompatibleRuntimePluginRegistry: vi.fn(),
|
resolveCompatibleRuntimePluginRegistry: vi.fn(),
|
||||||
resolvePluginRegistryLoadCacheKey: vi.fn((options: unknown) => JSON.stringify(options)),
|
resolvePluginRegistryLoadCacheKey: vi.fn((options: unknown) => JSON.stringify(options)),
|
||||||
resolveRuntimePluginRegistry: vi.fn(),
|
resolveRuntimePluginRegistry: vi.fn(),
|
||||||
|
getActivePluginRegistry: vi.fn(() => null),
|
||||||
getActivePluginRegistryWorkspaceDir: vi.fn(() => undefined),
|
getActivePluginRegistryWorkspaceDir: vi.fn(() => undefined),
|
||||||
buildPluginRuntimeLoadOptionsFromValues: vi.fn(
|
buildPluginRuntimeLoadOptionsFromValues: vi.fn(
|
||||||
(_values: unknown, overrides?: Record<string, unknown>) => ({
|
(_values: unknown, overrides?: Record<string, unknown>) => ({
|
||||||
@@ -29,6 +30,7 @@ vi.mock("./loader.js", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("./runtime.js", () => ({
|
vi.mock("./runtime.js", () => ({
|
||||||
|
getActivePluginRegistry: mocks.getActivePluginRegistry,
|
||||||
getActivePluginRegistryWorkspaceDir: mocks.getActivePluginRegistryWorkspaceDir,
|
getActivePluginRegistryWorkspaceDir: mocks.getActivePluginRegistryWorkspaceDir,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -56,6 +58,8 @@ describe("web-provider-runtime-shared", () => {
|
|||||||
JSON.stringify(options),
|
JSON.stringify(options),
|
||||||
);
|
);
|
||||||
mocks.resolveRuntimePluginRegistry.mockReset();
|
mocks.resolveRuntimePluginRegistry.mockReset();
|
||||||
|
mocks.getActivePluginRegistry.mockReset();
|
||||||
|
mocks.getActivePluginRegistry.mockReturnValue(null);
|
||||||
mocks.getActivePluginRegistryWorkspaceDir.mockReset();
|
mocks.getActivePluginRegistryWorkspaceDir.mockReset();
|
||||||
mocks.getActivePluginRegistryWorkspaceDir.mockReturnValue(undefined);
|
mocks.getActivePluginRegistryWorkspaceDir.mockReturnValue(undefined);
|
||||||
mocks.buildPluginRuntimeLoadOptionsFromValues.mockReset();
|
mocks.buildPluginRuntimeLoadOptionsFromValues.mockReset();
|
||||||
@@ -156,4 +160,75 @@ describe("web-provider-runtime-shared", () => {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("reuses the active registry after deriving web provider candidates from resolved config", () => {
|
||||||
|
const activeRegistry = { source: "active" };
|
||||||
|
const resolvedConfig = { plugins: { entries: { brave: { enabled: true } } } };
|
||||||
|
const resolveCandidatePluginIds = vi.fn(() => ["brave"]);
|
||||||
|
const mapRegistryProviders = vi.fn(() => ["provider"]);
|
||||||
|
mocks.resolveCompatibleRuntimePluginRegistry.mockReturnValue(null);
|
||||||
|
mocks.getActivePluginRegistry.mockReturnValue(activeRegistry);
|
||||||
|
|
||||||
|
const providers = resolvePluginWebProviders(
|
||||||
|
{
|
||||||
|
config: { plugins: { entries: {} } },
|
||||||
|
env: { BRAVE_API_KEY: "key" },
|
||||||
|
onlyPluginIds: ["brave", "firecrawl"],
|
||||||
|
origin: "bundled",
|
||||||
|
workspaceDir: "/workspace",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
resolveBundledResolutionConfig: () => ({
|
||||||
|
config: resolvedConfig,
|
||||||
|
activationSourceConfig: { plugins: { entries: {} } },
|
||||||
|
autoEnabledReasons: { brave: ["env"] },
|
||||||
|
}),
|
||||||
|
resolveCandidatePluginIds,
|
||||||
|
mapRegistryProviders,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(providers).toEqual(["provider"]);
|
||||||
|
expect(resolveCandidatePluginIds).toHaveBeenCalledWith({
|
||||||
|
config: resolvedConfig,
|
||||||
|
workspaceDir: "/workspace",
|
||||||
|
env: { BRAVE_API_KEY: "key" },
|
||||||
|
onlyPluginIds: ["brave", "firecrawl"],
|
||||||
|
origin: "bundled",
|
||||||
|
});
|
||||||
|
expect(mapRegistryProviders).toHaveBeenCalledWith({
|
||||||
|
registry: activeRegistry,
|
||||||
|
onlyPluginIds: ["brave"],
|
||||||
|
});
|
||||||
|
expect(mocks.loadOpenClawPlugins).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("preserves explicit empty candidate scopes when reusing the active registry", () => {
|
||||||
|
const activeRegistry = { source: "active" };
|
||||||
|
const mapRegistryProviders = vi.fn(() => []);
|
||||||
|
mocks.resolveCompatibleRuntimePluginRegistry.mockReturnValue(null);
|
||||||
|
mocks.getActivePluginRegistry.mockReturnValue(activeRegistry);
|
||||||
|
|
||||||
|
resolvePluginWebProviders(
|
||||||
|
{
|
||||||
|
config: {},
|
||||||
|
onlyPluginIds: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
resolveBundledResolutionConfig: () => ({
|
||||||
|
config: {},
|
||||||
|
activationSourceConfig: {},
|
||||||
|
autoEnabledReasons: {},
|
||||||
|
}),
|
||||||
|
resolveCandidatePluginIds: () => [],
|
||||||
|
mapRegistryProviders,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mapRegistryProviders).toHaveBeenCalledWith({
|
||||||
|
registry: activeRegistry,
|
||||||
|
onlyPluginIds: [],
|
||||||
|
});
|
||||||
|
expect(mocks.loadOpenClawPlugins).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type { PluginLoadOptions } from "./loader.js";
|
|||||||
import type { PluginManifestRecord } from "./manifest-registry.js";
|
import type { PluginManifestRecord } from "./manifest-registry.js";
|
||||||
import { hasExplicitPluginIdScope, normalizePluginIdScope } from "./plugin-scope.js";
|
import { hasExplicitPluginIdScope, normalizePluginIdScope } from "./plugin-scope.js";
|
||||||
import type { PluginRegistry } from "./registry.js";
|
import type { PluginRegistry } from "./registry.js";
|
||||||
import { getActivePluginRegistryWorkspaceDir } from "./runtime.js";
|
import { getActivePluginRegistry, getActivePluginRegistryWorkspaceDir } from "./runtime.js";
|
||||||
import {
|
import {
|
||||||
buildPluginRuntimeLoadOptionsFromValues,
|
buildPluginRuntimeLoadOptionsFromValues,
|
||||||
createPluginRuntimeLoaderLogger,
|
createPluginRuntimeLoaderLogger,
|
||||||
@@ -58,7 +58,7 @@ type ResolveWebProviderRuntimeDeps<TEntry> = {
|
|||||||
}) => TEntry[] | null;
|
}) => TEntry[] | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
function resolveWebProviderLoadOptions<TEntry>(
|
function resolveWebProviderRuntimeContext<TEntry>(
|
||||||
params: ResolvePluginWebProvidersParams,
|
params: ResolvePluginWebProvidersParams,
|
||||||
deps: ResolveWebProviderRuntimeDeps<TEntry>,
|
deps: ResolveWebProviderRuntimeDeps<TEntry>,
|
||||||
) {
|
) {
|
||||||
@@ -79,19 +79,35 @@ function resolveWebProviderLoadOptions<TEntry>(
|
|||||||
origin: params.origin,
|
origin: params.origin,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
return {
|
||||||
|
activationSourceConfig,
|
||||||
|
autoEnabledReasons,
|
||||||
|
config,
|
||||||
|
env,
|
||||||
|
onlyPluginIds,
|
||||||
|
workspaceDir,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveWebProviderLoadOptions<TEntry>(
|
||||||
|
context: ReturnType<typeof resolveWebProviderRuntimeContext<TEntry>>,
|
||||||
|
params: ResolvePluginWebProvidersParams,
|
||||||
|
) {
|
||||||
return buildPluginRuntimeLoadOptionsFromValues(
|
return buildPluginRuntimeLoadOptionsFromValues(
|
||||||
{
|
{
|
||||||
env,
|
env: context.env,
|
||||||
config,
|
config: context.config,
|
||||||
activationSourceConfig,
|
activationSourceConfig: context.activationSourceConfig,
|
||||||
autoEnabledReasons,
|
autoEnabledReasons: context.autoEnabledReasons,
|
||||||
workspaceDir,
|
workspaceDir: context.workspaceDir,
|
||||||
logger: createPluginRuntimeLoaderLogger(),
|
logger: createPluginRuntimeLoaderLogger(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
cache: params.cache ?? false,
|
cache: params.cache ?? false,
|
||||||
activate: params.activate ?? false,
|
activate: params.activate ?? false,
|
||||||
...(hasExplicitPluginIdScope(onlyPluginIds) ? { onlyPluginIds } : {}),
|
...(hasExplicitPluginIdScope(context.onlyPluginIds)
|
||||||
|
? { onlyPluginIds: context.onlyPluginIds }
|
||||||
|
: {}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -149,20 +165,28 @@ export function resolvePluginWebProviders<TEntry>(
|
|||||||
return deps.mapRegistryProviders({ registry, onlyPluginIds: pluginIds });
|
return deps.mapRegistryProviders({ registry, onlyPluginIds: pluginIds });
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadOptions = resolveWebProviderLoadOptions(params, deps);
|
const context = resolveWebProviderRuntimeContext(params, deps);
|
||||||
|
const loadOptions = resolveWebProviderLoadOptions(context, params);
|
||||||
const compatible = resolveCompatibleRuntimePluginRegistry(loadOptions);
|
const compatible = resolveCompatibleRuntimePluginRegistry(loadOptions);
|
||||||
if (compatible) {
|
if (compatible) {
|
||||||
return deps.mapRegistryProviders({
|
return deps.mapRegistryProviders({
|
||||||
registry: compatible,
|
registry: compatible,
|
||||||
onlyPluginIds: params.onlyPluginIds,
|
onlyPluginIds: context.onlyPluginIds,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (isPluginRegistryLoadInFlight(loadOptions)) {
|
if (isPluginRegistryLoadInFlight(loadOptions)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
const activeRegistry = getActivePluginRegistry();
|
||||||
|
if (activeRegistry) {
|
||||||
|
return deps.mapRegistryProviders({
|
||||||
|
registry: activeRegistry,
|
||||||
|
onlyPluginIds: context.onlyPluginIds,
|
||||||
|
});
|
||||||
|
}
|
||||||
return deps.mapRegistryProviders({
|
return deps.mapRegistryProviders({
|
||||||
registry: loadOpenClawPlugins(loadOptions),
|
registry: loadOpenClawPlugins(loadOptions),
|
||||||
onlyPluginIds: params.onlyPluginIds,
|
onlyPluginIds: context.onlyPluginIds,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +195,9 @@ export function resolveRuntimeWebProviders<TEntry>(
|
|||||||
deps: ResolveWebProviderRuntimeDeps<TEntry>,
|
deps: ResolveWebProviderRuntimeDeps<TEntry>,
|
||||||
): TEntry[] {
|
): TEntry[] {
|
||||||
const loadOptions =
|
const loadOptions =
|
||||||
params.config === undefined ? undefined : resolveWebProviderLoadOptions(params, deps);
|
params.config === undefined
|
||||||
|
? undefined
|
||||||
|
: resolveWebProviderLoadOptions(resolveWebProviderRuntimeContext(params, deps), params);
|
||||||
const runtimeRegistry = resolveRuntimePluginRegistry(loadOptions);
|
const runtimeRegistry = resolveRuntimePluginRegistry(loadOptions);
|
||||||
if (runtimeRegistry) {
|
if (runtimeRegistry) {
|
||||||
return deps.mapRegistryProviders({
|
return deps.mapRegistryProviders({
|
||||||
|
|||||||
Reference in New Issue
Block a user