Skip to content

Commit c85816b

Browse files
committed
Blocked bash and raised unit test turns
1 parent 65542b4 commit c85816b

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

render_machine/agent/tool_executor.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111

1212
ToolFunction = Callable[[dict, RenderContext], str]
1313

14+
# Mirrors the server's TEMPORARILY_DISABLED_TOOLS (agent/agent_llm.py in codeplain-api).
15+
# The model can emit calls to tools that were never declared to it (Gemini does not
16+
# hard-constrain function-call names), and servers without the corresponding fix forward
17+
# such calls verbatim — so the block must also be enforced here, at the point of
18+
# execution. Empty this set to re-enable.
19+
TEMPORARILY_DISABLED_TOOLS = {"run_command"}
20+
1421
DEFAULT_TOOLS: dict[str, ToolFunction] = {
1522
"run_unit_tests": tools.run_unit_tests,
1623
"run_command": tools.run_command,
@@ -44,6 +51,14 @@ def _execute_single(self, call: dict, render_context: RenderContext) -> str:
4451
name = call["name"]
4552
args = call.get("args", {})
4653

54+
if name in TEMPORARILY_DISABLED_TOOLS:
55+
trace("tool", name=name, error="temporarily disabled")
56+
return (
57+
f"Error: the tool '{name}' is temporarily disabled and was NOT executed. "
58+
f"Do not call it again — further calls will also be refused. Use run_unit_tests "
59+
f"to run the unit test suite, and read_file / grep / ls_files to investigate instead."
60+
)
61+
4762
tool_fn = self._tools.get(name)
4863
if tool_fn is None:
4964
trace("tool", name=name, error="unknown tool")

tests/test_agent_tools.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,3 +612,34 @@ def test_edit_file_fuzzy_tolerates_indentation_differences(project_dir):
612612

613613
assert "Successfully edited" in result
614614
assert "return 2" in Path(file_path).read_text(encoding="utf-8")
615+
616+
617+
def test_tool_executor_refuses_temporarily_disabled_tool(monkeypatch):
618+
"""A call to a disabled tool must be refused without executing it, even though the
619+
tool is present in DEFAULT_TOOLS (the model can emit calls the server never
620+
declared to it, and unpatched servers forward them verbatim)."""
621+
import render_machine.agent.tool_executor as tool_executor_module
622+
from render_machine.agent.tool_executor import ToolExecutor
623+
624+
monkeypatch.setattr(tool_executor_module, "TEMPORARILY_DISABLED_TOOLS", {"run_command"})
625+
626+
executed = []
627+
executor = ToolExecutor({"run_command": lambda args, ctx: executed.append(args) or "ran"})
628+
results = executor.execute_calls([{"id": "c1", "name": "run_command", "args": {"command": "ls"}}], None)
629+
630+
assert executed == []
631+
assert results[0]["call_id"] == "c1"
632+
assert "temporarily disabled" in results[0]["output"]
633+
assert "run_command" in results[0]["output"]
634+
635+
636+
def test_tool_executor_runs_tools_not_in_disabled_set(monkeypatch):
637+
import render_machine.agent.tool_executor as tool_executor_module
638+
from render_machine.agent.tool_executor import ToolExecutor
639+
640+
monkeypatch.setattr(tool_executor_module, "TEMPORARILY_DISABLED_TOOLS", set())
641+
642+
executor = ToolExecutor({"run_command": lambda args, ctx: "ran: " + args["command"]})
643+
results = executor.execute_calls([{"id": "c1", "name": "run_command", "args": {"command": "ls"}}], None)
644+
645+
assert results[0]["output"] == "ran: ls"

0 commit comments

Comments
 (0)