agent-exec transport 35d469bccb3546fa95ccf1db49e5a7c2/79ece004c6be

This commit is contained in:
agent-exec-transport
2026-10-15 20:18:51 +00:00
parent 6b00f4f8b0
commit 253bce37aa
2 changed files with 52 additions and 12 deletions
+27 -10
View File
@@ -429,22 +429,39 @@ def _build_bounded_unit_prompt(packet_text: str) -> str:
def extract_terminal_record(text: str | None) -> dict[str, Any] | None:
"""Return the last valid terminal record embedded in one final message."""
"""Return the sole terminal record that ends one final message.
Only whitespace may follow the record, and no other valid record may
precede it; anything else fails closed.
"""
if not isinstance(text, str) or not text:
return None
text = text[-_MAX_TERMINAL_TEXT_CHARS:]
text = text[-_MAX_TERMINAL_TEXT_CHARS:].rstrip()
decoder = json.JSONDecoder()
def decode_at(pos: int) -> tuple[object, int]:
try:
return decoder.raw_decode(text, pos)
except json.JSONDecodeError:
return None, -1
pos = text.rfind("{")
while pos != -1:
try:
candidate, _ = decoder.raw_decode(text, pos)
except json.JSONDecodeError:
candidate = None
record = _valid_terminal_record(candidate)
if record is not None:
return record
candidate, end = decode_at(pos)
if end == len(text) and isinstance(candidate, dict):
break
pos = text.rfind("{", 0, pos)
return None
else:
return None
record = _valid_terminal_record(candidate)
if record is None:
return None
earlier = text.rfind("{", 0, pos)
while earlier != -1:
if _valid_terminal_record(decode_at(earlier)[0]) is not None:
return None
earlier = text.rfind("{", 0, earlier)
return record
def _valid_terminal_record(obj: object) -> dict[str, Any] | None:
+25 -2
View File
@@ -2325,7 +2325,7 @@ class AgentExecControllerTests(unittest.TestCase):
"pi",
_pi_help(),
_pi_json_events(
"ok\n```json\n" + _terminal_json() + "\n```",
"ok\n" + _terminal_json() + "\n",
agent_end_padding=2 * agent_exec.TERMINAL_LINE_MAX_BYTES,
),
"success",
@@ -2411,12 +2411,35 @@ class TerminalRecordDecodingTests(unittest.TestCase):
)
def test_cursor_stream_json_result_is_decoded(self) -> None:
lines = _cursor_stream_events("Done.\n" + _terminal_json() + "\nThanks.")
lines = _cursor_stream_events("Done.\n" + _terminal_json() + "\n \n")
record = self._decode("cursor", lines)
assert record is not None
self.assertEqual(record["terminal_status"], "completed")
self.assertEqual(record["changed_files"], ["src/marker.txt"])
def test_terminal_record_must_be_sole_final_content(self) -> None:
invalid = json.dumps({"terminal_status": "done", "summary": "x"})
texts = {
"trailing-prose": "Done.\n" + _terminal_json() + "\nThanks.",
"trailing-invalid-object": _terminal_json() + "\n" + invalid,
"trailing-non-terminal-object": _terminal_json() + ' {"note":"x"}',
"truncated-terminal-object": _terminal_json() + '\n{"terminal_status":',
"two-records": _terminal_json() + "\n" + _terminal_json(),
"conflicting-records": _terminal_json() + "\n" + _terminal_json("blocked"),
}
for name, text in texts.items():
for adapter, lines in (
("cursor", _cursor_stream_events(text)),
("antigravity", _agy_stream_events(text)),
("pi", _pi_json_events(text)),
):
with self.subTest(case=name, adapter=adapter):
self.assertIsNone(self._decode(adapter, lines))
with self.subTest(case=name, adapter="custom"):
self.assertIsNone(
self._decode("custom", [], stdout_tail="", result_text=text)
)
def test_cursor_user_echo_and_error_result_are_not_records(self) -> None:
lines = _cursor_stream_events("prose only")
echo = json.loads(lines[1])