mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
refactor: retire due compat-ledger surfaces (context-engine host params, deactivate alias, logging internals) (#121845)
* refactor(plugins): retire deactivate hook alias
* refactor(plugin-sdk): prune retired facade exports
* test(logging): isolate logger test controls
* refactor(logging): internalize file transport controls
* test(plugin-sdk): preserve retired facade coverage
* test(auto-reply): remove stale diagnostic imports
* refactor(logging): delete dead config-read guard
shouldSkipMutatingLoggingConfigRead had no production caller even on main;
it survived the dead-export scan only via logger's testApi re-export. The
test-isolation commit removed that mask, exposing the fossil. Delete the
guard, its test-only re-export, its mock entry, and its dedicated test file.
* refactor(plugin-sdk): retire due compatibility subpaths
* test(plugin-sdk): type group policy predicates
* refactor(plugin-sdk): split removed subpath records
* refactor(secrets): remove retired collector barrel
* test(plugin-sdk): tighten wildcard surface pin
* refactor(plugin-sdk): retire matrix facade metadata
* style(plugin-sdk): format facade metadata
* fix(ci): load channel setup contracts from source
Repair the main-owned regression from 99d662473c (Peter Steinberger): the new env-contract test could consume stale ignored dist metadata instead of the checked-in plugin declaration.
* test(plugin-sdk): refresh API baseline after rebase
This commit is contained in:
committed by
GitHub
parent
49092ebab3
commit
dceb2c343c
@@ -1,16 +1,19 @@
|
||||
import type { PluginCompatRecord } from "./types.js";
|
||||
|
||||
type SeedFields = "code" | "owner" | "removeAfter" | "removalGate" | "replacement";
|
||||
type DeprecatedPluginSdkSubpathSeed = Pick<PluginCompatRecord, SeedFields> &
|
||||
Record<"subpath", string>;
|
||||
type PluginSdkSubpathSeed = Pick<PluginCompatRecord, SeedFields> &
|
||||
Record<"subpath", string> &
|
||||
Partial<Pick<PluginCompatRecord, "status" | "releaseNote">>;
|
||||
|
||||
const DEPRECATED_PLUGIN_SDK_SUBPATH_SEEDS = [
|
||||
const PLUGIN_SDK_SUBPATH_SEEDS = [
|
||||
{
|
||||
code: "plugin-sdk-channel-streaming-subpath",
|
||||
subpath: "channel-streaming",
|
||||
status: "removed",
|
||||
owner: "channel",
|
||||
removeAfter: "2026-08-15",
|
||||
replacement: "`openclaw/plugin-sdk/channel-outbound`",
|
||||
releaseNote:
|
||||
"The deprecated `channel-streaming` Plugin SDK subpath was removed; plugins now import channel streaming helpers from `channel-outbound`.",
|
||||
},
|
||||
{
|
||||
code: "plugin-sdk-config-runtime-subpath",
|
||||
@@ -45,39 +48,49 @@ const DEPRECATED_PLUGIN_SDK_SUBPATH_SEEDS = [
|
||||
{
|
||||
code: "plugin-sdk-text-runtime-subpath",
|
||||
subpath: "text-runtime",
|
||||
status: "removed",
|
||||
owner: "sdk",
|
||||
removeAfter: "2026-08-15",
|
||||
replacement:
|
||||
"`openclaw/plugin-sdk/logging-core`, `openclaw/plugin-sdk/text-chunking`, `openclaw/plugin-sdk/text-utility-runtime`, and `openclaw/plugin-sdk/string-coerce-runtime`",
|
||||
releaseNote:
|
||||
"The deprecated `text-runtime` Plugin SDK facade was removed; plugins now import logging, chunking, text utility, and string coercion helpers from their focused subpaths.",
|
||||
},
|
||||
{
|
||||
code: "plugin-sdk-channel-secret-runtime-subpath",
|
||||
subpath: "channel-secret-runtime",
|
||||
status: "removed",
|
||||
owner: "channel",
|
||||
removeAfter: "2026-08-15",
|
||||
replacement:
|
||||
"`openclaw/plugin-sdk/channel-secret-basic-runtime` and `openclaw/plugin-sdk/channel-secret-tts-runtime`",
|
||||
releaseNote:
|
||||
"The deprecated `channel-secret-runtime` Plugin SDK subpath was removed; plugins now use the focused basic and TTS secret-runtime subpaths.",
|
||||
},
|
||||
{
|
||||
code: "plugin-sdk-agent-config-primitives-subpath",
|
||||
subpath: "agent-config-primitives",
|
||||
status: "removed",
|
||||
owner: "config",
|
||||
removeAfter: "2026-08-15",
|
||||
replacement: "`openclaw/plugin-sdk/channel-config-schema`",
|
||||
releaseNote:
|
||||
"The deprecated `agent-config-primitives` Plugin SDK subpath was removed; plugins now use maintained config-schema primitives.",
|
||||
},
|
||||
{
|
||||
code: "plugin-sdk-matrix-subpath",
|
||||
subpath: "matrix",
|
||||
status: "removed",
|
||||
owner: "channel",
|
||||
removeAfter: "2026-08-15",
|
||||
replacement: "`openclaw/plugin-sdk/run-command`",
|
||||
releaseNote:
|
||||
"The deprecated `matrix` Plugin SDK facade was removed; command execution now uses the generic `run-command` subpath.",
|
||||
},
|
||||
{
|
||||
code: "plugin-sdk-channel-logging-subpath",
|
||||
subpath: "channel-logging",
|
||||
status: "removed",
|
||||
owner: "channel",
|
||||
removeAfter: "2026-08-15",
|
||||
replacement: "`openclaw/plugin-sdk/channel-inbound` and `openclaw/plugin-sdk/channel-outbound`",
|
||||
releaseNote:
|
||||
"The deprecated `channel-logging` Plugin SDK subpath was removed; channel logging helpers now come from the inbound and outbound channel surfaces.",
|
||||
},
|
||||
{
|
||||
code: "plugin-sdk-channel-lifecycle-subpath",
|
||||
@@ -96,38 +109,60 @@ const DEPRECATED_PLUGIN_SDK_SUBPATH_SEEDS = [
|
||||
{
|
||||
code: "plugin-sdk-group-access-subpath",
|
||||
subpath: "group-access",
|
||||
status: "removed",
|
||||
owner: "channel",
|
||||
removeAfter: "2026-08-15",
|
||||
replacement: "`openclaw/plugin-sdk/channel-ingress-runtime`",
|
||||
releaseNote:
|
||||
"The deprecated `group-access` Plugin SDK subpath was removed; plugins now resolve message admission through `channel-ingress-runtime`.",
|
||||
},
|
||||
{
|
||||
code: "plugin-sdk-zod-subpath",
|
||||
subpath: "zod",
|
||||
status: "removed",
|
||||
owner: "sdk",
|
||||
removeAfter: "2026-08-15",
|
||||
replacement: "the direct `zod` package import",
|
||||
releaseNote:
|
||||
"The deprecated `zod` Plugin SDK re-export was removed; plugins now import `zod` directly.",
|
||||
},
|
||||
] as const satisfies readonly DeprecatedPluginSdkSubpathSeed[];
|
||||
] as const satisfies readonly PluginSdkSubpathSeed[];
|
||||
|
||||
export const DEPRECATED_PLUGIN_SDK_SUBPATH_RECORDS = DEPRECATED_PLUGIN_SDK_SUBPATH_SEEDS.map(
|
||||
(seed) =>
|
||||
({
|
||||
function buildPluginSdkSubpathRecord(seed: (typeof PLUGIN_SDK_SUBPATH_SEEDS)[number]) {
|
||||
if ("status" in seed) {
|
||||
return {
|
||||
code: seed.code,
|
||||
status: "deprecated" as const,
|
||||
status: seed.status,
|
||||
owner: seed.owner,
|
||||
introduced: "2026-07-06",
|
||||
deprecated: "2026-07-06",
|
||||
warningStarts: "2026-07-06",
|
||||
removeAfter: "removeAfter" in seed ? seed.removeAfter : undefined,
|
||||
removalGate: "removalGate" in seed ? seed.removalGate : undefined,
|
||||
replacement: seed.replacement,
|
||||
docsPath: "/plugins/sdk-migration",
|
||||
surfaces: [`openclaw/plugin-sdk/${seed.subpath}`],
|
||||
diagnostics: [
|
||||
"repository deprecated API usage guard for core and bundled plugins; no external runtime import warning",
|
||||
],
|
||||
diagnostics: ["plugin SDK compatibility registry and migration guide"],
|
||||
tests: ["src/plugins/compat/registry.test.ts"],
|
||||
}) satisfies PluginCompatRecord,
|
||||
releaseNote: seed.releaseNote,
|
||||
} satisfies PluginCompatRecord;
|
||||
}
|
||||
|
||||
return {
|
||||
code: seed.code,
|
||||
status: "deprecated",
|
||||
owner: seed.owner,
|
||||
introduced: "2026-07-06",
|
||||
deprecated: "2026-07-06",
|
||||
warningStarts: "2026-07-06",
|
||||
removeAfter: "removeAfter" in seed ? seed.removeAfter : undefined,
|
||||
removalGate: "removalGate" in seed ? seed.removalGate : undefined,
|
||||
replacement: seed.replacement,
|
||||
docsPath: "/plugins/sdk-migration",
|
||||
surfaces: [`openclaw/plugin-sdk/${seed.subpath}`],
|
||||
diagnostics: [
|
||||
"repository deprecated API usage guard for core and bundled plugins; no external runtime import warning",
|
||||
],
|
||||
tests: ["src/plugins/compat/registry.test.ts"],
|
||||
} satisfies PluginCompatRecord;
|
||||
}
|
||||
|
||||
export const PLUGIN_SDK_SUBPATH_RECORDS = PLUGIN_SDK_SUBPATH_SEEDS.map(
|
||||
buildPluginSdkSubpathRecord,
|
||||
) satisfies readonly PluginCompatRecord[];
|
||||
|
||||
const BUNDLED_ONLY_PUBLIC_PLUGIN_SDK_SUBPATH_SEEDS = [
|
||||
|
||||
@@ -2,12 +2,12 @@ import { DEPRECATION_MARKING_COMPAT_RECORDS } from "./deprecation-marking.js";
|
||||
import { MEDIA_LEGACY_PROJECTION_COMPAT_RECORD } from "./media-legacy-projection.js";
|
||||
import {
|
||||
BUNDLED_ONLY_PUBLIC_PLUGIN_SDK_SUBPATH_RECORDS,
|
||||
DEPRECATED_PLUGIN_SDK_SUBPATH_RECORDS,
|
||||
PLUGIN_SDK_SUBPATH_RECORDS,
|
||||
} from "./plugin-sdk-subpath-records.js";
|
||||
import type { PluginCompatRecord } from "./types.js";
|
||||
|
||||
export const PLUGIN_COMPAT_RECORDS = [
|
||||
...DEPRECATED_PLUGIN_SDK_SUBPATH_RECORDS,
|
||||
...PLUGIN_SDK_SUBPATH_RECORDS,
|
||||
...BUNDLED_ONLY_PUBLIC_PLUGIN_SDK_SUBPATH_RECORDS,
|
||||
...DEPRECATION_MARKING_COMPAT_RECORDS,
|
||||
MEDIA_LEGACY_PROJECTION_COMPAT_RECORD,
|
||||
@@ -44,19 +44,16 @@ export const PLUGIN_COMPAT_RECORDS = [
|
||||
},
|
||||
{
|
||||
code: "legacy-deactivate-hook-alias",
|
||||
status: "deprecated",
|
||||
status: "removed",
|
||||
owner: "sdk",
|
||||
introduced: "2026-05-16",
|
||||
deprecated: "2026-05-16",
|
||||
warningStarts: "2026-05-16",
|
||||
removeAfter: "2026-08-16",
|
||||
replacement: "`gateway_stop` hook",
|
||||
docsPath: "/plugins/hooks#upcoming-deprecations",
|
||||
docsPath: "/plugins/sdk-migration#deactivate-hook-alias",
|
||||
surfaces: ['api.on("deactivate", ...)', "plugin typed hook registration"],
|
||||
diagnostics: ["plugin runtime compatibility warning"],
|
||||
tests: ["src/plugins/loader.test.ts"],
|
||||
diagnostics: ["plugin compatibility registry and migration guide"],
|
||||
tests: ["src/plugins/compat/registry.test.ts"],
|
||||
releaseNote:
|
||||
'`api.on("deactivate", ...)` remains wired as a deprecated compatibility alias while plugins migrate to `gateway_stop`.',
|
||||
'The deprecated `api.on("deactivate", ...)` hook alias was removed; plugins must register cleanup with `gateway_stop`.',
|
||||
},
|
||||
{
|
||||
code: "legacy-subagent-spawning-hook",
|
||||
|
||||
@@ -25,6 +25,16 @@ const removalDatePendingCompatCodes = new Set<PluginCompatCode>([
|
||||
"plugin-sdk-tool-plugin-public-demotion",
|
||||
"agent-harness-sdk-alias",
|
||||
]);
|
||||
const retiredPluginSdkSubpathCodes = [
|
||||
"plugin-sdk-channel-streaming-subpath",
|
||||
"plugin-sdk-text-runtime-subpath",
|
||||
"plugin-sdk-channel-secret-runtime-subpath",
|
||||
"plugin-sdk-agent-config-primitives-subpath",
|
||||
"plugin-sdk-matrix-subpath",
|
||||
"plugin-sdk-channel-logging-subpath",
|
||||
"plugin-sdk-group-access-subpath",
|
||||
"plugin-sdk-zod-subpath",
|
||||
] as const satisfies readonly PluginCompatCode[];
|
||||
const deprecationMarkingCodes = [
|
||||
"plugin-sdk-channel-setup-input-fields",
|
||||
"plugin-sdk-broad-runtime-barrels",
|
||||
@@ -137,6 +147,18 @@ describe("plugin compatibility registry", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps retired Plugin SDK subpaths as migration tombstones", () => {
|
||||
const records = new Map(listPluginCompatRecords().map((record) => [record.code, record]));
|
||||
|
||||
for (const code of retiredPluginSdkSubpathCodes) {
|
||||
expect(records.get(code)).toMatchObject({
|
||||
status: "removed",
|
||||
releaseNote: expect.stringMatching(/\S/u),
|
||||
});
|
||||
expect(records.get(code)?.removeAfter, code).toBeUndefined();
|
||||
}
|
||||
});
|
||||
|
||||
it("tracks the deprecation-marking families through the approved window", () => {
|
||||
const records = new Map(listPluginCompatRecords().map((record) => [record.code, record]));
|
||||
|
||||
@@ -189,6 +211,18 @@ describe("plugin compatibility registry", () => {
|
||||
expect(record?.removeAfter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps the removed deactivate hook alias as a migration tombstone", () => {
|
||||
const record = listPluginCompatRecords().find(
|
||||
(candidate) => candidate.code === "legacy-deactivate-hook-alias",
|
||||
);
|
||||
|
||||
expect(record).toMatchObject({
|
||||
status: "removed",
|
||||
replacement: "`gateway_stop` hook",
|
||||
});
|
||||
expect(record?.removeAfter).toBeUndefined();
|
||||
});
|
||||
|
||||
it("keeps deprecated explicit target parser calls inside compatibility shims", () => {
|
||||
expect(deprecatedTargetParserOffenders).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -227,12 +227,6 @@ describe("opt-in extension package boundaries", () => {
|
||||
expect(packageJson.exports?.["./acp-runtime"]?.types).toBe(
|
||||
"./dist/src/plugin-sdk/acp-runtime.d.ts",
|
||||
);
|
||||
expect(packageJson.exports?.["./channel-secret-runtime"]?.types).toBe(
|
||||
"./dist/src/plugin-sdk/channel-secret-runtime.d.ts",
|
||||
);
|
||||
expect(packageJson.exports?.["./channel-streaming"]?.types).toBe(
|
||||
"./dist/src/plugin-sdk/channel-streaming.d.ts",
|
||||
);
|
||||
expect(packageJson.exports?.["./cli-runtime"]?.types).toBe(
|
||||
"./dist/src/plugin-sdk/cli-runtime.d.ts",
|
||||
);
|
||||
@@ -291,10 +285,6 @@ describe("opt-in extension package boundaries", () => {
|
||||
expect(packageJson.exports?.["./infra-runtime"]?.types).toBe(
|
||||
"./dist/src/plugin-sdk/infra-runtime.d.ts",
|
||||
);
|
||||
expect(packageJson.exports?.["./text-runtime"]?.types).toBe(
|
||||
"./dist/src/plugin-sdk/text-runtime.d.ts",
|
||||
);
|
||||
expect(packageJson.exports?.["./zod"]?.types).toBe("./dist/src/plugin-sdk/zod.d.ts");
|
||||
expect(fs.existsSync(resolve(REPO_ROOT, "packages/plugin-sdk/types/plugin-entry.d.ts"))).toBe(
|
||||
false,
|
||||
);
|
||||
|
||||
@@ -667,7 +667,6 @@ describe("plugin-sdk subpath exports", () => {
|
||||
"createChannelHistoryWindow",
|
||||
"recordPendingHistoryEntryIfEnabled",
|
||||
]);
|
||||
expectSourceMentions("matrix", ["runPluginCommandWithTimeout"]);
|
||||
expectSourceContract("reply-runtime", {
|
||||
omits: [
|
||||
"buildPendingHistoryContextFromMap",
|
||||
@@ -694,25 +693,6 @@ describe("plugin-sdk subpath exports", () => {
|
||||
],
|
||||
omits: ["collectNestedChannelTtsAssignments"],
|
||||
});
|
||||
expectSourceContract("channel-secret-runtime", {
|
||||
mentions: [
|
||||
"collectSimpleChannelFieldAssignments",
|
||||
"collectConditionalChannelFieldAssignments",
|
||||
"collectSecretInputAssignment",
|
||||
"getChannelSurface",
|
||||
"pushAssignment",
|
||||
"pushInactiveSurfaceWarning",
|
||||
"ResolverContext",
|
||||
"SecretTargetRegistryEntry",
|
||||
],
|
||||
omits: [
|
||||
"buildChannelMetadata",
|
||||
"buildUntrustedChannelMetadata",
|
||||
"evaluateSupplementalContextVisibility",
|
||||
"resolvePinnedMainDmOwnerFromAllowlist",
|
||||
"safeMatchRegex",
|
||||
],
|
||||
});
|
||||
expectSourceContract("channel-secret-tts-runtime", {
|
||||
mentions: ["collectNestedChannelTtsAssignments"],
|
||||
omits: ["collectSimpleChannelFieldAssignments", "collectConditionalChannelFieldAssignments"],
|
||||
@@ -1160,8 +1140,6 @@ describe("plugin-sdk subpath exports", () => {
|
||||
]);
|
||||
expectRepoSourceOmitsSnippet("src/channels/ack-reactions.ts", "shouldAckReactionForWhatsApp");
|
||||
expectRepoSourceOmitsSnippet("src/channels/ack-reactions.ts", "WhatsAppAckReactionMode");
|
||||
expectSourceMentions("channel-streaming", ["SlackChannelStreamingConfig"]);
|
||||
expectRepoSourceOmitsSnippet("src/channels/streaming.ts", "SlackChannelStreamingConfig");
|
||||
expectSourceMentions("status-helpers", [
|
||||
"appendMatchMetadata",
|
||||
"asString",
|
||||
|
||||
@@ -129,8 +129,6 @@ export type PluginHookName =
|
||||
| "subagent_spawned"
|
||||
| "subagent_progress"
|
||||
| "subagent_ended"
|
||||
/** @deprecated Use gateway_stop. */
|
||||
| "deactivate"
|
||||
| "gateway_start"
|
||||
| "gateway_stop"
|
||||
| "heartbeat_prompt_contribution"
|
||||
@@ -176,7 +174,6 @@ const PLUGIN_HOOK_NAMES = [
|
||||
"subagent_spawned",
|
||||
"subagent_progress",
|
||||
"subagent_ended",
|
||||
"deactivate",
|
||||
"gateway_start",
|
||||
"gateway_stop",
|
||||
"heartbeat_prompt_contribution",
|
||||
@@ -197,7 +194,7 @@ type AssertAllPluginHookNamesListed = MissingPluginHookNames extends never ? tru
|
||||
const assertAllPluginHookNamesListed: AssertAllPluginHookNamesListed = true;
|
||||
void assertAllPluginHookNamesListed;
|
||||
|
||||
type DeprecatedPluginHookName = "subagent_spawning" | "deactivate";
|
||||
type DeprecatedPluginHookName = "subagent_spawning";
|
||||
|
||||
type PluginHookDeprecation = {
|
||||
replacement: string;
|
||||
@@ -231,11 +228,6 @@ export const DEPRECATED_PLUGIN_HOOKS = {
|
||||
"Core prepares thread-bound subagent bindings through channel session-binding adapters before `subagent_spawned` fires.",
|
||||
removeAfter: "2026-08-30",
|
||||
},
|
||||
deactivate: {
|
||||
replacement: "`gateway_stop`",
|
||||
reason: "`deactivate` is a legacy cleanup hook alias for `gateway_stop`.",
|
||||
removeAfter: "2026-08-16",
|
||||
},
|
||||
} as const satisfies Record<DeprecatedPluginHookName, PluginHookDeprecation>;
|
||||
|
||||
const DEPRECATED_PLUGIN_HOOK_NAMES = Object.keys(
|
||||
@@ -1375,19 +1367,6 @@ export type PluginHookHandlerMap = {
|
||||
event: PluginHookSubagentEndedEvent,
|
||||
ctx: PluginHookSubagentContext,
|
||||
) => Promise<void> | void;
|
||||
/**
|
||||
* Deprecated compatibility alias for gateway_stop.
|
||||
*
|
||||
* New plugins should register gateway_stop directly; the loader normalizes
|
||||
* deactivate registrations onto gateway_stop so cleanup handlers still run
|
||||
* during Gateway shutdown.
|
||||
*
|
||||
* @deprecated Use gateway_stop.
|
||||
*/
|
||||
deactivate: (
|
||||
event: PluginHookGatewayStopEvent,
|
||||
ctx: PluginHookGatewayContext,
|
||||
) => Promise<void> | void;
|
||||
gateway_start: (
|
||||
event: PluginHookGatewayStartEvent,
|
||||
ctx: PluginHookGatewayContext,
|
||||
|
||||
@@ -1464,45 +1464,6 @@ ${channelPluginSource({
|
||||
]);
|
||||
});
|
||||
|
||||
it("normalizes legacy deactivate typed hooks onto gateway_stop", () => {
|
||||
useNoBundledPlugins();
|
||||
const plugin = writePlugin({
|
||||
id: "legacy-deactivate-hook",
|
||||
filename: "legacy-deactivate-hook.cjs",
|
||||
body: `module.exports = { id: "legacy-deactivate-hook", register(api) {
|
||||
api.on("deactivate", () => undefined);
|
||||
} };`,
|
||||
});
|
||||
|
||||
const registry = loadRegistryFromSinglePlugin({
|
||||
plugin,
|
||||
pluginConfig: {
|
||||
allow: ["legacy-deactivate-hook"],
|
||||
entries: {
|
||||
"legacy-deactivate-hook": {
|
||||
hooks: {
|
||||
timeoutMs: 250,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(registry.plugins.find((entry) => entry.id === "legacy-deactivate-hook")?.status).toBe(
|
||||
"loaded",
|
||||
);
|
||||
expect(registry.typedHooks.map((entry) => entry.hookName)).toEqual(["gateway_stop"]);
|
||||
expect(registry.typedHooks[0]?.timeoutMs).toBe(250);
|
||||
expect(
|
||||
registry.diagnostics.some(
|
||||
(diag) =>
|
||||
diag.pluginId === "legacy-deactivate-hook" &&
|
||||
diag.message ===
|
||||
'typed hook "deactivate" is deprecated (legacy-deactivate-hook-alias); use "gateway_stop". This compatibility alias will be removed after 2026-08-16.',
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("warns when plugins register deprecated subagent_spawning typed hooks", () => {
|
||||
useNoBundledPlugins();
|
||||
const plugin = writePlugin({
|
||||
|
||||
@@ -52,7 +52,6 @@ import type {
|
||||
PluginHookRegistration as TypedPluginHookRegistration,
|
||||
} from "./types.js";
|
||||
|
||||
const LEGACY_DEACTIVATE_HOOK_ALIAS_COMPAT = getPluginCompatRecord("legacy-deactivate-hook-alias");
|
||||
const LEGACY_SUBAGENT_SPAWNING_HOOK_COMPAT = getPluginCompatRecord("legacy-subagent-spawning-hook");
|
||||
|
||||
function normalizeEligibleTriggers(value: unknown) {
|
||||
@@ -66,17 +65,8 @@ function normalizeEligibleTriggers(value: unknown) {
|
||||
return uniqueValues(triggers);
|
||||
}
|
||||
|
||||
function formatLegacyDeactivateHookAliasDiagnostic(): string {
|
||||
const removeAfter =
|
||||
LEGACY_DEACTIVATE_HOOK_ALIAS_COMPAT.removeAfter ?? "a future breaking release";
|
||||
return (
|
||||
`typed hook "deactivate" is deprecated (${LEGACY_DEACTIVATE_HOOK_ALIAS_COMPAT.code}); ` +
|
||||
`use "gateway_stop". This compatibility alias will be removed after ${removeAfter}.`
|
||||
);
|
||||
}
|
||||
|
||||
function formatDeprecatedTypedHookDiagnostic(hookName: PluginHookName): string | undefined {
|
||||
if (!isDeprecatedPluginHookName(hookName) || hookName === "deactivate") {
|
||||
if (!isDeprecatedPluginHookName(hookName)) {
|
||||
return undefined;
|
||||
}
|
||||
const deprecation = DEPRECATED_PLUGIN_HOOKS[hookName];
|
||||
@@ -425,36 +415,25 @@ export function createToolHookRegistrars(state: PluginRegistryState) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
const effectiveHookName = hookName === "deactivate" ? "gateway_stop" : hookName;
|
||||
if (hookName === "deactivate") {
|
||||
const diagnostic = formatDeprecatedTypedHookDiagnostic(hookName);
|
||||
if (diagnostic) {
|
||||
pushDiagnostic({
|
||||
level: "warn",
|
||||
pluginId: record.id,
|
||||
source: record.source,
|
||||
message: formatLegacyDeactivateHookAliasDiagnostic(),
|
||||
message: diagnostic,
|
||||
});
|
||||
} else {
|
||||
const diagnostic = formatDeprecatedTypedHookDiagnostic(hookName);
|
||||
if (diagnostic) {
|
||||
pushDiagnostic({
|
||||
level: "warn",
|
||||
pluginId: record.id,
|
||||
source: record.source,
|
||||
message: diagnostic,
|
||||
});
|
||||
}
|
||||
}
|
||||
const effectiveHandler = handler;
|
||||
if (policy?.allowPromptInjection === false && isPromptInjectionHookName(effectiveHookName)) {
|
||||
if (policy?.allowPromptInjection === false && isPromptInjectionHookName(hookName)) {
|
||||
pushDiagnostic({
|
||||
level: "warn",
|
||||
pluginId: record.id,
|
||||
source: record.source,
|
||||
message: `typed hook "${effectiveHookName}" blocked by plugins.entries.${record.id}.hooks.allowPromptInjection=false`,
|
||||
message: `typed hook "${hookName}" blocked by plugins.entries.${record.id}.hooks.allowPromptInjection=false`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (isConversationHookName(effectiveHookName)) {
|
||||
if (isConversationHookName(hookName)) {
|
||||
const explicitConversationAccess = policy?.allowConversationAccess;
|
||||
if (record.origin !== "bundled" && explicitConversationAccess !== true) {
|
||||
pushDiagnostic({
|
||||
@@ -462,7 +441,7 @@ export function createToolHookRegistrars(state: PluginRegistryState) {
|
||||
pluginId: record.id,
|
||||
source: record.source,
|
||||
message:
|
||||
`typed hook "${effectiveHookName}" blocked because non-bundled plugins must set ` +
|
||||
`typed hook "${hookName}" blocked because non-bundled plugins must set ` +
|
||||
`plugins.entries.${record.id}.hooks.allowConversationAccess=true`,
|
||||
});
|
||||
return;
|
||||
@@ -472,38 +451,34 @@ export function createToolHookRegistrars(state: PluginRegistryState) {
|
||||
level: "warn",
|
||||
pluginId: record.id,
|
||||
source: record.source,
|
||||
message: `typed hook "${effectiveHookName}" blocked by plugins.entries.${record.id}.hooks.allowConversationAccess=false`,
|
||||
message: `typed hook "${hookName}" blocked by plugins.entries.${record.id}.hooks.allowConversationAccess=false`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
const timeoutMs = resolveTypedHookTimeoutMs({ hookName: effectiveHookName, opts, policy });
|
||||
const timeoutMs = resolveTypedHookTimeoutMs({ hookName, opts, policy });
|
||||
const eligibleTriggers =
|
||||
effectiveHookName === "before_agent_reply"
|
||||
hookName === "before_agent_reply"
|
||||
? normalizeEligibleTriggers(opts?.eligibleTriggers)
|
||||
: undefined;
|
||||
const matcher =
|
||||
effectiveHookName === "before_tool_call" || effectiveHookName === "after_tool_call"
|
||||
hookName === "before_tool_call" || hookName === "after_tool_call"
|
||||
? normalizePluginToolMatcher(opts?.matcher)
|
||||
: undefined;
|
||||
if (
|
||||
opts?.matcher &&
|
||||
effectiveHookName !== "before_tool_call" &&
|
||||
effectiveHookName !== "after_tool_call"
|
||||
) {
|
||||
if (opts?.matcher && hookName !== "before_tool_call" && hookName !== "after_tool_call") {
|
||||
pushDiagnostic({
|
||||
level: "warn",
|
||||
pluginId: record.id,
|
||||
source: record.source,
|
||||
message: `typed hook "${effectiveHookName}" ignores tool matcher`,
|
||||
message: `typed hook "${hookName}" ignores tool matcher`,
|
||||
});
|
||||
}
|
||||
record.hookCount += 1;
|
||||
registry.typedHooks.push({
|
||||
pluginId: record.id,
|
||||
...(opts?.registrationId ? { registrationId: opts.registrationId } : {}),
|
||||
hookName: effectiveHookName,
|
||||
handler: effectiveHandler,
|
||||
hookName,
|
||||
handler,
|
||||
...(matcher ? { matcher } : {}),
|
||||
priority: opts?.priority,
|
||||
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
||||
|
||||
Reference in New Issue
Block a user