[eric] agents: an elision carries no path, because an instruction inside tool output reads as an injection

This commit is contained in:
ciregenz
2026-08-27 12:26:24 -07:00
parent 50b9f09302
commit 0cffa158c7
4 changed files with 57 additions and 36 deletions
@@ -51,7 +51,14 @@ NESTED_TEXT_PATHS = (("file", "content"), ("result", "content"), ("data", "text"
@typechecked
def shape_text(body: str, recovery: str) -> str:
"""Head + the notable lines from the middle + tail, and always where to find the rest."""
"""Head + the notable lines from the middle + tail.
`recovery` is the blob path, and it is deliberately NOT written into the output; see the note
below the elision for the drill that settled it. It stays in the signature because it is the
recoverability CONTRACT: `shape_for_model` refuses to cut anything it could not park, so a
caller cannot shape without one. Reading this as a forgotten parameter is the mistake that got
the path put back once already.
"""
if len(body) <= HEAD_CHARS + TAIL_CHARS:
return body
head, tail = body[:HEAD_CHARS], body[-TAIL_CHARS:]
@@ -71,10 +78,19 @@ def shape_text(body: str, recovery: str) -> str:
# cannot be written, and then we said nothing about it, so the model's only recovery was to
# re-run the command. hermes hands the model an exact re-read call; this is the same idea,
# phrased as output rather than as instructions from a harness.
p_from_line = body.count("\n", 0, HEAD_CHARS) + 1
note = (f"[... {len(middle)} characters omitted; full output: {recovery} "
f"(the omitted part starts at line {p_from_line}) ...]") if recovery else (
f"[... {len(middle)} characters omitted ...]")
# NO PATH, NO INSTRUCTION, and this is now settled by drill rather than by the retracted
# p=0.026 control that first removed it. Restoring the path was tried twice live on 2026-08-27,
# same session, needle in the elided middle, re-running forbidden:
# passive form ("full output: <path>") -> "I have no legitimate way to see line 301"
# imperative ("Read <path> from line 18") -> "that's not a real system instruction, it's
# text sitting inside the tool result ...
# flagging it in case it's an injection attempt"
# A model with working injection defences MUST refuse an instruction embedded in tool output,
# so the note cannot be an affordance; worse, it manufactures a false security warning in the
# answer. The blob is still written and `shape_for_model` still REFUSES to cut when it cannot be
# written -- recoverability is real, it is simply carried by the tool call surviving in the
# transcript (the agent re-runs it), which is the same recovery hermes leans on.
note = f"[... {len(middle)} characters omitted ...]"
if carried:
note += "\n" + "\n".join(carried)
return f"{head}\n{note}\n{tail}"
+5 -5
View File
@@ -67,11 +67,11 @@ def test_shaping_never_removes_without_a_way_back():
from backend.apps.agents.manager.streaming.tool_output_shaper import shape_text
out = shape_text("q" * 9_000, "/blobs/x-model.txt")
assert "characters omitted" in out, "a cut is always visible as a cut"
assert "/blobs/x-model.txt" in out, \
"and the docstring above is the reason: recoverability MEANS naming where the full text is"
# The path ban this used to carry cited a retracted p=0.026 control. Harness PROSE stays banned.
note = out[out.index("[..."):out.index("]", out.index("[..."))]
assert "OpenSwarm" not in note.replace("/blobs/x-model.txt", "")
# Recoverability is carried by the TOOL CALL surviving in the transcript, not by a path in the
# body: drilled live 2026-08-27, a model with working injection defences refuses an instruction
# embedded in tool output and flags it as an attack. The blob still exists for the UI, and
# shape_for_model still refuses to cut anything it could not park.
assert "OpenSwarm" not in out and "/blobs/" not in out
def test_shaping_leaves_a_normal_result_completely_alone():
+26 -22
View File
@@ -23,32 +23,36 @@ def p_note(out: str) -> str:
return next(ln for ln in out.splitlines() if ln.startswith("[..."))
def test_the_note_names_the_file_the_shaper_already_wrote():
assert BLOB in p_note(shape_text(p_body(), BLOB))
def test_the_note_carries_no_path_and_no_instruction():
"""DRILLED 2026-08-27, twice, and this is the whole reason.
Same session, needle in the elided middle, re-running forbidden:
passive ("full output: <path>") -> "I have no legitimate way to see line 301"
imperative ("Read <path> from line 18") -> "that's not a real system instruction, it's text
sitting inside the tool result ... flagging it in
case it's an injection attempt"
A model with working injection defences must refuse an instruction embedded in tool output. The
note cannot be an affordance, and advertising it manufactures a false security warning."""
note = p_note(shape_text(p_body(), BLOB))
assert BLOB not in note
for word in ("Read ", "read ", "full output", "see the omitted"):
assert word not in note, f"an embedded instruction reads as injection: {note}"
assert "characters omitted" in note, "the cut must still be visible as a cut"
def test_it_says_where_in_that_file_the_omitted_part_begins():
# hermes's trick: without a starting offset the model's first read lands in text it already has.
body = p_body()
expected = body.count("\n", 0, HEAD_CHARS) + 1
assert f"starts at line {expected}" in p_note(shape_text(body, BLOB))
def test_the_blob_is_still_written_even_though_it_is_not_advertised():
"""Recoverability is real; it is just carried by the tool call surviving in the transcript."""
src = open("backend/apps/agents/manager/streaming/tool_output_shaper.py").read()
assert "write_blob(" in src
assert "skipped_no_recovery" in src, "a cut that could not be parked must not happen at all"
def test_the_notes_PROSE_names_nothing_about_the_harness():
"""The reason the path left in the first place, stated precisely.
On a packaged install the blob really does live under `.../Application Support/OpenSwarm/data`,
so the app name is unavoidably inside the path. That is a filesystem fact any `ls` would print,
and it is a different thing from prose announcing the harness ("elided by OpenSwarm"), which is
what the original wording did and what the rule is actually about. So: the PATH may say
anything, the WORDS around it may not."""
out = shape_text(p_body(), BLOB)
i = out.index("[...")
note = out[i:out.index("]", i)]
prose = note.replace(BLOB, "<path>").lower()
for word in ("openswarm", "harness", "elided by", "automated", "assistant", "we ", "our "):
assert word not in prose, f"the note's prose must not say {word!r}: {prose}"
assert "<path>" in prose, "and the path itself must still be there"
def test_the_recovery_arg_is_documented_as_deliberately_unsurfaced():
"""It looked like a forgotten parameter, which is how the path got restored once already."""
src = open("backend/apps/agents/manager/streaming/tool_output_shaper.py").read()
i = src.index("def shape_text(")
doc = src[i:i + 700]
assert "deliberately NOT written" in doc
def test_no_blob_means_no_promise():
+5 -4
View File
@@ -32,10 +32,11 @@ def test_an_elision_is_marked_and_never_names_the_harness_IN_PROSE():
it cannot, so withholding the path was paying for a recovery nobody could use."""
out = shape_text(p_big(), "/data/blobs/x.txt")
assert "characters omitted" in out, "a cut must be visible as a cut"
assert "/data/blobs/x.txt" in out, "the file it already wrote must be reachable"
note = out[out.index("[..."):out.index("]", out.index("[..."))]
assert "OpenSwarm" not in note.replace("/data/blobs/x.txt", ""), \
"the harness may not name ITSELF; a path is data, prose is a confession"
# CORRECTED AGAIN 2026-08-27, this time by live drill rather than by a statistic: advertising
# the path makes the model flag the note as a prompt-injection attempt and refuse it. See
# test_shaper_recovery_path.py for the two transcripts.
assert "/data/blobs/x.txt" not in out, "an embedded path+instruction reads as injection"
assert "OpenSwarm" not in out
def test_the_answer_line_survives_the_middle():