diff --git a/CHANGELOG.md b/CHANGELOG.md index f8cbdfeecc9..e7d4c0e5536 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai - Agents/context engines: run opt-in turn maintenance as idle-aware background work so the next foreground turn no longer waits on proactive maintenance. (#65233) thanks @100yenadmin +- Plugins/status: report the registered context-engine IDs in `plugins inspect` instead of the owning plugin ID, so non-matching engine IDs and multi-engine plugins are classified correctly. (#58766) thanks @zhuisDEV ## 2026.4.12 ### Changes diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index c8ff13cd30f..f25ec8e78fe 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -2608,6 +2608,12 @@ module.exports = { id: "throws-after-import", register() {} };`, selectCount: () => 1, duplicateMessage: "context engine already registered: shared-context-engine-loader-test (plugin:context-engine-owner-a)", + assertPrimaryOwner: (registry: ReturnType) => { + expect( + registry.plugins.find((entry) => entry.id === "context-engine-owner-a") + ?.contextEngineIds, + ).toEqual(["shared-context-engine-loader-test"]); + }, assert: expectDuplicateRegistrationResult, }, { diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index 8e9ce34261b..5194c4c6224 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -733,6 +733,7 @@ function createPluginRecord(params: { musicGenerationProviderIds: [], webFetchProviderIds: [], webSearchProviderIds: [], + contextEngineIds: [], memoryEmbeddingProviderIds: [], agentHarnessIds: [], gatewayMethods: [], diff --git a/src/plugins/registry-types.ts b/src/plugins/registry-types.ts index fbd62a77b44..578bf42cdc2 100644 --- a/src/plugins/registry-types.ts +++ b/src/plugins/registry-types.ts @@ -246,6 +246,7 @@ export type PluginRecord = { musicGenerationProviderIds: string[]; webFetchProviderIds: string[]; webSearchProviderIds: string[]; + contextEngineIds?: string[]; memoryEmbeddingProviderIds: string[]; agentHarnessIds: string[]; gatewayMethods: string[]; diff --git a/src/plugins/registry.ts b/src/plugins/registry.ts index f30fe801399..fa6f241efbd 100644 --- a/src/plugins/registry.ts +++ b/src/plugins/registry.ts @@ -1220,6 +1220,10 @@ export function createPluginRegistry(registryParams: PluginRegistryParams) { source: record.source, message: `context engine already registered: ${id} (${result.existingOwner})`, }); + return; + } + if (!record.contextEngineIds?.includes(id)) { + record.contextEngineIds = [...(record.contextEngineIds ?? []), id]; } }, registerCompactionProvider: ( diff --git a/src/plugins/status.test-helpers.ts b/src/plugins/status.test-helpers.ts index d4b0d764dd6..d76edf74555 100644 --- a/src/plugins/status.test-helpers.ts +++ b/src/plugins/status.test-helpers.ts @@ -59,6 +59,7 @@ export function createPluginRecord( musicGenerationProviderIds: [], webFetchProviderIds: [], webSearchProviderIds: [], + contextEngineIds: [], memoryEmbeddingProviderIds: [], agentHarnessIds: [], gatewayMethods: [], diff --git a/src/plugins/status.test.ts b/src/plugins/status.test.ts index ddd2c9968bd..bbed67b61b0 100644 --- a/src/plugins/status.test.ts +++ b/src/plugins/status.test.ts @@ -656,6 +656,32 @@ describe("plugin status reports", () => { expect(inspect.capabilities).toEqual([{ kind: "cli-backend", ids: ["claude-cli"] }]); }); + it("treats a context-engine plugin as a plain capability", () => { + setPluginLoadResult({ + plugins: [ + createPluginRecord({ + id: "moon", + name: "Moon", + kind: "context-engine", + contextEngineIds: ["moon-engine"], + hookCount: 1, + }), + ], + hooks: [createCustomHook({ pluginId: "moon", events: ["message"] })], + }); + + const inspect = expectInspectReport("moon"); + + expectInspectShape(inspect, { + shape: "plain-capability", + capabilityMode: "plain", + capabilityKinds: ["context-engine"], + }); + expect(inspect.capabilities).toEqual([{ kind: "context-engine", ids: ["moon-engine"] }]); + expect(inspect.compatibility).toEqual([]); + expectNoCompatibilityWarnings(); + }); + it("builds compatibility warnings for legacy compatibility paths", () => { setPluginLoadResult({ plugins: [ diff --git a/src/plugins/status.ts b/src/plugins/status.ts index 340c1537bbe..e8fee4010ef 100644 --- a/src/plugins/status.ts +++ b/src/plugins/status.ts @@ -21,6 +21,7 @@ import { resolvePluginRuntimeLoadContext, } from "./runtime/load-context.js"; import { loadPluginMetadataRegistrySnapshot } from "./runtime/metadata-registry-loader.js"; +import { hasKind } from "./slots.js"; import type { PluginHookName } from "./types.js"; export type PluginStatusReport = PluginRegistry & { @@ -37,6 +38,7 @@ export type PluginCapabilityKind = | "image-generation" | "web-search" | "agent-harness" + | "context-engine" | "channel"; export type PluginInspectShape = @@ -249,6 +251,13 @@ function buildCapabilityEntries(plugin: PluginRegistry["plugins"][number]) { { kind: "image-generation" as const, ids: plugin.imageGenerationProviderIds }, { kind: "web-search" as const, ids: plugin.webSearchProviderIds }, { kind: "agent-harness" as const, ids: plugin.agentHarnessIds }, + { + kind: "context-engine" as const, + ids: + plugin.status === "loaded" && hasKind(plugin.kind, "context-engine") + ? (plugin.contextEngineIds ?? []) + : [], + }, { kind: "channel" as const, ids: plugin.channelIds }, ].filter((entry) => entry.ids.length > 0); }