core/tools: add get_truncated tool for chained resume

This commit is contained in:
2026-08-11 14:59:39 +08:00
parent c96f669bcb
commit 00db7cdc7f
7 changed files with 118 additions and 5 deletions
@@ -21,6 +21,7 @@ Tool playbook still applies (see system). Hard constraints:
use `run_argv_batch` for ordered pipelines.
- `edit_replace`: fix match / `max_replaces`; `edit_lineno` edits only lines read via \
`read_file(with_lineno=true)`, and line numbers reset after any file write.
- Truncated results carry a `TRUNCATE_TOKEN`; continue with `get_truncated`.
- Prefer `fetch` for HTTP(S); private/LAN hosts need human policy allow (not YOLO).
- PTY secrets only via `ask_into_pty`; denylists and confirms still apply.
- `wait` pauses; pressing Enter disturbs early (optional reason).
+2
View File
@@ -32,6 +32,8 @@ raise `max_replaces` or narrow `old_string`.
- `edit_lineno` edits only lines you read via `read_file(path, with_lineno=true)`; \
re-read with line numbers after any `edit_*` / `write_file` / copy/move/delete, \
since line numbers go stale.
- Truncated `read_file` / `fetch` results carry a `TRUNCATE_TOKEN`; use \
`get_truncated` with it to keep reading (chains through truncations).
### Commands
- Prefer `run_argv` / `run_argv_batch` (argv lists, no shell) over `bash -c` or similar.
+2
View File
@@ -5,6 +5,7 @@ from .edit_replace import edit_replace as edit_replace
from .fs_ops import copy_path as copy_path
from .fs_ops import delete_path as delete_path
from .fs_ops import move_path as move_path
from .get_truncated import get_truncated as get_truncated
from .glob_paths import glob_paths as glob_paths
from .grep_files import grep_files as grep_files
from .listdir import listdir as listdir
@@ -25,4 +26,5 @@ FILE_TOOLS = [
move_path,
delete_path,
new_temporary_workspace,
get_truncated,
]
+40
View File
@@ -0,0 +1,40 @@
"""``get_truncated`` tool: resume truncated tool results via a truncate token."""
from __future__ import annotations
from plyngent.agent import ToolTag, tool
from plyngent.agent.budget import DEFAULT_TOOL_RESULT_MAX_CHARS
from plyngent.tools.file.read import read_file
from plyngent.tools.net.fetch import fetch
from plyngent.tools.truncate_token import decode_truncate_token, truncate_with_token
@tool(tags=ToolTag.LOCAL | ToolTag.INSTANCE_STATE)
async def get_truncated(token: str, *, max_chars: int = DEFAULT_TOOL_RESULT_MAX_CHARS) -> str:
"""Fetch the next chunk of a truncated result using its ``TRUNCATE_TOKEN``.
Pass the token shown at the end of a truncated ``read_file``, ``fetch``, or
earlier ``get_truncated`` result to keep reading without re-requesting the
whole source or raising limits. Truncated output carries a fresh token, so
chunks chain indefinitely.
"""
parsed = decode_truncate_token(token)
if parsed is None:
return "error: invalid truncate token"
if parsed.kind == "http":
return await fetch.handler(parsed.location, offset=parsed.offset, max_chars=parsed.limit)
text = await read_file.handler(parsed.location)
if text.startswith("error:"):
return text
start = parsed.offset
segment = text[start : start + max_chars]
chunk, _ = truncate_with_token(
segment,
max_chars,
kind="file",
location=parsed.location,
offset=start,
limit=max_chars,
total_len=len(text),
)
return chunk