mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:20:43 +00:00
fix: keep runtime deps repair out of hot paths
This commit is contained in:
@@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Plugins/runtime-deps: prune legacy version-scoped plugin runtime-deps roots during bundled dependency repair and cover the path in Package Acceptance's upgrade-survivor matrix, so upgrades from 2026.4.x no longer leave stale per-plugin runtime trees after doctor runs. Thanks @vincentkoc.
|
||||
- Plugins/runtime-deps: keep Gateway startup plugin imports and runtime plugin fallback loads verify-only after startup/config repair planning, so packaged installs no longer spawn package-manager repair from hot paths after readiness. Refs #75283 and #75069. Thanks @brokemac79 and @xiaohuaxi.
|
||||
- Google Meet: interrupt Realtime provider output when local barge-in clears playback, so command-pair audio stops model speech instead of only restarting Chrome playback. Fixes #73850. (#73834) Thanks @shhtheonlyperson.
|
||||
- Gateway/config: cap oversized plugin-owned schemas in the full `config.schema` response so large installed plugin sets cannot balloon Gateway RSS or crash schema clients. Thanks @vincentkoc.
|
||||
- Gateway/sessions: use bounded tail reads for sessions-list transcript usage fallbacks and cap bulk title/last-message hydration, keeping large session stores responsive when rows request derived previews. Thanks @vincentkoc.
|
||||
|
||||
@@ -342,7 +342,7 @@ That stages grounded durable candidates into the short-term dreaming store while
|
||||
<Accordion title="7b. Bundled plugin runtime deps">
|
||||
Doctor verifies runtime dependencies only for bundled plugins that are active in the current config or enabled by their bundled manifest default, for example `plugins.entries.discord.enabled: true`, legacy `channels.discord.enabled: true`, configured `models.providers.*` / agent model refs, or a default-enabled bundled plugin without provider ownership. If any are missing, doctor reports the packages and installs them in `openclaw doctor --fix` / `openclaw doctor --repair` mode. External plugins still use `openclaw plugins install` / `openclaw plugins update`; doctor does not install dependencies for arbitrary plugin paths.
|
||||
|
||||
During doctor repair, bundled runtime-dependency npm installs report spinner progress in TTY sessions and periodic line progress in piped/headless output. The Gateway and local CLI can also repair active bundled plugin runtime dependencies on demand before importing a bundled plugin. These installs are scoped to the plugin runtime install root, run with scripts disabled, do not write a package lock, and are guarded by an install-root lock so concurrent CLI or Gateway starts do not mutate the same `node_modules` tree at the same time. Stale legacy locks from killed Docker/container starts are reclaimed when their owner metadata cannot prove a current process incarnation and the lock files are old.
|
||||
During doctor repair, bundled runtime-dependency npm installs report spinner progress in TTY sessions and periodic line progress in piped/headless output. Gateway startup and config reload enter plugin-plan mode before importing bundled plugin runtime modules; normal runtime imports are verify-only and do not spawn package-manager repair. These installs are scoped to the plugin runtime install root, run with scripts disabled, do not write a package lock, and are guarded by an install-root lock so concurrent CLI or Gateway starts do not mutate the same `node_modules` tree at the same time. Stale legacy locks from killed Docker/container starts are reclaimed when their owner metadata cannot prove a current process incarnation and the lock files are old.
|
||||
|
||||
</Accordion>
|
||||
<Accordion title="8. Gateway service migrations and cleanup hints">
|
||||
|
||||
@@ -48,6 +48,7 @@ describe("ensureRuntimePluginsLoaded", () => {
|
||||
|
||||
expect(hoisted.resolveRuntimePluginRegistry).toHaveBeenCalledWith({
|
||||
config: {} as never,
|
||||
installBundledRuntimeDeps: false,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
runtimeOptions: {
|
||||
allowGatewaySubagentBinding: true,
|
||||
@@ -63,6 +64,7 @@ describe("ensureRuntimePluginsLoaded", () => {
|
||||
|
||||
expect(hoisted.resolveRuntimePluginRegistry).toHaveBeenCalledWith({
|
||||
config: {} as never,
|
||||
installBundledRuntimeDeps: false,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
runtimeOptions: undefined,
|
||||
});
|
||||
@@ -78,6 +80,7 @@ describe("ensureRuntimePluginsLoaded", () => {
|
||||
|
||||
expect(hoisted.resolveRuntimePluginRegistry).toHaveBeenCalledWith({
|
||||
config: {} as never,
|
||||
installBundledRuntimeDeps: false,
|
||||
workspaceDir: "/tmp/workspace",
|
||||
runtimeOptions: {
|
||||
allowGatewaySubagentBinding: true,
|
||||
|
||||
@@ -18,6 +18,7 @@ export function ensureRuntimePluginsLoaded(params: {
|
||||
const loadOptions = {
|
||||
config: params.config,
|
||||
workspaceDir,
|
||||
installBundledRuntimeDeps: false,
|
||||
runtimeOptions: allowGatewaySubagentBinding
|
||||
? {
|
||||
allowGatewaySubagentBinding: true,
|
||||
|
||||
@@ -218,7 +218,7 @@ describe("prepareGatewayPluginBootstrap runtime-deps staging", () => {
|
||||
runStartupSessionMigration.mockClear();
|
||||
});
|
||||
|
||||
it("falls back to loader-level runtime-deps staging after failed pre-start staging", async () => {
|
||||
it("loads startup plugins in verify-only mode after failed pre-start staging", async () => {
|
||||
repairBundledRuntimeDepsPackagePlanAsync.mockRejectedValueOnce(new Error("offline registry"));
|
||||
const log = createLog();
|
||||
const { prepareGatewayPluginBootstrap } = await import("./server-startup-plugins.js");
|
||||
@@ -245,7 +245,7 @@ describe("prepareGatewayPluginBootstrap runtime-deps staging", () => {
|
||||
pluginLookUpTable: expect.objectContaining({
|
||||
manifestRegistry: pluginManifestRegistry,
|
||||
}),
|
||||
installBundledRuntimeDeps: true,
|
||||
installBundledRuntimeDeps: false,
|
||||
}),
|
||||
);
|
||||
expect(repairBundledRuntimeDepsPackagePlanAsync).toHaveBeenCalledOnce();
|
||||
@@ -296,7 +296,7 @@ describe("prepareGatewayPluginBootstrap runtime-deps staging", () => {
|
||||
}),
|
||||
);
|
||||
expect(loadGatewayStartupPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ installBundledRuntimeDeps: true }),
|
||||
expect.objectContaining({ installBundledRuntimeDeps: false }),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -321,7 +321,7 @@ describe("prepareGatewayPluginBootstrap runtime-deps staging", () => {
|
||||
}),
|
||||
);
|
||||
expect(loadGatewayStartupPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ installBundledRuntimeDeps: true }),
|
||||
expect.objectContaining({ installBundledRuntimeDeps: false }),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -491,7 +491,7 @@ describe("prepareGatewayPluginBootstrap runtime-deps staging", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to loader-level runtime-deps staging after failed pre-start scan", async () => {
|
||||
it("keeps startup plugin loading verify-only after failed pre-start scan", async () => {
|
||||
repairBundledRuntimeDepsPackagePlanAsync.mockRejectedValueOnce(
|
||||
new Error("unsupported runtime dependency spec"),
|
||||
);
|
||||
@@ -518,7 +518,7 @@ describe("prepareGatewayPluginBootstrap runtime-deps staging", () => {
|
||||
expect.stringContaining("unsupported runtime dependency spec"),
|
||||
);
|
||||
expect(loadGatewayStartupPlugins).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ installBundledRuntimeDeps: true }),
|
||||
expect.objectContaining({ installBundledRuntimeDeps: false }),
|
||||
);
|
||||
expect(loadGatewayStartupPlugins.mock.calls[0]?.[0]).not.toHaveProperty(
|
||||
"bundledRuntimeDepsInstaller",
|
||||
|
||||
@@ -290,7 +290,7 @@ export async function loadGatewayStartupPluginRuntime(params: {
|
||||
baseMethods: params.baseMethods,
|
||||
pluginIds: params.startupPluginIds,
|
||||
pluginLookUpTable: params.pluginLookUpTable,
|
||||
installBundledRuntimeDeps: true,
|
||||
installBundledRuntimeDeps: false,
|
||||
bundledRuntimeDepsRepairError: prestageResult.repairError,
|
||||
preferSetupRuntimeForChannelPlugins: params.preferSetupRuntimeForChannelPlugins,
|
||||
suppressPluginInfoLogs: params.suppressPluginInfoLogs,
|
||||
|
||||
@@ -103,6 +103,7 @@ function expectBundledCompatLoadPath(params: {
|
||||
config: params.enablementCompat,
|
||||
onlyPluginIds: ["openai"],
|
||||
activate: false,
|
||||
installBundledRuntimeDeps: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -408,6 +409,7 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
}),
|
||||
onlyPluginIds: ["microsoft"],
|
||||
activate: false,
|
||||
installBundledRuntimeDeps: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -616,6 +618,7 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
config: expect.anything(),
|
||||
onlyPluginIds: [],
|
||||
activate: false,
|
||||
installBundledRuntimeDeps: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -660,6 +663,7 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
config: compatConfig,
|
||||
onlyPluginIds: ["google"],
|
||||
activate: false,
|
||||
installBundledRuntimeDeps: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -795,6 +799,7 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
config: compatConfig,
|
||||
onlyPluginIds: ["microsoft"],
|
||||
activate: false,
|
||||
installBundledRuntimeDeps: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -818,6 +823,7 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
config: expect.anything(),
|
||||
onlyPluginIds: [],
|
||||
activate: false,
|
||||
installBundledRuntimeDeps: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -955,6 +961,7 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
config: enablementCompat,
|
||||
onlyPluginIds: ["google"],
|
||||
activate: false,
|
||||
installBundledRuntimeDeps: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1077,6 +1084,7 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
config: enablementCompat,
|
||||
onlyPluginIds: ["microsoft"],
|
||||
activate: false,
|
||||
installBundledRuntimeDeps: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -315,7 +315,7 @@ export function resolvePluginCapabilityProvider<K extends CapabilityProviderRegi
|
||||
const loadOptions = createCapabilityProviderFallbackLoadOptions({
|
||||
compatConfig,
|
||||
pluginIds,
|
||||
installBundledRuntimeDeps: params.installBundledRuntimeDeps,
|
||||
installBundledRuntimeDeps: params.installBundledRuntimeDeps ?? false,
|
||||
});
|
||||
const cache = resolveCapabilityProviderSnapshotCache(params.cfg);
|
||||
const cacheKey = cache
|
||||
@@ -373,7 +373,7 @@ export function resolvePluginCapabilityProviders<K extends CapabilityProviderReg
|
||||
const loadOptions = createCapabilityProviderFallbackLoadOptions({
|
||||
compatConfig,
|
||||
pluginIds,
|
||||
installBundledRuntimeDeps: params.installBundledRuntimeDeps,
|
||||
installBundledRuntimeDeps: params.installBundledRuntimeDeps ?? false,
|
||||
});
|
||||
const cache = resolveCapabilityProviderSnapshotCache(params.cfg);
|
||||
const cacheKey = cache
|
||||
|
||||
@@ -261,7 +261,7 @@ function resolveRuntimeProviderPluginLoadState(
|
||||
pluginSdkResolution: params.pluginSdkResolution,
|
||||
cache: params.cache ?? true,
|
||||
activate: params.activate ?? false,
|
||||
installBundledRuntimeDeps: params.installBundledRuntimeDeps,
|
||||
installBundledRuntimeDeps: params.installBundledRuntimeDeps ?? false,
|
||||
},
|
||||
);
|
||||
return { loadOptions };
|
||||
|
||||
@@ -162,6 +162,7 @@ describe("ensurePluginRegistryLoaded", () => {
|
||||
workspaceDir: "/resolved-workspace",
|
||||
onlyPluginIds: ["demo-channel"],
|
||||
throwOnLoadError: true,
|
||||
installBundledRuntimeDeps: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -175,7 +175,7 @@ export function ensurePluginRegistryLoaded(options?: {
|
||||
},
|
||||
{
|
||||
throwOnLoadError: true,
|
||||
installBundledRuntimeDeps: options?.installBundledRuntimeDeps,
|
||||
installBundledRuntimeDeps: options?.installBundledRuntimeDeps ?? false,
|
||||
...(hasExplicitPluginIdScope(requestedPluginIds) ||
|
||||
shouldForwardChannelScope({ scope, scopedLoad }) ||
|
||||
hasNonEmptyPluginIdScope(expectedChannelPluginIds)
|
||||
|
||||
@@ -457,6 +457,7 @@ describe("resolvePluginTools optional tools", () => {
|
||||
|
||||
expect(loadOpenClawPluginsMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
installBundledRuntimeDeps: false,
|
||||
runtimeOptions: {
|
||||
allowGatewaySubagentBinding: true,
|
||||
},
|
||||
|
||||
@@ -133,7 +133,10 @@ export function resolvePluginTools(params: {
|
||||
const runtimeOptions = params.allowGatewaySubagentBinding
|
||||
? { allowGatewaySubagentBinding: true as const }
|
||||
: undefined;
|
||||
const loadOptions = buildPluginRuntimeLoadOptions(context, { runtimeOptions });
|
||||
const loadOptions = buildPluginRuntimeLoadOptions(context, {
|
||||
installBundledRuntimeDeps: false,
|
||||
runtimeOptions,
|
||||
});
|
||||
const registry = resolvePluginToolRegistry({
|
||||
loadOptions,
|
||||
allowGatewaySubagentBinding: params.allowGatewaySubagentBinding,
|
||||
|
||||
Reference in New Issue
Block a user