Execute a VM tool (page_fault or search_pages) via MemoryManager. VM tools are internal memory operations that bypass the MCP ToolManager and all guard checks (dataflow tracking, $vN references, per-tool limits). Includes: - UI display so tool calls are visible in t
(
self,
tool_name: str,
arguments: dict,
llm_tool_name: str,
call_id: str,
)
| 841 | self._add_tool_result_to_history(llm_tool_name, call_id, result_text) |
| 842 | |
| 843 | async def _handle_vm_tool( |
| 844 | self, |
| 845 | tool_name: str, |
| 846 | arguments: dict, |
| 847 | llm_tool_name: str, |
| 848 | call_id: str, |
| 849 | ) -> None: |
| 850 | """Execute a VM tool (page_fault or search_pages) via MemoryManager. |
| 851 | |
| 852 | VM tools are internal memory operations that bypass the MCP ToolManager |
| 853 | and all guard checks (dataflow tracking, $vN references, per-tool limits). |
| 854 | |
| 855 | Includes: |
| 856 | - UI display so tool calls are visible in the chat output |
| 857 | - Loop prevention: refuses to re-fault the same page_id twice |
| 858 | - Content truncation for oversized pages |
| 859 | """ |
| 860 | vm = getattr(getattr(self.context, "session", None), "vm", None) |
| 861 | if not vm: |
| 862 | self._add_tool_result_to_history( |
| 863 | llm_tool_name, call_id, "Error: VM not available." |
| 864 | ) |
| 865 | return |
| 866 | |
| 867 | logger.info(f"VM tool {tool_name} called with args: {arguments}") |
| 868 | |
| 869 | # Show tool call in UI (so page_fault calls are visible) |
| 870 | try: |
| 871 | self.ui_manager.print_tool_call(tool_name, arguments) |
| 872 | await self.ui_manager.start_tool_execution(tool_name, arguments) |
| 873 | except Exception as e: |
| 874 | logger.debug("UI error displaying VM tool call: %s", e) |
| 875 | |
| 876 | content: str | list[dict[str, Any]] = "" |
| 877 | success = True |
| 878 | try: |
| 879 | if tool_name == "page_fault": |
| 880 | page_id = arguments.get("page_id", "") |
| 881 | |
| 882 | # Loop prevention: don't re-fault the same page |
| 883 | if page_id in self._faulted_page_ids: |
| 884 | content = json.dumps( |
| 885 | { |
| 886 | "success": True, |
| 887 | "already_loaded": True, |
| 888 | "page_id": page_id, |
| 889 | "message": ( |
| 890 | "This page was already loaded earlier in the " |
| 891 | "conversation. The content is in a previous " |
| 892 | "tool result message — use that directly." |
| 893 | ), |
| 894 | } |
| 895 | ) |
| 896 | else: |
| 897 | result = await vm.handle_fault( |
| 898 | page_id=page_id, |
| 899 | target_level=arguments.get("target_level", 2), |
| 900 | ) |