From a4aa72e19829953f1fc3b8360f70c86eaf40ab19 Mon Sep 17 00:00:00 2001 From: Michael Panchenko Date: Thu, 25 Jun 2026 13:43:33 +0200 Subject: [PATCH] Hooks: handle tool_input passed as string gracefully instead of failing Copilot CLI sends strings --- CHANGELOG.md | 3 +++ src/serena/hooks.py | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7087b497..a9c71a00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,9 @@ Status of the `main` branch. Changes prior to the next official version change w - Tray manager: Add NixOS-support for AppIndicator-based trays (e.g., most Wayland-trays) to the package in flake.nix. - Fix: Wait for the subprocess that opens the browser window, preventing zombie processes #1488 +* Hooks: + - Handle tool_input passed as string gracefully instead of failing (Copilot CLI sends strings). + Dependencies: - Add dependency `oslex` diff --git a/src/serena/hooks.py b/src/serena/hooks.py index ef5e2106..83a0a8cc 100644 --- a/src/serena/hooks.py +++ b/src/serena/hooks.py @@ -70,7 +70,11 @@ class PreToolUseHook(Hook, ABC): if not _tool_name: raise ValueError("Tool name is required in the hook input data") self._tool_name = _tool_name - self._tool_input: dict | None = self._input_data.get("tool_input") or self._input_data.get("toolInput") + raw_tool_input = self._input_data.get("tool_input") or self._input_data.get("toolInput") + # TODO: some agents, like copilot CLI, can send a string as value for raw_tool_input + # Example: "tool_input":"*** Begin Patch\n*** Add File: /Users/acbdef/.copilot/session-state/08a961db-02f0-4c7c-b783-1e9818290292/files/hook-tool-test-3.txt\n+third edit tool test\n*** End Patch\n" + # We currently don't parse such tool input and hence don't react to it in hooks + self._tool_input: dict | None = raw_tool_input if isinstance(raw_tool_input, dict) else None # only relevant in claude code at the moment, (not all events include this field; default to empty string) raw_permission_mode = self._input_data.get("permission_mode") or self._input_data.get("permissionMode") or ""