diff --git a/docs/plugins/architecture-internals.md b/docs/plugins/architecture-internals.md index 98f04e25933..4b3ca1ff1a7 100644 --- a/docs/plugins/architecture-internals.md +++ b/docs/plugins/architecture-internals.md @@ -61,10 +61,8 @@ to narrow plugin loading before broader registry materialization: - explicit provider setup/runtime resolution narrows to plugins that own the requested provider id - Gateway startup planning uses `activation.onStartup` for explicit startup - imports and startup opt-outs; every plugin should declare it as OpenClaw - moves away from implicit startup imports, while plugins without static - capability metadata and without `activation.onStartup` still use the - deprecated implicit startup sidecar fallback for compatibility + imports and startup opt-outs; plugins without startup metadata load only + through narrower activation triggers The activation planner exposes both an ids-only API for existing callers and a plan API for new diagnostics. Plan entries report why a plugin was selected, diff --git a/docs/plugins/compatibility.md b/docs/plugins/compatibility.md index b0359f47978..e2b50c6d1eb 100644 --- a/docs/plugins/compatibility.md +++ b/docs/plugins/compatibility.md @@ -135,9 +135,6 @@ Current compatibility records include: - legacy channel route key and comparable-target helper aliases while plugins move to `openclaw/plugin-sdk/channel-route` - activation hints that are being replaced by manifest contribution ownership -- deprecated implicit startup sidecar loading for plugins that have not declared - `activation.onStartup`; maintainers can test the future stricter behavior with - `OPENCLAW_DISABLE_LEGACY_IMPLICIT_STARTUP_SIDECARS=1` - `setup-api` runtime fallback while setup descriptors move to cold `setup.requiresRuntime: false` metadata - provider `discovery` hooks while provider catalog hooks move to diff --git a/docs/plugins/manifest.md b/docs/plugins/manifest.md index 93b3af3ebe3..d8cbff721f7 100644 --- a/docs/plugins/manifest.md +++ b/docs/plugins/manifest.md @@ -255,24 +255,15 @@ embedded agent harness ids that do not already have an ownership field. This block is metadata only. It does not register runtime behavior, and it does not replace `register(...)`, `setupEntry`, or other runtime/plugin entrypoints. Current consumers use it as a narrowing hint before broader plugin loading, so -missing activation metadata usually only costs performance; it should not -change correctness while legacy manifest ownership fallbacks still exist. +missing non-startup activation metadata usually only costs performance; it +should not change correctness while manifest ownership fallbacks still exist. -Every plugin should set `activation.onStartup` intentionally as OpenClaw moves -away from implicit startup imports. Set it to `true` only when the plugin must -run during Gateway startup. Set it to `false` when the plugin is inert at -startup and should load only from narrower triggers. Omitting `onStartup` keeps -the deprecated legacy implicit startup sidecar fallback for plugins with no -static capability metadata; future versions may stop startup-loading those -plugins unless they declare `activation.onStartup: true`. Plugin status and -compatibility reports warn with `legacy-implicit-startup-sidecar` when a plugin -still relies on that fallback. - -For migration testing, set -`OPENCLAW_DISABLE_LEGACY_IMPLICIT_STARTUP_SIDECARS=1` to disable only that -deprecated fallback. This opt-in mode does not block explicit -`activation.onStartup: true` plugins or plugins loaded by channel, config, -agent-harness, memory, or other narrower activation triggers. +Every plugin should set `activation.onStartup` intentionally. Set it to `true` +only when the plugin must run during Gateway startup. Set it to `false` when +the plugin is inert at startup and should load only from narrower triggers. +Omitting `onStartup` no longer startup-loads the plugin implicitly; use explicit +activation metadata for startup, channel, config, agent-harness, memory, or +other narrower activation triggers. ```json { @@ -288,21 +279,21 @@ agent-harness, memory, or other narrower activation triggers. } ``` -| Field | Required | Type | What it means | -| ------------------ | -------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `onStartup` | No | `boolean` | Explicit Gateway startup activation. Every plugin should set this. `true` imports the plugin during startup; `false` opts out of the deprecated implicit sidecar startup fallback unless another matched trigger requires loading. | -| `onProviders` | No | `string[]` | Provider ids that should include this plugin in activation/load plans. | -| `onAgentHarnesses` | No | `string[]` | Embedded agent harness runtime ids that should include this plugin in activation/load plans. Use top-level `cliBackends` for CLI backend aliases. | -| `onCommands` | No | `string[]` | Command ids that should include this plugin in activation/load plans. | -| `onChannels` | No | `string[]` | Channel ids that should include this plugin in activation/load plans. | -| `onRoutes` | No | `string[]` | Route kinds that should include this plugin in activation/load plans. | -| `onConfigPaths` | No | `string[]` | Root-relative config paths that should include this plugin in startup/load plans when the path is present and not explicitly disabled. | -| `onCapabilities` | No | `Array<"provider" \| "channel" \| "tool" \| "hook">` | Broad capability hints used by control-plane activation planning. Prefer narrower fields when possible. | +| Field | Required | Type | What it means | +| ------------------ | -------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `onStartup` | No | `boolean` | Explicit Gateway startup activation. Every plugin should set this. `true` imports the plugin during startup; `false` keeps it startup-lazy unless another matched trigger requires loading. | +| `onProviders` | No | `string[]` | Provider ids that should include this plugin in activation/load plans. | +| `onAgentHarnesses` | No | `string[]` | Embedded agent harness runtime ids that should include this plugin in activation/load plans. Use top-level `cliBackends` for CLI backend aliases. | +| `onCommands` | No | `string[]` | Command ids that should include this plugin in activation/load plans. | +| `onChannels` | No | `string[]` | Channel ids that should include this plugin in activation/load plans. | +| `onRoutes` | No | `string[]` | Route kinds that should include this plugin in activation/load plans. | +| `onConfigPaths` | No | `string[]` | Root-relative config paths that should include this plugin in startup/load plans when the path is present and not explicitly disabled. | +| `onCapabilities` | No | `Array<"provider" \| "channel" \| "tool" \| "hook">` | Broad capability hints used by control-plane activation planning. Prefer narrower fields when possible. | Current live consumers: - Gateway startup planning uses `activation.onStartup` for explicit startup - import and opt-out of deprecated implicit sidecar startup fallback + import - command-triggered CLI planning falls back to legacy `commandAliases[].cliCommand` or `commandAliases[].name` - agent-runtime startup planning uses `activation.onAgentHarnesses` for diff --git a/scripts/bench-gateway-startup.ts b/scripts/bench-gateway-startup.ts index c936a4aa64a..034d7648b53 100644 --- a/scripts/bench-gateway-startup.ts +++ b/scripts/bench-gateway-startup.ts @@ -137,16 +137,6 @@ const GATEWAY_CASES: readonly GatewayBenchCase[] = [ pluginCount: 50, config: BASE_CONFIG, }, - { - id: "fiftyPluginsFutureStrict", - name: "gateway, 50 manifest plugins with legacy startup fallback disabled", - env: { - OPENCLAW_DISABLE_LEGACY_IMPLICIT_STARTUP_SIDECARS: "1", - OPENCLAW_SKIP_CHANNELS: "1", - }, - pluginCount: 50, - config: BASE_CONFIG, - }, { id: "fiftyStartupLazyPlugins", name: "gateway, 50 startup-lazy manifest plugins", diff --git a/src/gateway/gateway.test.ts b/src/gateway/gateway.test.ts index 87a506e873d..49172d80ad2 100644 --- a/src/gateway/gateway.test.ts +++ b/src/gateway/gateway.test.ts @@ -52,6 +52,7 @@ async function writeWorkspacePlugin(params: { workspaceDir: string; id: string; body: string; + activation?: { onStartup?: boolean }; }): Promise { const pluginDir = path.join(params.workspaceDir, ".openclaw", "extensions", params.id); await fs.mkdir(pluginDir, { recursive: true }); @@ -60,6 +61,7 @@ async function writeWorkspacePlugin(params: { `${JSON.stringify( { id: params.id, + ...(params.activation ? { activation: params.activation } : {}), configSchema: { type: "object", additionalProperties: false, properties: {} }, }, null, @@ -233,6 +235,7 @@ describe("gateway e2e", () => { await writeWorkspacePlugin({ workspaceDir, id: "http-probe", + activation: { onStartup: true }, body: ` const fs = require("node:fs"); const counterPath = ${JSON.stringify(registerCountPath)}; diff --git a/src/plugins/channel-plugin-ids.test.ts b/src/plugins/channel-plugin-ids.test.ts index d89fd770189..77571b7d0b6 100644 --- a/src/plugins/channel-plugin-ids.test.ts +++ b/src/plugins/channel-plugin-ids.test.ts @@ -104,6 +104,7 @@ function createManifestRegistryFixture(): PluginManifestRegistry { id: "browser", channels: [], activation: { + onStartup: true, onConfigPaths: ["browser"], }, origin: "bundled", @@ -212,6 +213,9 @@ function createManifestRegistryFixture(): PluginManifestRegistry { { id: "voice-call", channels: [], + activation: { + onStartup: true, + }, origin: "bundled", enabledByDefault: undefined, providers: [], @@ -238,6 +242,9 @@ function createManifestRegistryFixture(): PluginManifestRegistry { { id: "demo-global-sidecar", channels: [], + activation: { + onStartup: true, + }, origin: "global", enabledByDefault: undefined, providers: [], @@ -295,10 +302,6 @@ function normalizeStartupAgentHarnesses(record: PluginManifestRecord): readonly ].toSorted((left, right) => left.localeCompare(right)); } -function hasRuntimeContractSurface(record: PluginManifestRecord): boolean { - return record.providers.length > 0 || record.cliBackends.length > 0; -} - function hasPluginKind(record: PluginManifestRecord, kind: string): boolean { return Array.isArray(record.kind) ? record.kind.includes(kind as never) : record.kind === kind; } @@ -317,12 +320,7 @@ function createInstalledPluginRecordFixture( enabled: true, ...(record.enabledByDefault === true ? { enabledByDefault: true } : {}), startup: { - sidecar: - record.activation?.onStartup === true || - (record.activation?.onStartup === undefined && - record.channels.length === 0 && - !hasRuntimeContractSurface(record) && - !memory), + sidecar: record.activation?.onStartup === true, memory, deferConfiguredChannelFullLoadUntilAfterListen: record.startupDeferConfiguredChannelFullLoadUntilAfterListen === true, @@ -654,35 +652,7 @@ describe("resolveGatewayStartupPluginIds", () => { }); }); - it("keeps deprecated implicit startup sidecar fallback for legacy plugins", () => { - expectStartupPluginIdsCase({ - config: createStartupConfig({ - enabledPluginIds: ["demo-global-sidecar"], - allowPluginIds: ["demo-global-sidecar"], - noConfiguredChannels: true, - memorySlot: "none", - }), - expected: ["demo-global-sidecar"], - }); - }); - - it("can disable deprecated implicit startup sidecar fallback for future-mode testing", () => { - expectStartupPluginIdsCase({ - config: createStartupConfig({ - enabledPluginIds: ["demo-global-sidecar"], - allowPluginIds: ["demo-global-sidecar"], - noConfiguredChannels: true, - memorySlot: "none", - }), - env: { - ...process.env, - OPENCLAW_DISABLE_LEGACY_IMPLICIT_STARTUP_SIDECARS: "1", - }, - expected: [], - }); - }); - - it("skips deprecated implicit startup sidecar fallback when activation.onStartup is false", () => { + it("skips startup when activation.onStartup is false", () => { expectStartupPluginIdsCase({ config: createStartupConfig({ enabledPluginIds: ["demo-global-startup-opt-out"], @@ -702,10 +672,6 @@ describe("resolveGatewayStartupPluginIds", () => { noConfiguredChannels: true, memorySlot: "none", }), - env: { - ...process.env, - OPENCLAW_DISABLE_LEGACY_IMPLICIT_STARTUP_SIDECARS: "1", - }, expected: ["demo-global-explicit-startup"], }); }); diff --git a/src/plugins/compat/registry.ts b/src/plugins/compat/registry.ts index 0a8966c95f3..8680af64aea 100644 --- a/src/plugins/compat/registry.ts +++ b/src/plugins/compat/registry.ts @@ -277,21 +277,6 @@ export const PLUGIN_COMPAT_RECORDS = [ diagnostics: ["activation plan compat reason"], tests: ["src/plugins/activation-planner.test.ts"], }, - { - code: "legacy-implicit-startup-sidecar", - status: "deprecated", - owner: "plugin-execution", - introduced: "2026-04-28", - deprecated: "2026-04-28", - warningStarts: "2026-04-28", - removeAfter: "2026-07-28", - replacement: - "`activation.onStartup: true` for startup work or `activation.onStartup: false` for inert plugins", - docsPath: "/plugins/manifest", - surfaces: ["Gateway startup plugin planning", "openclaw.plugin.json activation"], - diagnostics: ["plugin compatibility notice"], - tests: ["src/plugins/channel-plugin-ids.test.ts", "src/plugins/installed-plugin-index.test.ts"], - }, { code: "activation-provider-hint", status: "active", diff --git a/src/plugins/gateway-startup-plugin-ids.ts b/src/plugins/gateway-startup-plugin-ids.ts index 8523aa9845a..5c08fb8b8fc 100644 --- a/src/plugins/gateway-startup-plugin-ids.ts +++ b/src/plugins/gateway-startup-plugin-ids.ts @@ -23,18 +23,6 @@ import { } from "./plugin-registry-contributions.js"; import { loadPluginRegistrySnapshot } from "./plugin-registry-snapshot.js"; -const DISABLE_LEGACY_IMPLICIT_STARTUP_SIDECARS_ENV = - "OPENCLAW_DISABLE_LEGACY_IMPLICIT_STARTUP_SIDECARS"; - -function isTruthyEnvValue(value: string | undefined): boolean { - const normalized = value?.trim().toLowerCase(); - return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on"; -} - -function shouldDisableLegacyImplicitStartupSidecars(env: NodeJS.ProcessEnv): boolean { - return isTruthyEnvValue(env[DISABLE_LEGACY_IMPLICIT_STARTUP_SIDECARS_ENV]); -} - function isRecord(value: unknown): value is Record { return Boolean(value && typeof value === "object" && !Array.isArray(value)); } @@ -60,18 +48,6 @@ function isGatewayStartupMemoryPlugin(plugin: InstalledPluginIndexRecord): boole return plugin.startup.memory; } -/** - * @deprecated Compatibility fallback for plugins that do not declare - * `activation.onStartup`. Keep this path visible so we can remove it after - * plugin manifests migrate to explicit startup activation. - */ -function isDeprecatedLegacyImplicitStartupSidecar(params: { - plugin: InstalledPluginIndexRecord; - manifest: PluginManifestRecord | undefined; -}): boolean { - return params.plugin.startup.sidecar && params.manifest?.activation?.onStartup === undefined; -} - function resolveGatewayStartupDreamingPluginIds(config: OpenClawConfig): Set { const dreamingConfig = resolveMemoryDreamingConfig({ pluginConfig: resolveMemoryDreamingPluginConfig(config), @@ -112,29 +88,12 @@ function resolveMemorySlotStartupPluginId(params: { function shouldConsiderForGatewayStartup(params: { plugin: InstalledPluginIndexRecord; manifest: PluginManifestRecord | undefined; - disableLegacyImplicitStartupSidecars: boolean; startupDreamingPluginIds: ReadonlySet; memorySlotStartupPluginId?: string; }): boolean { if (params.manifest?.activation?.onStartup === true) { return true; } - if (params.plugin.startup.sidecar) { - if (params.manifest?.activation?.onStartup === false) { - return false; - } - if (params.disableLegacyImplicitStartupSidecars) { - return false; - } - // Deprecated compatibility fallback: plugins without explicit startup - // activation metadata may still need startup import to register hooks or - // services. All plugins should declare activation.onStartup explicitly as - // we migrate away from implicit startup sidecar loading. - return isDeprecatedLegacyImplicitStartupSidecar({ - plugin: params.plugin, - manifest: params.manifest, - }); - } if (!isGatewayStartupMemoryPlugin(params.plugin)) { return false; } @@ -391,9 +350,6 @@ export function resolveGatewayStartupPluginIdsFromRegistry(params: { collectConfiguredAgentHarnessRuntimes(activationSourceConfig, params.env), ); const startupDreamingPluginIds = resolveGatewayStartupDreamingPluginIds(params.config); - const disableLegacyImplicitStartupSidecars = shouldDisableLegacyImplicitStartupSidecars( - params.env, - ); const manifestLookup = createManifestRegistryLookup(params.manifestRegistry); const memorySlotStartupPluginId = resolveMemorySlotStartupPluginId({ activationSourceConfig, @@ -448,7 +404,6 @@ export function resolveGatewayStartupPluginIdsFromRegistry(params: { !shouldConsiderForGatewayStartup({ plugin, manifest, - disableLegacyImplicitStartupSidecars, startupDreamingPluginIds, memorySlotStartupPluginId, }) diff --git a/src/plugins/install.path.test.ts b/src/plugins/install.path.test.ts index 8c2ebe439bd..2a2445d101a 100644 --- a/src/plugins/install.path.test.ts +++ b/src/plugins/install.path.test.ts @@ -338,7 +338,7 @@ describe("installPluginFromPath", () => { expect(fs.existsSync(path.join(result.targetDir, ".claude-plugin", "plugin.json"))).toBe(true); }); - it("prefers native package metadata for dual-format archives", async () => { + it("prefers native package metadata without installing dependencies for dual-format archives", async () => { const { pluginDir, extensionsDir } = setupDualFormatInstallFixture({ bundleFormat: "claude", }); @@ -371,5 +371,6 @@ describe("installPluginFromPath", () => { expect(result.pluginId).toBe("native-dual"); expect(result.targetDir).toBe(path.join(extensionsDir, "native-dual")); expect(run).not.toHaveBeenCalled(); + expect(fs.existsSync(path.join(result.targetDir, "node_modules"))).toBe(false); }); }); diff --git a/src/plugins/installed-plugin-index-record-builder.ts b/src/plugins/installed-plugin-index-record-builder.ts index 144eef2f356..9df34c47e4b 100644 --- a/src/plugins/installed-plugin-index-record-builder.ts +++ b/src/plugins/installed-plugin-index-record-builder.ts @@ -29,44 +29,9 @@ function sortUnique(values: readonly string[] | undefined): readonly string[] { ); } -function hasRuntimeContractSurface(record: PluginManifestRecord): boolean { - const providers = record.providers ?? []; - const cliBackends = record.cliBackends ?? []; - return Boolean( - providers.length > 0 || - cliBackends.length > 0 || - record.contracts?.speechProviders?.length || - record.contracts?.mediaUnderstandingProviders?.length || - record.contracts?.documentExtractors?.length || - record.contracts?.imageGenerationProviders?.length || - record.contracts?.videoGenerationProviders?.length || - record.contracts?.musicGenerationProviders?.length || - record.contracts?.webContentExtractors?.length || - record.contracts?.webFetchProviders?.length || - record.contracts?.webSearchProviders?.length || - record.contracts?.migrationProviders?.length || - record.contracts?.memoryEmbeddingProviders?.length || - hasKind(record.kind, "memory"), - ); -} - -/** - * @deprecated Compatibility classification for plugins that predate explicit - * `activation.onStartup`. Every plugin manifest should move to an explicit - * startup decision so Gateway boot can avoid importing inert plugins. - */ -function isLegacyImplicitStartupSidecar(record: PluginManifestRecord): boolean { - const channels = Array.isArray(record.channels) ? record.channels : []; - return ( - channels.length === 0 && - !hasRuntimeContractSurface(record) && - record.activation?.onStartup === undefined - ); -} - function buildStartupInfo(record: PluginManifestRecord): InstalledPluginStartupInfo { return { - sidecar: record.activation?.onStartup === true || isLegacyImplicitStartupSidecar(record), + sidecar: record.activation?.onStartup === true, memory: hasKind(record.kind, "memory"), deferConfiguredChannelFullLoadUntilAfterListen: record.startupDeferConfiguredChannelFullLoadUntilAfterListen === true, @@ -81,9 +46,6 @@ export function collectPluginManifestCompatCodes( record: PluginManifestRecord, ): readonly PluginCompatCode[] { const codes: PluginCompatCode[] = []; - if (isLegacyImplicitStartupSidecar(record)) { - codes.push("legacy-implicit-startup-sidecar"); - } if (record.providerAuthEnvVars && Object.keys(record.providerAuthEnvVars).length > 0) { codes.push("provider-auth-env-vars"); } diff --git a/src/plugins/installed-plugin-index.test.ts b/src/plugins/installed-plugin-index.test.ts index 36f1d4ef91b..7e1ac7337eb 100644 --- a/src/plugins/installed-plugin-index.test.ts +++ b/src/plugins/installed-plugin-index.test.ts @@ -270,7 +270,7 @@ describe("installed plugin index", () => { }); }); - it("tags deprecated implicit startup sidecars for legacy plugins", () => { + it("does not classify legacy plugins as startup sidecars", () => { const rootDir = makeTempDir(); writeRuntimeEntry(rootDir); writePluginManifest(rootDir, { @@ -290,9 +290,9 @@ describe("installed plugin index", () => { expect(index.plugins[0]).toMatchObject({ pluginId: "legacy-sidecar", startup: { - sidecar: true, + sidecar: false, }, - compat: ["legacy-implicit-startup-sidecar"], + compat: [], }); }); @@ -326,9 +326,9 @@ describe("installed plugin index", () => { expect(records[0]).toMatchObject({ pluginId: "stale-record", startup: { - sidecar: true, + sidecar: false, }, - compat: ["legacy-implicit-startup-sidecar"], + compat: [], }); }); @@ -394,7 +394,7 @@ describe("installed plugin index", () => { expect(second.plugins[0]?.manifestHash).not.toBe(first.plugins[0]?.manifestHash); }); - it("does not classify or tag explicit startup opt-outs as deprecated implicit sidecars", () => { + it("keeps explicit startup opt-outs out of startup sidecars", () => { const rootDir = makeTempDir(); writeRuntimeEntry(rootDir); writePluginManifest(rootDir, { diff --git a/src/plugins/manifest.ts b/src/plugins/manifest.ts index be331f8dc24..9c8df9a4dc6 100644 --- a/src/plugins/manifest.ts +++ b/src/plugins/manifest.ts @@ -155,11 +155,9 @@ export type PluginManifestActivationCapability = "provider" | "channel" | "tool" export type PluginManifestActivation = { /** - * Explicit Gateway startup activation. Every plugin should set this as - * OpenClaw moves away from implicit startup sidecar loading. Set true when - * the plugin must be imported during Gateway startup; set false to opt out - * of the deprecated implicit startup sidecar fallback when no other - * activation trigger matches. + * Explicit Gateway startup activation. Set true when the plugin must be + * imported during Gateway startup; set false when narrower activation + * triggers should load it on demand. */ onStartup?: boolean; /** diff --git a/src/plugins/status.compatibility.integration.test.ts b/src/plugins/status.compatibility.integration.test.ts index 9e6d3b401c7..addbdb0617e 100644 --- a/src/plugins/status.compatibility.integration.test.ts +++ b/src/plugins/status.compatibility.integration.test.ts @@ -47,18 +47,16 @@ describe("plugin compatibility snapshot notices", () => { cleanupPluginLoaderFixturesForTest(); }); - it("reports implicit startup sidecar compatibility from a real legacy manifest", () => { + it("does not report startup compatibility warnings for legacy manifests", () => { const plugin = writePlugin({ id: "legacy-sidecar", body: `module.exports = { id: "legacy-sidecar", register() {} };\n`, }); - expect(buildSnapshotCompatibilityNoticeCodes(plugin)).toEqual([ - "legacy-implicit-startup-sidecar", - ]); + expect(buildSnapshotCompatibilityNoticeCodes(plugin)).toEqual([]); }); - it("does not report implicit startup compatibility for explicit startup-lazy manifests", () => { + it("does not report startup compatibility warnings for explicit startup-lazy manifests", () => { const plugin = writePlugin({ id: "modern-startup-lazy", body: `module.exports = { id: "modern-startup-lazy", register() {} };\n`, diff --git a/src/plugins/status.test-helpers.ts b/src/plugins/status.test-helpers.ts index 420bbcea184..8d96a8f54ce 100644 --- a/src/plugins/status.test-helpers.ts +++ b/src/plugins/status.test-helpers.ts @@ -5,8 +5,6 @@ import type { PluginHookName } from "./types.js"; export const LEGACY_BEFORE_AGENT_START_MESSAGE = "still uses legacy before_agent_start; keep regression coverage on this plugin, and prefer before_model_resolve/before_prompt_build for new work."; -export const LEGACY_IMPLICIT_STARTUP_SIDECAR_MESSAGE = - "relies on deprecated implicit startup loading; add activation.onStartup: true for startup work or activation.onStartup: false for startup-lazy plugins."; export const HOOK_ONLY_MESSAGE = "is hook-only. This remains a supported compatibility path, but it has not migrated to explicit capability registration yet."; @@ -22,14 +20,6 @@ export function createCompatibilityNotice( severity: "warn", message: LEGACY_BEFORE_AGENT_START_MESSAGE, }; - case "legacy-implicit-startup-sidecar": - return { - pluginId: params.pluginId, - code: params.code, - compatCode: "legacy-implicit-startup-sidecar", - severity: "warn", - message: LEGACY_IMPLICIT_STARTUP_SIDECAR_MESSAGE, - }; case "hook-only": return { pluginId: params.pluginId, diff --git a/src/plugins/status.test.ts b/src/plugins/status.test.ts index 7a9dc4681ad..4258b0bbec1 100644 --- a/src/plugins/status.test.ts +++ b/src/plugins/status.test.ts @@ -7,7 +7,6 @@ import { createTypedHook, HOOK_ONLY_MESSAGE, LEGACY_BEFORE_AGENT_START_MESSAGE, - LEGACY_IMPLICIT_STARTUP_SIDECAR_MESSAGE, } from "./status.test-helpers.js"; const loadConfigMock = vi.fn(); @@ -450,34 +449,34 @@ describe("plugin status reports", () => { loadPluginRegistrySnapshotWithMetadataMock.mockReturnValue({ snapshot: createInstalledPluginIndexSnapshot([ { - pluginId: "legacy-sidecar", - manifestPath: "/tmp/legacy-sidecar/openclaw.plugin.json", + pluginId: "provider-env-plugin", + manifestPath: "/tmp/provider-env-plugin/openclaw.plugin.json", manifestHash: "manifest-hash", - rootDir: "/tmp/legacy-sidecar", + rootDir: "/tmp/provider-env-plugin", origin: "workspace", enabled: true, startup: { - sidecar: true, + sidecar: false, memory: false, deferConfiguredChannelFullLoadUntilAfterListen: false, agentHarnesses: [], }, - compat: ["legacy-implicit-startup-sidecar"], + compat: ["provider-auth-env-vars"], }, ]), source: "derived", diagnostics: [], }); loadPluginManifestRegistryForInstalledIndexMock.mockReturnValue({ - plugins: [{ id: "legacy-sidecar", name: "Legacy Sidecar" }], + plugins: [{ id: "provider-env-plugin", name: "Provider Env Plugin" }], diagnostics: [], }); const report = buildPluginRegistrySnapshotReport({ config: {} }); expect(report.plugins[0]).toMatchObject({ - id: "legacy-sidecar", - compat: ["legacy-implicit-startup-sidecar"], + id: "provider-env-plugin", + compat: ["provider-auth-env-vars"], }); }); @@ -843,27 +842,7 @@ describe("plugin status reports", () => { }); }); - it("builds compatibility warnings for deprecated implicit startup sidecar metadata", () => { - setSinglePluginLoadResult( - createPluginRecord({ - id: "legacy-sidecar", - name: "Legacy Sidecar", - compat: ["legacy-implicit-startup-sidecar"], - }), - ); - - expectCompatibilityOutput({ - notices: [ - createCompatibilityNotice({ - pluginId: "legacy-sidecar", - code: "legacy-implicit-startup-sidecar", - }), - ], - warnings: [`legacy-sidecar ${LEGACY_IMPLICIT_STARTUP_SIDECAR_MESSAGE}`], - }); - }); - - it("does not warn when explicit startup-lazy metadata avoids legacy startup compatibility", () => { + it("does not warn for explicit startup-lazy metadata", () => { setSinglePluginLoadResult( createPluginRecord({ id: "modern-startup-lazy", @@ -939,14 +918,10 @@ describe("plugin status reports", () => { expect( summarizePluginCompatibility([ notice, - createCompatibilityNotice({ - pluginId: "legacy-plugin", - code: "legacy-implicit-startup-sidecar", - }), createCompatibilityNotice({ pluginId: "legacy-plugin", code: "hook-only" }), ]), ).toEqual({ - noticeCount: 3, + noticeCount: 2, pluginCount: 1, }); }); diff --git a/src/plugins/status.ts b/src/plugins/status.ts index e120739590a..a1ed11778fa 100644 --- a/src/plugins/status.ts +++ b/src/plugins/status.ts @@ -53,7 +53,7 @@ export type { PluginCapabilityKind, PluginInspectShape } from "./inspect-shape.j export type PluginCompatibilityNotice = { pluginId: string; - code: "legacy-before-agent-start" | "legacy-implicit-startup-sidecar" | "hook-only"; + code: "legacy-before-agent-start" | "hook-only"; compatCode: PluginCompatCode; severity: "warn" | "info"; message: string; @@ -124,16 +124,6 @@ function buildCompatibilityNoticesForInspect( "still uses legacy before_agent_start; keep regression coverage on this plugin, and prefer before_model_resolve/before_prompt_build for new work.", }); } - if (inspect.plugin.compat?.includes("legacy-implicit-startup-sidecar")) { - warnings.push({ - pluginId: inspect.plugin.id, - code: "legacy-implicit-startup-sidecar", - compatCode: "legacy-implicit-startup-sidecar", - severity: "warn", - message: - "relies on deprecated implicit startup loading; add activation.onStartup: true for startup work or activation.onStartup: false for startup-lazy plugins.", - }); - } if (inspect.shape === "hook-only") { warnings.push({ pluginId: inspect.plugin.id,