| 45 | |
| 46 | |
| 47 | async def run_tool_use( |
| 48 | tool_use: Any, |
| 49 | assistant_message: AssistantMessage, |
| 50 | can_use_tool: Any, |
| 51 | tool_use_context: ToolContext, |
| 52 | ) -> AsyncGenerator[MessageUpdateLazy, None]: |
| 53 | from src.tool_system.build_tool import find_tool_by_name |
| 54 | |
| 55 | tool_name = tool_use.name |
| 56 | tool = find_tool_by_name(tool_use_context.options.tools, tool_name) |
| 57 | |
| 58 | if tool is None: |
| 59 | # Old-transcript names and pool-hidden tools resolve through the |
| 60 | # FULL base-tool list (TS toolExecution.ts:335-341: |
| 61 | # findToolByName(getAllBaseTools(), toolName)). Not a permission |
| 62 | # bypass: resolution still runs downstream for the resolved tool. |
| 63 | try: |
| 64 | from src.tool_system.defaults import build_default_registry |
| 65 | |
| 66 | tool = find_tool_by_name( |
| 67 | build_default_registry().list_tools(), tool_name |
| 68 | ) |
| 69 | except Exception: |
| 70 | pass |
| 71 | |
| 72 | if tool is None: |
| 73 | logger.debug("Unknown tool %s: %s", tool_name, tool_use.id) |
| 74 | yield MessageUpdateLazy( |
| 75 | message=create_user_message( |
| 76 | content=[{ |
| 77 | "type": "tool_result", |
| 78 | "content": f"<tool_use_error>Error: No such tool available: {tool_name}</tool_use_error>", |
| 79 | "is_error": True, |
| 80 | "tool_use_id": tool_use.id, |
| 81 | }], |
| 82 | toolUseResult=f"Error: No such tool available: {tool_name}", |
| 83 | ), |
| 84 | ) |
| 85 | return |
| 86 | |
| 87 | tool_input = tool_use.input if isinstance(tool_use.input, dict) else {} |
| 88 | |
| 89 | try: |
| 90 | # ``abort_controller`` is non-optional on ``ToolContext`` — the |
| 91 | # historical ``if abort_ctrl and …`` guard masked the field-is-None |
| 92 | # hazard class that broke ESC propagation into subagents. |
| 93 | if tool_use_context.abort_controller.signal.aborted: |
| 94 | if _is_user_cancelled_abort(tool_use_context): |
| 95 | # ESC tripped before dispatch: REJECT_MESSAGE so the |
| 96 | # model sees an unambiguous "user rejected" signal (TS |
| 97 | # StreamingToolExecutor.ts:153-205 user_interrupted). |
| 98 | yield MessageUpdateLazy( |
| 99 | message=_build_user_cancelled_message(tool_use.id), |
| 100 | ) |
| 101 | return |
| 102 | content = _create_tool_result_stop(tool_use.id) |
| 103 | yield MessageUpdateLazy( |
| 104 | message=create_user_message( |