refactor: move plugin state slices to sqlite

* refactor: move plugin state slices to sqlite

* fix: keep legacy plugin state migration out of runtime

* fix: add doctor migrations for plugin sqlite state

* fix: preserve teams feedback learning migration keys

* fix: merge teams legacy feedback learnings

* fix: guard doctor imports against plugin state caps

* fix: leave lossy teams learning filenames unmigrated

* fix: preserve teams feedback learning scope

* fix: load plugin doctor contracts from package dist

* fix: satisfy plugin state migration gates
This commit is contained in:
Peter Steinberger
2026-05-31 18:09:27 +01:00
committed by GitHub
parent 12d4dda1bb
commit 33c246dbba
19 changed files with 1576 additions and 445 deletions

View File

@@ -94,6 +94,55 @@ module.exports = {
);
}
function writeDistDoctorPlugin(pluginRoot: string, pluginId: string): void {
fs.mkdirSync(path.join(pluginRoot, "dist"), { recursive: true });
fs.writeFileSync(
path.join(pluginRoot, "openclaw.plugin.json"),
JSON.stringify(
{
id: pluginId,
name: "Dist Doctor",
version: "0.0.0-test",
configSchema: {},
},
null,
2,
),
"utf8",
);
fs.writeFileSync(
path.join(pluginRoot, "package.json"),
JSON.stringify(
{
name: `@openclaw/${pluginId}`,
version: "0.0.0-test",
type: "module",
openclaw: {
extensions: ["./dist/index.js"],
},
},
null,
2,
),
"utf8",
);
fs.writeFileSync(path.join(pluginRoot, "dist", "index.js"), "export {};\n", "utf8");
fs.writeFileSync(
path.join(pluginRoot, "dist", "doctor-contract-api.cjs"),
`
module.exports = {
legacyConfigRules: [
{
path: ["plugins", "entries", ${JSON.stringify(pluginId)}, "config", "distOnly"],
message: "dist doctor contract warning",
},
],
};
`,
"utf8",
);
}
function writeDoctorSessionOwnerPlugin(pluginRoot: string, pluginId: string): void {
fs.mkdirSync(pluginRoot, { recursive: true });
fs.writeFileSync(
@@ -192,6 +241,26 @@ describe("doctor contract registry load-path plugins", () => {
]);
});
it("discovers doctor warning rules from package dist contracts", () => {
const stateDir = makeTempDir();
const pluginRoot = makeTempDir();
const pluginId = "dist-doctor";
writeDistDoctorPlugin(pluginRoot, pluginId);
const config = createDoctorPluginConfig(pluginRoot, pluginId);
const rules = listPluginDoctorLegacyConfigRules({
config,
env: makeHermeticDoctorEnv(stateDir),
pluginIds: [pluginId],
});
expect(rules).toEqual([
{
path: ["plugins", "entries", pluginId, "config", "distOnly"],
message: "dist doctor contract warning",
},
]);
});
it("applies compatibility normalizers from plugins.load.paths", () => {
const stateDir = makeTempDir();
const pluginRoot = makeTempDir();

View File

@@ -104,16 +104,14 @@ function resolveContractApiPath(rootDir: string): string | null {
const orderedExtensions = RUNNING_FROM_BUILT_ARTIFACT
? CONTRACT_API_EXTENSIONS
: ([...CONTRACT_API_EXTENSIONS.slice(3), ...CONTRACT_API_EXTENSIONS.slice(0, 3)] as const);
for (const extension of orderedExtensions) {
const candidate = path.join(rootDir, `doctor-contract-api${extension}`);
if (fs.existsSync(candidate)) {
return candidate;
}
}
for (const extension of orderedExtensions) {
const candidate = path.join(rootDir, `contract-api${extension}`);
if (fs.existsSync(candidate)) {
return candidate;
for (const basename of ["doctor-contract-api", "contract-api"]) {
for (const extension of orderedExtensions) {
for (const baseDir of [rootDir, path.join(rootDir, "dist")]) {
const candidate = path.join(baseDir, `${basename}${extension}`);
if (fs.existsSync(candidate)) {
return candidate;
}
}
}
}
return null;