Cap run_command spam and force a usable finalize after tool-round limits.

Budget shell calls like CLI, and when rounds are exhausted inject a no-more-tools system nudge plus any user_facing_hint constraints into the final synthesis pass.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-10 23:26:54 +08:00
parent 28a5f2586c
commit 9d447b1c51
5 changed files with 186 additions and 27 deletions

View file

@ -245,3 +245,49 @@ def test_exec_managed_ne_fail_budget(tmp_path: Path) -> None:
blocked, _ = results["f3"]
assert calls["n"] == 2
assert blocked.get("error_code") == "cli_fail_budget_exceeded"
def test_run_command_call_budget(tmp_path: Path) -> None:
store = SqliteStore(str(tmp_path / "shell.sqlite"))
sess = store.create_session("t")
calls = {"n": 0}
def _handler(_args):
calls["n"] += 1
return {"ok": True, "stdout": "ok", "exit_code": 0}
reg = ToolRegistry(
[
ToolSpec(
name="run_command",
description="shell",
parameters={"type": "object", "properties": {"command": {"type": "string"}}},
handler=_handler,
read_only=False,
)
]
)
ctx = ToolExecutionContext(
store=store,
tools=reg,
session_id=sess.id,
turn_uuid="turn-shell-budget",
lang="en",
)
for i in range(5):
ToolExecutor().execute_tool_uses(
ctx=ctx,
assistant_msg_id=i + 1,
tool_uses=[LLMToolCall(id=f"s{i}", name="run_command", arguments={"command": f"echo {i}"})],
signature_budget=2,
)
assert calls["n"] == 5
_, results = ToolExecutor().execute_tool_uses(
ctx=ctx,
assistant_msg_id=99,
tool_uses=[LLMToolCall(id="sX", name="run_command", arguments={"command": "echo x"})],
signature_budget=2,
)
blocked, _ = results["sX"]
assert calls["n"] == 5
assert blocked.get("error_code") == "shell_call_budget_exceeded"