fix: before_tool_call hook double-fires with abort signal (#16852)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 6269d617f3
Co-authored-by: sreuter <550246+sreuter@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
This commit is contained in:
Sascha Reuter
2026-02-17 18:23:54 +11:00
committed by GitHub
parent 583844ecf6
commit 60dc3741c0
3 changed files with 21 additions and 1 deletions

View File

@@ -2,6 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import { resetDiagnosticSessionStateForTest } from "../logging/diagnostic-session-state.js";
import { getGlobalHookRunner } from "../plugins/hook-runner-global.js";
import { toClientToolDefinitions, toToolDefinitions } from "./pi-tool-definition-adapter.js";
import { wrapToolWithAbortSignal } from "./pi-tools.abort.js";
import { wrapToolWithBeforeToolCallHook } from "./pi-tools.before-tool-call.js";
vi.mock("../plugins/hook-runner-global.js");
@@ -162,6 +163,24 @@ describe("before_tool_call hook deduplication (#15502)", () => {
expect(hookRunner.runBeforeToolCall).toHaveBeenCalledTimes(1);
});
it("fires hook exactly once when tool goes through wrap + abort + toToolDefinitions", async () => {
const execute = vi.fn().mockResolvedValue({ content: [], details: { ok: true } });
// oxlint-disable-next-line typescript/no-explicit-any
const baseTool = { name: "Bash", execute, description: "bash", parameters: {} } as any;
const abortController = new AbortController();
const wrapped = wrapToolWithBeforeToolCallHook(baseTool, {
agentId: "main",
sessionKey: "main",
});
const withAbort = wrapToolWithAbortSignal(wrapped, abortController.signal);
const [def] = toToolDefinitions([withAbort]);
await def.execute("call-abort-dedup", { command: "ls" }, undefined, undefined, undefined);
expect(hookRunner.runBeforeToolCall).toHaveBeenCalledTimes(1);
});
});
describe("before_tool_call hook integration for client tools", () => {