



















@@ -5,10 +5,18 @@
55 */
66import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
77import { setEmbeddedMode } from "../infra/embedded-mode.js";
8-import { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
8+import {
9+getGlobalHookRunner,
10+initializeGlobalHookRunner,
11+resetGlobalHookRunner,
12+} from "../plugins/hook-runner-global.js";
913import type { HookRunner } from "../plugins/hooks.js";
1014import { createEmptyPluginRegistry } from "../plugins/registry-empty.js";
11-import { setActivePluginRegistry } from "../plugins/runtime.js";
15+import {
16+pinActivePluginChannelRegistry,
17+releasePinnedPluginChannelRegistry,
18+setActivePluginRegistry,
19+} from "../plugins/runtime.js";
1220import { PluginApprovalResolutions } from "../plugins/types.js";
1321import { runBeforeToolCallHook } from "./agent-tools.before-tool-call.js";
1422import { callGatewayTool } from "./tools/gateway.js";
@@ -69,6 +77,7 @@ describe("runBeforeToolCallHook — embedded mode approvals", () => {
6977let runBeforeToolCallMock: ReturnType<typeof vi.fn<HookRunner["runBeforeToolCall"]>>;
70787179beforeEach(() => {
80+resetGlobalHookRunner();
7281runBeforeToolCallMock = vi.fn<HookRunner["runBeforeToolCall"]>();
7382hookRunner = {
7483hasHooks: vi.fn<HookRunner["hasHooks"]>().mockReturnValue(true),
@@ -82,6 +91,7 @@ describe("runBeforeToolCallHook — embedded mode approvals", () => {
8291afterEach(() => {
8392setEmbeddedMode(false);
8493setActivePluginRegistry(createEmptyPluginRegistry());
94+resetGlobalHookRunner();
8595});
86968797it("blocks approval-required tools in embedded mode when no gateway approval route exists", async () => {
@@ -445,6 +455,94 @@ describe("runBeforeToolCallHook — embedded mode approvals", () => {
445455expect(runBeforeToolCallMock).not.toHaveBeenCalled();
446456});
447457458+it("runs trusted policies from the global hook registry after the active registry changes", async () => {
459+const evaluatePolicy = vi.fn(() => ({
460+block: true,
461+blockReason: "gateway registry policy blocked",
462+}));
463+const gatewayRegistry = createEmptyPluginRegistry();
464+gatewayRegistry.trustedToolPolicies = [
465+{
466+pluginId: "gateway-policy",
467+pluginName: "Gateway Policy",
468+source: "test",
469+policy: {
470+id: "gateway-block",
471+description: "Gateway policy",
472+evaluate: evaluatePolicy,
473+},
474+},
475+];
476+initializeGlobalHookRunner(gatewayRegistry);
477+setActivePluginRegistry(createEmptyPluginRegistry());
478+runBeforeToolCallMock.mockResolvedValue(undefined);
479+480+const result = await runBeforeToolCallHook({
481+toolName: "bash",
482+params: { command: "deploy" },
483+toolCallId: "call-gateway-policy",
484+ctx: { agentId: "main", sessionKey: "main" },
485+});
486+487+expect(result).toEqual({
488+blocked: true,
489+kind: "veto",
490+deniedReason: "plugin-before-tool-call",
491+reason: "gateway registry policy blocked",
492+params: { command: "deploy" },
493+});
494+expect(evaluatePolicy).toHaveBeenCalledTimes(1);
495+expect(runBeforeToolCallMock).not.toHaveBeenCalled();
496+});
497+498+it("runs pinned gateway trusted policies after a later global runner initialization", async () => {
499+const evaluatePolicy = vi.fn(() => ({
500+block: true,
501+blockReason: "pinned gateway policy blocked",
502+}));
503+const gatewayRegistry = createEmptyPluginRegistry();
504+gatewayRegistry.trustedToolPolicies = [
505+{
506+pluginId: "gateway-policy",
507+pluginName: "Gateway Policy",
508+source: "test",
509+policy: {
510+id: "gateway-block",
511+description: "Gateway policy",
512+evaluate: evaluatePolicy,
513+},
514+},
515+];
516+setActivePluginRegistry(gatewayRegistry);
517+initializeGlobalHookRunner(gatewayRegistry);
518+pinActivePluginChannelRegistry(gatewayRegistry);
519+try {
520+const laterRegistry = createEmptyPluginRegistry();
521+setActivePluginRegistry(laterRegistry);
522+initializeGlobalHookRunner(laterRegistry);
523+runBeforeToolCallMock.mockResolvedValue(undefined);
524+525+const result = await runBeforeToolCallHook({
526+toolName: "bash",
527+params: { command: "deploy" },
528+toolCallId: "call-pinned-gateway-policy",
529+ctx: { agentId: "main", sessionKey: "main" },
530+});
531+532+expect(result).toEqual({
533+blocked: true,
534+kind: "veto",
535+deniedReason: "plugin-before-tool-call",
536+reason: "pinned gateway policy blocked",
537+params: { command: "deploy" },
538+});
539+expect(evaluatePolicy).toHaveBeenCalledTimes(1);
540+expect(runBeforeToolCallMock).not.toHaveBeenCalled();
541+} finally {
542+releasePinnedPluginChannelRegistry(gatewayRegistry);
543+}
544+});
545+448546it("does not require skill_workshop lifecycle approval in auto mode", async () => {
449547(hookRunner.hasHooks as ReturnType<typeof vi.fn>).mockReturnValue(false);
450548此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。