Handle a hooks invocation from the Copilot CLI. Note: This method is internal and should not be called directly. Args: hook_type: The type of hook being invoked. input_data: The input data for the hook. Returns: The
(self, hook_type: str, input_data: Any)
| 2648 | return {"sections": result} |
| 2649 | |
| 2650 | async def _handle_hooks_invoke(self, hook_type: str, input_data: Any) -> Any: |
| 2651 | """ |
| 2652 | Handle a hooks invocation from the Copilot CLI. |
| 2653 | |
| 2654 | Note: |
| 2655 | This method is internal and should not be called directly. |
| 2656 | |
| 2657 | Args: |
| 2658 | hook_type: The type of hook being invoked. |
| 2659 | input_data: The input data for the hook. |
| 2660 | |
| 2661 | Returns: |
| 2662 | The hook output, or None if no handler is registered. |
| 2663 | """ |
| 2664 | with self._hooks_lock: |
| 2665 | hooks = self._hooks |
| 2666 | |
| 2667 | if not hooks: |
| 2668 | return None |
| 2669 | |
| 2670 | handler_map = { |
| 2671 | "preToolUse": hooks.get("on_pre_tool_use"), |
| 2672 | "preMcpToolCall": hooks.get("on_pre_mcp_tool_call"), |
| 2673 | "postToolUse": hooks.get("on_post_tool_use"), |
| 2674 | "postToolUseFailure": hooks.get("on_post_tool_use_failure"), |
| 2675 | "userPromptSubmitted": hooks.get("on_user_prompt_submitted"), |
| 2676 | "sessionStart": hooks.get("on_session_start"), |
| 2677 | "sessionEnd": hooks.get("on_session_end"), |
| 2678 | "errorOccurred": hooks.get("on_error_occurred"), |
| 2679 | } |
| 2680 | |
| 2681 | handler = handler_map.get(hook_type) |
| 2682 | if not handler: |
| 2683 | return None |
| 2684 | |
| 2685 | try: |
| 2686 | handler_start = time.perf_counter() |
| 2687 | # Normalize input from the wire format: |
| 2688 | # - Remap wire key "cwd" to public API key "workingDirectory". |
| 2689 | # - Convert "timestamp" from epoch milliseconds to ``datetime`` so |
| 2690 | # hook handlers see a timezone-aware ``datetime`` rather than a |
| 2691 | # raw integer (matches TS PR #1357 Phase E). |
| 2692 | transformed: dict[str, Any] = dict(input_data) |
| 2693 | if "cwd" in transformed: |
| 2694 | transformed["workingDirectory"] = transformed.pop("cwd") |
| 2695 | timestamp = transformed.get("timestamp") |
| 2696 | if isinstance(timestamp, (int, float)): |
| 2697 | transformed["timestamp"] = datetime.fromtimestamp(timestamp / 1000, tz=UTC) |
| 2698 | # Each per-hook-type TypedDict is structurally compatible with the |
| 2699 | # normalized dict; cast to ``Any`` so ty doesn't try to narrow the |
| 2700 | # specific TypedDict variant from the runtime ``dict``. |
| 2701 | input_data = cast(Any, transformed) |
| 2702 | result = handler(input_data, {"session_id": self.session_id}) |
| 2703 | if inspect.isawaitable(result): |
| 2704 | result = await result |
| 2705 | log_timing( |
| 2706 | logger, |
| 2707 | logging.DEBUG, |
nothing calls this directly
no test coverage detected