Merge branch 'main' into fix/custodian-cancel-hint

This commit is contained in:
Jesse Merhi
2026-08-02 03:19:42 +10:00
committed by GitHub
3 changed files with 94 additions and 1 deletions

View File

@@ -1986,6 +1986,7 @@ const EXACT_TOOLING_TARGETS = new Map([
],
],
["scripts/run-vitest.mjs", ["run-vitest", "test-projects", "vitest-local-scheduling"]],
["scripts/run-oxlint-shards.mjs", ["run-oxlint"]],
["scripts/docker-e2e-rerun.mjs", ["docker-e2e-helper-cli"]],
["scripts/openclaw-postpack.mjs", [TOOLING_VITEST_CONFIG]],
["scripts/openclaw-npm-prepublish-verify.ts", ["test/openclaw-npm-prepublish-verify.test.ts"]],

View File

@@ -148,6 +148,51 @@ describe("pristine startup state", () => {
expect(canSkipPristineStartupStateMigrations(env)).toBe(true);
});
it("accepts canonical internal hook configuration", () => {
const env = createFixture({
hooks: {
internal: {
enabled: true,
entries: {
"session-memory": {
enabled: true,
env: { OPENCLAW_HOOK_TEST: "enabled" },
customOption: "value",
},
},
},
},
});
expect(planPristineStartupStateMigrations(env)).toEqual({
skipAllStateMigrations: true,
skipCoreStateMigrations: true,
});
});
it("retains migrations for legacy, external, and malformed hook configuration", () => {
const unsafeHooks = [
{ gmail: { account: "operator@example.com" } },
{ internal: { installs: { "session-memory": { source: "bundled" } } } },
{ internal: { handlers: [] } },
{ internal: { load: { extraDirs: ["/tmp/hooks"] } } },
{ internal: { enabled: "yes" } },
{ internal: { entries: [] } },
{ internal: { entries: { "session-memory": { enabled: "yes" } } } },
{ internal: { entries: { "session-memory": { env: { INVALID: true } } } } },
];
for (const hooks of unsafeHooks) {
expect(
planPristineStartupStateMigrations(createFixture({ hooks })),
JSON.stringify(hooks),
).toEqual({
skipAllStateMigrations: false,
skipCoreStateMigrations: false,
});
}
});
it("retains migrations for bundled plugins with doctor state surfaces", () => {
const env = addBundledPlugin(
createFixture({ plugins: { entries: { example: { enabled: true } } } }),

View File

@@ -31,7 +31,6 @@ const STATEFUL_CONFIG_KEYS = new Set([
"cron",
"discovery",
"env",
"hooks",
"marketplaces",
"mcp",
"media",
@@ -48,6 +47,51 @@ const STATEFUL_CONFIG_KEYS = new Set([
"web",
]);
// Canonical internal entries have no legacy machine state to import. Keep every
// older or external hook shape on Doctor's full migration path.
function hasOnlyMigrationSafeInternalHooks(config: Record<string, unknown>): boolean {
const hooks = config.hooks;
if (hooks === undefined) {
return true;
}
if (!isRecord(hooks) || Object.keys(hooks).some((key) => key !== "internal")) {
return false;
}
const internal = hooks.internal;
if (internal === undefined) {
return true;
}
if (
!isRecord(internal) ||
Object.keys(internal).some((key) => !["enabled", "entries"].includes(key)) ||
(internal.enabled !== undefined && typeof internal.enabled !== "boolean")
) {
return false;
}
if (internal.entries === undefined) {
return true;
}
if (!isRecord(internal.entries)) {
return false;
}
return Object.values(internal.entries).every((entry) => {
if (!isRecord(entry)) {
return false;
}
if (entry.enabled !== undefined && typeof entry.enabled !== "boolean") {
return false;
}
if (entry.env === undefined) {
return true;
}
return (
isRecord(entry.env) && Object.values(entry.env).every((value) => typeof value === "string")
);
});
}
function containsObjectKey(value: unknown, targetKey: string): boolean {
if (Array.isArray(value)) {
return value.some((entry) => containsObjectKey(entry, targetKey));
@@ -142,6 +186,9 @@ function configIsPristineCoreStateSafe(config: Record<string, unknown>): boolean
if ([...STATEFUL_CONFIG_KEYS].some((key) => Object.hasOwn(config, key))) {
return false;
}
if (!hasOnlyMigrationSafeInternalHooks(config)) {
return false;
}
if (containsObjectKey(config.agents, "memorySearch")) {
return false;
}