mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
tools: normalize file tool pagination bounds
This commit is contained in:
parent
3e652f75b2
commit
40619b393f
5 changed files with 145 additions and 3 deletions
|
|
@ -271,6 +271,40 @@ LINTERS = {
|
|||
MAX_LINES = 2000
|
||||
MAX_LINE_LENGTH = 2000
|
||||
MAX_FILE_SIZE = 50 * 1024 # 50KB
|
||||
DEFAULT_READ_OFFSET = 1
|
||||
DEFAULT_READ_LIMIT = 500
|
||||
DEFAULT_SEARCH_OFFSET = 0
|
||||
DEFAULT_SEARCH_LIMIT = 50
|
||||
|
||||
|
||||
def _coerce_int(value: Any, default: int) -> int:
|
||||
"""Best-effort integer coercion for tool pagination inputs."""
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
|
||||
def normalize_read_pagination(offset: Any = DEFAULT_READ_OFFSET,
|
||||
limit: Any = DEFAULT_READ_LIMIT) -> tuple[int, int]:
|
||||
"""Return safe read_file pagination bounds.
|
||||
|
||||
Tool schemas declare minimum/maximum values, but not every caller or
|
||||
provider enforces schemas before dispatch. Clamp here so invalid values
|
||||
cannot leak into sed ranges like ``0,-1p``.
|
||||
"""
|
||||
normalized_offset = max(1, _coerce_int(offset, DEFAULT_READ_OFFSET))
|
||||
normalized_limit = _coerce_int(limit, DEFAULT_READ_LIMIT)
|
||||
normalized_limit = max(1, min(normalized_limit, MAX_LINES))
|
||||
return normalized_offset, normalized_limit
|
||||
|
||||
|
||||
def normalize_search_pagination(offset: Any = DEFAULT_SEARCH_OFFSET,
|
||||
limit: Any = DEFAULT_SEARCH_LIMIT) -> tuple[int, int]:
|
||||
"""Return safe search pagination bounds for shell head/tail pipelines."""
|
||||
normalized_offset = max(0, _coerce_int(offset, DEFAULT_SEARCH_OFFSET))
|
||||
normalized_limit = max(1, _coerce_int(limit, DEFAULT_SEARCH_LIMIT))
|
||||
return normalized_offset, normalized_limit
|
||||
|
||||
|
||||
class ShellFileOperations(FileOperations):
|
||||
|
|
@ -461,8 +495,7 @@ class ShellFileOperations(FileOperations):
|
|||
# Expand ~ and other shell paths
|
||||
path = self._expand_path(path)
|
||||
|
||||
# Clamp limit
|
||||
limit = min(limit, MAX_LINES)
|
||||
offset, limit = normalize_read_pagination(offset, limit)
|
||||
|
||||
# Check if file exists and get size (wc -c is POSIX, works on Linux + macOS)
|
||||
stat_cmd = f"wc -c < {self._escape_shell_arg(path)} 2>/dev/null"
|
||||
|
|
@ -866,6 +899,8 @@ class ShellFileOperations(FileOperations):
|
|||
Returns:
|
||||
SearchResult with matches or file list
|
||||
"""
|
||||
offset, limit = normalize_search_pagination(offset, limit)
|
||||
|
||||
# Expand ~ and other shell paths
|
||||
path = self._expand_path(path)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,11 @@ from typing import Optional
|
|||
|
||||
from agent.file_safety import get_read_block_error
|
||||
from tools.binary_extensions import has_binary_extension
|
||||
from tools.file_operations import ShellFileOperations
|
||||
from tools.file_operations import (
|
||||
ShellFileOperations,
|
||||
normalize_read_pagination,
|
||||
normalize_search_pagination,
|
||||
)
|
||||
from tools import file_state
|
||||
from agent.redact import redact_sensitive_text
|
||||
|
||||
|
|
@ -351,6 +355,8 @@ def clear_file_ops_cache(task_id: str = None):
|
|||
def read_file_tool(path: str, offset: int = 1, limit: int = 500, task_id: str = "default") -> str:
|
||||
"""Read a file with pagination and line numbers."""
|
||||
try:
|
||||
offset, limit = normalize_read_pagination(offset, limit)
|
||||
|
||||
# ── Device path guard ─────────────────────────────────────────
|
||||
# Block paths that would hang the process (infinite output,
|
||||
# blocking on input). Pure path check — no I/O.
|
||||
|
|
@ -762,6 +768,8 @@ def search_tool(pattern: str, target: str = "content", path: str = ".",
|
|||
task_id: str = "default") -> str:
|
||||
"""Search for content or files."""
|
||||
try:
|
||||
offset, limit = normalize_search_pagination(offset, limit)
|
||||
|
||||
# Track searches to detect *consecutive* repeated search loops.
|
||||
# Include pagination args so users can page through truncated
|
||||
# results without tripping the repeated-search guard.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue