feat(agent): truncated context files name the sections that were dropped

When a context file still overflows (small windows, other repositories), the marker
listed only char counts, so the agent could not tell whether what it lost mattered.
It now lists the markdown headings in the omitted middle (fenced # comments ignored).
This commit is contained in:
Teknium
2026-10-04 21:59:35 -07:00
parent e1ec584b57
commit 35f8786971
2 changed files with 34 additions and 2 deletions

View File

@@ -1576,14 +1576,32 @@ def _truncate_content(
warnings.append(msg)
head_chars = int(max_chars * CONTEXT_TRUNCATE_HEAD_RATIO)
tail_chars = int(max_chars * CONTEXT_TRUNCATE_TAIL_RATIO)
omitted = _omitted_headings(content, head_chars, len(content) - tail_chars)
sections = f" Omitted sections: {'; '.join(omitted)}." if omitted else ""
marker = (
f"\n\n[...truncated {filename}: kept {head_chars}+{tail_chars} of {len(content)} chars. The middle is "
f"omitted — if you need the full instructions, read the complete file with the read_file tool: "
f"{read_path or filename}]\n\n"
f"omitted.{sections} If you need the full instructions, read the complete file with the read_file "
f"tool: {read_path or filename}]\n\n"
)
return content[:head_chars] + marker + content[-tail_chars:]
def _omitted_headings(content: str, start: int, end: int, limit: int = 15) -> list:
"""Markdown headings whose line starts inside ``content[start:end]``, so a truncation marker tells
the agent what it lost; ``#`` lines inside fenced code blocks are comments, not headings."""
import re
headings, offset, fenced = [], 0, False
for line in content.splitlines(keepends=True):
stripped = line.strip()
if stripped.startswith(("```", "~~~")):
fenced = not fenced
elif not fenced and start <= offset < end and re.match(r"#{1,6} \S", stripped):
headings.append(stripped.lstrip("#").strip())
offset += len(line)
return headings[:limit] + (["..."] if len(headings) > limit else [])
def load_soul_md(context_length: Optional[int] = None, home_override: "Path | None" = None) -> Optional[str]:
"""SOUL.md from HERMES_HOME (identity slot #1), or None.

View File

@@ -99,3 +99,17 @@ def test_truncated_and_suppressed_statuses_follow_the_builder(project, monkeypat
assert "evil identity text" in prompt and "[BLOCKED: SOUL.md" not in prompt and "[BLOCKED: AGENTS.md" in prompt
assert any("SOUL.md" in line and "review the file" in line
for line in render_context_file_lines(list(entries.values())))
def test_truncation_marker_names_the_omitted_sections_only():
from agent.prompt_builder import _truncate_content
pad = "word " * 400
content = (
f"# Kept head\n{pad}\n## Lost section\n{pad}\n```bash\n# a shell comment\n```\n"
f"### Lost subsection\n{pad}\n{pad}\n## Kept tail\nshort\n"
)
out = _truncate_content(content, "AGENTS.md", max_chars=2500, queue_warning=False)
marker = out[out.index("[...truncated"):out.index("]", out.index("[...truncated"))]
assert "Lost section; Lost subsection" in marker
assert "Kept head" not in marker and "Kept tail" not in marker and "shell comment" not in marker