49c38de3b2
Tools: - Add a single ``dump_tool_result`` helper in ``tools/_decorator.py`` and remove the eight identical ``_dump`` definitions from ``proxy/tools.py``, ``file_edit/tools.py``, ``python/tool.py``, ``terminal/tool.py``, ``todo/tools.py``, ``browser/tool.py``, ``notes/tools.py``, ``agents_graph/tools.py``. Imports trimmed. Net -50 LoC across the tool modules. run_config_factory: - Inline the four retry-policy plumbing pieces (``_RETRYABLE_HTTP_STATUSES``, ``_DEFAULT_MAX_RETRIES``, ``_DEFAULT_BACKOFF``, ``_default_retry_policy()``) into a single module-level ``_DEFAULT_RETRY`` ``ModelRetrySettings`` literal. The inputs were never overridden and the helper had one caller. Tests: - Drop migration scars from ``tests/test_run_config_factory.py`` (``Phase 1`` / ``C1`` / ``C11`` / ``C21`` / ``HARNESS_WIKI`` / ``AUDIT`` references). Replace the ``_RETRYABLE_HTTP_STATUSES``-touching test with a ``retry.policy is not None`` smoke check now that the constant has been inlined. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
129 lines
4.2 KiB
Python
129 lines
4.2 KiB
Python
"""SDK function-tool wrappers for the legacy ``file_edit`` tools.
|
|
|
|
These three tools (``str_replace_editor``, ``list_files``, ``search_files``)
|
|
operate on files inside the sandbox container's ``/workspace`` filesystem.
|
|
The legacy harness marks them ``sandbox_execution=True`` (default) so the
|
|
executor POSTs them to the in-container tool server.
|
|
|
|
The host-side SDK wrappers therefore delegate to ``post_to_sandbox`` —
|
|
the legacy implementations live in the container image and we don't
|
|
import them on the host (they pull in ``openhands_aci``, which is a
|
|
sandbox-only dependency).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from agents import RunContextWrapper
|
|
|
|
from strix.tools._decorator import dump_tool_result, strix_tool
|
|
from strix.tools._sandbox_dispatch import post_to_sandbox
|
|
|
|
|
|
@strix_tool(timeout=180)
|
|
async def str_replace_editor(
|
|
ctx: RunContextWrapper,
|
|
command: str,
|
|
path: str,
|
|
file_text: str | None = None,
|
|
view_range: list[int] | None = None,
|
|
old_str: str | None = None,
|
|
new_str: str | None = None,
|
|
insert_line: int | None = None,
|
|
) -> str:
|
|
"""View, create, or edit a file in the sandbox filesystem.
|
|
|
|
Commands:
|
|
|
|
- ``view`` — show file contents. Optionally restrict to a line range
|
|
via ``view_range`` (1-indexed; ``[start, -1]`` for "from start to
|
|
end of file").
|
|
- ``create`` — write a new file with ``file_text``. Use this for
|
|
exploit scripts, PoCs, helper modules, etc.
|
|
- ``str_replace`` — find ``old_str`` in the file and replace with
|
|
``new_str``. ``old_str`` must be unique in the file; include
|
|
enough surrounding context to make it so.
|
|
- ``insert`` — insert ``new_str`` after line ``insert_line``.
|
|
- ``undo_edit`` — revert the most recent edit to ``path``.
|
|
|
|
Multi-line ``new_str`` / ``old_str`` / ``file_text`` use real
|
|
newlines, not literal ``\\n``.
|
|
|
|
Args:
|
|
command: ``view`` / ``create`` / ``str_replace`` / ``insert`` /
|
|
``undo_edit``.
|
|
path: File path. Relative paths anchor at ``/workspace``.
|
|
file_text: Required for ``create``.
|
|
view_range: Optional ``[start, end]`` (1-indexed) for ``view``.
|
|
old_str: Required for ``str_replace`` — must be unique in file.
|
|
new_str: Required for ``str_replace`` and ``insert``.
|
|
insert_line: Required for ``insert``; new content goes AFTER
|
|
this line.
|
|
"""
|
|
return dump_tool_result(
|
|
await post_to_sandbox(
|
|
ctx,
|
|
"str_replace_editor",
|
|
{
|
|
"command": command,
|
|
"path": path,
|
|
"file_text": file_text,
|
|
"view_range": view_range,
|
|
"old_str": old_str,
|
|
"new_str": new_str,
|
|
"insert_line": insert_line,
|
|
},
|
|
),
|
|
)
|
|
|
|
|
|
@strix_tool(timeout=120)
|
|
async def list_files(
|
|
ctx: RunContextWrapper,
|
|
path: str,
|
|
recursive: bool = False,
|
|
) -> str:
|
|
"""List files and directories under a sandbox path.
|
|
|
|
Output is sorted alphabetically and capped at 500 entries to avoid
|
|
flooding the model with huge directory trees.
|
|
|
|
Args:
|
|
path: Directory path; relative paths anchor at ``/workspace``.
|
|
recursive: When True, walks subdirectories.
|
|
"""
|
|
return dump_tool_result(
|
|
await post_to_sandbox(
|
|
ctx,
|
|
"list_files",
|
|
{"path": path, "recursive": recursive},
|
|
),
|
|
)
|
|
|
|
|
|
@strix_tool(timeout=120)
|
|
async def search_files(
|
|
ctx: RunContextWrapper,
|
|
path: str,
|
|
regex: str,
|
|
file_pattern: str = "*",
|
|
) -> str:
|
|
"""Recursively regex-search files in the sandbox using ripgrep.
|
|
|
|
Fast — uses ``rg`` under the hood. Walks subdirectories. Use this
|
|
for code-pattern hunts (``def\\s+authenticate``, ``API_KEY``,
|
|
secrets, etc.) when you don't already know the file.
|
|
|
|
Args:
|
|
path: Root path to search. Relative paths anchor at ``/workspace``.
|
|
regex: Pattern to match (PCRE-style; passed straight to ``rg``).
|
|
file_pattern: Glob filter (e.g. ``"*.py"``, ``"*.{js,ts}"``).
|
|
Defaults to all files.
|
|
"""
|
|
return dump_tool_result(
|
|
await post_to_sandbox(
|
|
ctx,
|
|
"search_files",
|
|
{"path": path, "regex": regex, "file_pattern": file_pattern},
|
|
),
|
|
)
|