Files
openclaw/src/plugins/installed-plugin-index-invalidation.ts
T
Vincent Koc e4aab26d01 fix(cli): avoid repeated migration scans during local agent turns (#119051)
* fix(cli): avoid repeated state migration scans

* fix(build): preserve source runtime provenance

* fix(cli): persist state checkpoint before plugin convergence

* fix(cli): invalidate migration checkpoints on input changes

* fix(plugins): refresh changed doctor contracts

* fix(plugins): preserve warm Doctor contract indexes

* test(plugins): align manifest registry fixtures

* test(cli): isolate ACP process state

* fix(doctor): persist refreshed plugin indexes

* fix(doctor): verify persisted plugin index from disk

* fix(doctor): fence plugin index persistence

* fix(doctor): retain validated migration lease

* fix(plugins): canonicalize persisted build metadata

* fix(plugins): preserve config policy during install repair
2026-08-04 20:28:16 +08:00

85 lines
3.0 KiB
TypeScript

// Invalidates installed plugin index entries after activation metadata changes.
import { hasConfigPathActivationMetadataMigration } from "./installed-plugin-index-config-path-scope.js";
import { hashJson } from "./installed-plugin-index-hash.js";
import type {
InstalledPluginIndex,
InstalledPluginIndexRefreshReason,
} from "./installed-plugin-index-types.js";
export function diffInstalledPluginIndexInvalidationReasons(
previous: InstalledPluginIndex,
current: InstalledPluginIndex,
): readonly InstalledPluginIndexRefreshReason[] {
const reasons = new Set<InstalledPluginIndexRefreshReason>();
if (previous.version !== current.version) {
reasons.add("missing");
}
if (previous.hostContractVersion !== current.hostContractVersion) {
reasons.add("host-contract-changed");
}
if (previous.compatRegistryVersion !== current.compatRegistryVersion) {
reasons.add("compat-registry-changed");
}
if (previous.migrationVersion !== current.migrationVersion) {
reasons.add("migration");
}
if (previous.policyHash !== current.policyHash) {
reasons.add("policy-changed");
}
if (hashJson(previous.installRecords ?? {}) !== hashJson(current.installRecords ?? {})) {
reasons.add("source-changed");
}
const previousByPluginId = new Map(previous.plugins.map((plugin) => [plugin.pluginId, plugin]));
const currentByPluginId = new Map(current.plugins.map((plugin) => [plugin.pluginId, plugin]));
for (const [pluginId, previousPlugin] of previousByPluginId) {
const currentPlugin = currentByPluginId.get(pluginId);
if (!currentPlugin) {
reasons.add("source-changed");
continue;
}
if (
previousPlugin.rootDir !== currentPlugin.rootDir ||
previousPlugin.manifestPath !== currentPlugin.manifestPath ||
previousPlugin.installRecordHash !== currentPlugin.installRecordHash
) {
reasons.add("source-changed");
}
if (previousPlugin.enabled !== currentPlugin.enabled) {
reasons.add("policy-changed");
}
if (
hasConfigPathActivationMetadataMigration({
previous: previousPlugin,
current: currentPlugin,
})
) {
reasons.add("migration");
}
if (
previousPlugin.manifestHash !== currentPlugin.manifestHash ||
previousPlugin.doctorContractHash !== currentPlugin.doctorContractHash
) {
reasons.add("stale-manifest");
}
if (
previousPlugin.packageVersion !== currentPlugin.packageVersion ||
previousPlugin.packageJson?.path !== currentPlugin.packageJson?.path ||
previousPlugin.packageJson?.hash !== currentPlugin.packageJson?.hash
) {
reasons.add("stale-package");
}
}
for (const pluginId of currentByPluginId.keys()) {
if (!previousByPluginId.has(pluginId)) {
const currentPlugin = currentByPluginId.get(pluginId);
if (currentPlugin?.enabled === false) {
continue;
}
reasons.add("source-changed");
}
}
return Array.from(reasons).toSorted((left, right) => left.localeCompare(right));
}