diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dc27b82453..1aad78b155c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai - fix(config): validate browser sandbox bind sources [AI]. (#84799) Thanks @pgondhi987. - doctor: constrain legacy plugin cleanup paths [AI]. (#84801) Thanks @pgondhi987. +- Update/doctor: prune stale local bundled plugin install records that point at old compiled bundled output so current bundled plugin schemas win after upgrade. (#84863) Thanks @fuller-stack-dev. - Media/audio: skip empty structured sherpa-onnx transcripts instead of treating the raw JSON payload as spoken text. (#84667) Thanks @TurboTheTurtle. - Node/Linux: keep `OPENCLAW_GATEWAY_TOKEN` out of generated systemd unit files by writing node service token values to a node-specific env file. (#84408) - Memory-core/dreaming: reuse stable narrative subagent session keys per workspace and phase while keeping per-run idempotency and bounded cleanup, so stale `dreaming-narrative-*` sessions do not accumulate. Fixes #68252, #69187, and #70402. (#70464) Thanks @chiyouYCH. diff --git a/src/commands/doctor-plugin-registry.ts b/src/commands/doctor-plugin-registry.ts index 928f511d28b..9b5085e52c7 100644 --- a/src/commands/doctor-plugin-registry.ts +++ b/src/commands/doctor-plugin-registry.ts @@ -112,7 +112,9 @@ function listStaleManagedNpmBundledPlugins( const dependencies = readStringMap(readJsonObject(npmPackageJsonPath)?.dependencies); const stale: StaleManagedNpmBundledPlugin[] = []; - for (const packageName of Object.keys(dependencies).toSorted()) { + for (const packageName of Object.keys(dependencies).toSorted((left, right) => + left.localeCompare(right), + )) { if (!packageName.startsWith("@openclaw/")) { continue; } diff --git a/src/plugins/stale-local-bundled-plugin-install-records.ts b/src/plugins/stale-local-bundled-plugin-install-records.ts index 6e34c464214..f1c5c220a9a 100644 --- a/src/plugins/stale-local-bundled-plugin-install-records.ts +++ b/src/plugins/stale-local-bundled-plugin-install-records.ts @@ -12,10 +12,6 @@ export type StaleLocalBundledPluginInstallRecord = { bundledPath: string; }; -function escapeRegExp(input: string): string { - return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -} - function normalizePathForCompare(rawPath: string, env?: NodeJS.ProcessEnv): string { return path.resolve(normalizeBundledLookupPath(resolveUserPath(rawPath, env))); } @@ -34,11 +30,14 @@ function primaryInstallRecordPath(record: PluginInstallRecord): { } function looksLikeCompiledBundledPluginPath(targetPath: string, pluginId: string): boolean { - const normalized = normalizeBundledLookupPath(targetPath).split(path.sep).join("/"); - const escapedPluginId = escapeRegExp(pluginId); - return new RegExp(`(?:^|/)(?:dist|dist-runtime)/extensions/${escapedPluginId}(?:$|/)`).test( - normalized, - ); + const segments = normalizeBundledLookupPath(targetPath).split(/[\\/]+/u); + return segments.some((segment, index) => { + return ( + (segment === "dist" || segment === "dist-runtime") && + segments[index + 1] === "extensions" && + segments[index + 2] === pluginId + ); + }); } function hasStaleBundledVersion( @@ -64,7 +63,9 @@ export function listStaleLocalBundledPluginInstallRecords(params: { }); const stale: StaleLocalBundledPluginInstallRecord[] = []; - for (const [pluginId, record] of Object.entries(params.installRecords).toSorted()) { + for (const [pluginId, record] of Object.entries(params.installRecords).toSorted( + ([left], [right]) => left.localeCompare(right), + )) { if (record.source !== "path") { continue; }