
























@@ -19,6 +19,33 @@ const BUNDLED_TYPED_HOOK_REGISTRATION_FILES = [
1919"extensions/skill-workshop/index.ts",
2020"extensions/thread-ownership/index.ts",
2121] as const;
22+const BUNDLED_TYPED_HOOK_REGISTRATION_GUARDS = {
23+"extensions/acpx/index.ts": ["reply_dispatch"],
24+"extensions/active-memory/index.ts": ["before_prompt_build"],
25+"extensions/diffs/src/plugin.ts": ["before_prompt_build"],
26+"extensions/discord/subagent-hooks-api.ts": [
27+"subagent_delivery_target",
28+"subagent_ended",
29+"subagent_spawning",
30+],
31+"extensions/feishu/subagent-hooks-api.ts": [
32+"subagent_delivery_target",
33+"subagent_ended",
34+"subagent_spawning",
35+],
36+"extensions/matrix/subagent-hooks-api.ts": [
37+"subagent_delivery_target",
38+"subagent_ended",
39+"subagent_spawning",
40+],
41+"extensions/memory-core/src/dreaming.ts": ["before_agent_reply", "gateway_start"],
42+"extensions/memory-lancedb/index.ts": ["agent_end", "before_prompt_build"],
43+"extensions/skill-workshop/index.ts": ["agent_end", "before_prompt_build"],
44+"extensions/thread-ownership/index.ts": ["message_received", "message_sending"],
45+} as const satisfies Record<
46+(typeof BUNDLED_TYPED_HOOK_REGISTRATION_FILES)[number],
47+readonly string[]
48+>;
22492350type FileFilter = {
2451excludeTests?: boolean;
@@ -86,6 +113,13 @@ function collectBundledExtensionImports(source: string): string[] {
86113.filter((specifier): specifier is string => typeof specifier === "string");
87114}
88115116+function collectTypedHookNames(source: string): string[] {
117+return [...source.matchAll(/\bapi\.on\(\s*"([^"]+)"/gu)]
118+.map((match) => match[1])
119+.filter((hookName): hookName is string => typeof hookName === "string")
120+.toSorted();
121+}
122+89123describe("plugin contract boundary invariants", () => {
90124it("keeps bundled-capability-metadata confined to contract/test inventory", () => {
91125const files = listTsFiles("src");
@@ -163,6 +197,17 @@ describe("plugin contract boundary invariants", () => {
163197expect(hookRegistrationFiles).toEqual(BUNDLED_TYPED_HOOK_REGISTRATION_FILES);
164198});
165199200+it("keeps bundled plugin typed hook names on an explicit allowlist", () => {
201+expect(
202+Object.fromEntries(
203+BUNDLED_TYPED_HOOK_REGISTRATION_FILES.map((file) => [
204+file,
205+collectTypedHookNames(readRepoSource(file)),
206+]),
207+),
208+).toEqual(BUNDLED_TYPED_HOOK_REGISTRATION_GUARDS);
209+});
210+166211it("keeps bundled plugin production code off raw registerHook calls", () => {
167212const files = listTsFiles("extensions", { excludeTests: true });
168213const offenders = files.filter((file) => /\bregisterHook\(/u.test(readRepoSource(file)));
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。