diff --git a/src/plugins/installed-plugin-index-store.test.ts b/src/plugins/installed-plugin-index-store.test.ts index 47ea778ba14..1a97f406b55 100644 --- a/src/plugins/installed-plugin-index-store.test.ts +++ b/src/plugins/installed-plugin-index-store.test.ts @@ -36,6 +36,7 @@ function createIndex(overrides: Partial = {}): InstalledPl manifestHash: "manifest-hash", rootDir: "/plugins/demo", origin: "global", + enabled: true, contributions: { providers: ["demo"], channels: ["demo-chat"], @@ -150,7 +151,7 @@ describe("installed plugin index persistence", () => { refreshReasons: [], persisted: current, current: { - plugins: [expect.objectContaining({ pluginId: "demo" })], + plugins: [expect.objectContaining({ pluginId: "demo", enabled: true })], }, }); @@ -174,7 +175,7 @@ describe("installed plugin index persistence", () => { refreshReasons: ["policy-changed"], persisted: current, current: { - plugins: [expect.objectContaining({ pluginId: "demo" })], + plugins: [expect.objectContaining({ pluginId: "demo", enabled: false })], }, }); diff --git a/src/plugins/installed-plugin-index-store.ts b/src/plugins/installed-plugin-index-store.ts index 6e2298f69ac..d41e73d6f2d 100644 --- a/src/plugins/installed-plugin-index-store.ts +++ b/src/plugins/installed-plugin-index-store.ts @@ -60,6 +60,7 @@ const InstalledPluginIndexRecordSchema = z packageJsonHash: z.string().optional(), rootDir: z.string(), origin: z.string(), + enabled: z.boolean(), enabledByDefault: z.boolean().optional(), contributions: InstalledPluginIndexContributionsSchema, compat: z.array(z.string()), diff --git a/src/plugins/installed-plugin-index.test.ts b/src/plugins/installed-plugin-index.test.ts index fd8af6cbb91..71078aba5a9 100644 --- a/src/plugins/installed-plugin-index.test.ts +++ b/src/plugins/installed-plugin-index.test.ts @@ -162,6 +162,7 @@ describe("installed plugin index", () => { packageVersion: "1.2.3", origin: "global", rootDir: fixture.rootDir, + enabled: true, packageInstall: { defaultChoice: "npm", npm: { @@ -220,6 +221,7 @@ describe("installed plugin index", () => { const record = getInstalledPluginRecord(index, "demo"); expect(record).toMatchObject({ pluginId: "demo", + enabled: true, }); expect(record?.installRecord).toBeUndefined(); expect(isInstalledPluginEnabled(index, "demo")).toBe(true); @@ -259,6 +261,7 @@ describe("installed plugin index", () => { expect(listEnabledInstalledPluginRecords(index, config)).toEqual([]); expect(getInstalledPluginRecord(index, "demo")).toMatchObject({ pluginId: "demo", + enabled: false, }); expect(isInstalledPluginEnabled(index, "demo", config)).toBe(false); expect(listInstalledPluginContributionIds(index, "providers", { config })).toEqual([]); @@ -536,6 +539,7 @@ describe("installed plugin index", () => { }, }), ).toBe(false); + expect(index.plugins[0]?.enabled).toBe(false); expect(index.plugins[0]?.contributions.providers).toEqual(["demo"]); }); diff --git a/src/plugins/installed-plugin-index.ts b/src/plugins/installed-plugin-index.ts index 1cbd04b617c..cc90f7b62d3 100644 --- a/src/plugins/installed-plugin-index.ts +++ b/src/plugins/installed-plugin-index.ts @@ -91,6 +91,7 @@ export type InstalledPluginIndexRecord = { packageJsonHash?: string; rootDir: string; origin: PluginManifestRecord["origin"]; + enabled: boolean; enabledByDefault?: boolean; contributions: InstalledPluginIndexContributions; compat: readonly PluginCompatCode[]; @@ -400,6 +401,7 @@ function buildInstalledPluginIndex( const env = params.env ?? process.env; const { candidates, registry } = resolveRegistry(params); const candidateByRootDir = buildCandidateLookup(candidates); + const normalizedConfig = normalizePluginsConfigWithResolver(params.config?.plugins); const diagnostics: PluginDiagnostic[] = [...registry.diagnostics]; const generatedAtMs = (params.now?.() ?? new Date()).getTime(); const plugins = registry.plugins.map((record): InstalledPluginIndexRecord => { @@ -422,12 +424,20 @@ function buildInstalledPluginIndex( required: false, }) : undefined; + const enabled = resolveEffectiveEnableState({ + id: record.id, + origin: record.origin, + config: normalizedConfig, + rootConfig: params.config, + enabledByDefault: record.enabledByDefault, + }).enabled; const indexRecord: InstalledPluginIndexRecord = { pluginId: record.id, manifestPath: record.manifestPath, manifestHash, rootDir: record.rootDir, origin: record.origin, + enabled, contributions: buildContributions(record), compat: collectCompatCodes(record), }; @@ -490,6 +500,9 @@ export function listEnabledInstalledPluginRecords( index: InstalledPluginIndex, config?: OpenClawConfig, ): readonly InstalledPluginIndexRecord[] { + if (!config) { + return index.plugins.filter((plugin) => plugin.enabled); + } const normalizedConfig = normalizePluginsConfigWithResolver(config?.plugins); return index.plugins.filter( (plugin) => @@ -519,6 +532,9 @@ export function isInstalledPluginEnabled( if (!record) { return false; } + if (!config) { + return record.enabled; + } const normalizedConfig = normalizePluginsConfigWithResolver(config?.plugins); return resolveEffectiveEnableState({ id: record.pluginId, @@ -674,6 +690,9 @@ export function diffInstalledPluginIndexInvalidationReasons( ) { reasons.add("source-changed"); } + if (previousPlugin.enabled !== currentPlugin.enabled) { + reasons.add("policy-changed"); + } if (previousPlugin.manifestHash !== currentPlugin.manifestHash) { reasons.add("stale-manifest"); } diff --git a/src/plugins/plugin-registry.test.ts b/src/plugins/plugin-registry.test.ts index 865d322f696..ebdabb20578 100644 --- a/src/plugins/plugin-registry.test.ts +++ b/src/plugins/plugin-registry.test.ts @@ -97,6 +97,7 @@ describe("plugin registry facade", () => { expect(listPluginRecords({ index }).map((plugin) => plugin.pluginId)).toEqual(["demo"]); expect(getPluginRecord({ index, pluginId: "demo" })).toMatchObject({ pluginId: "demo", + enabled: true, }); expect(isPluginEnabled({ index, pluginId: "demo" })).toBe(true); expect(listPluginContributionIds({ index, contribution: "providers" })).toEqual(["demo"]); @@ -132,6 +133,7 @@ describe("plugin registry facade", () => { expect(getPluginRecord({ index, pluginId: "demo" })).toMatchObject({ pluginId: "demo", + enabled: false, }); const config = { plugins: {