mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[eric] agents: MCP responses over the CLI's silent output ceiling get shrunk instead of dropped; a 340KB result line was written and never resolved, the lost-delegation class (ENG-327 root)
This commit is contained in:
@@ -198,7 +198,10 @@ def call_backend(tasks: list[dict]) -> dict:
|
||||
return {"error": str(e)}
|
||||
|
||||
|
||||
MAX_IMAGE_B64_BYTES = 400_000
|
||||
# 400KB was over the CLI's silent MCP-result ceiling: a 340,096-byte response line was WRITTEN and
|
||||
# never resolved (diag-proven 2026-08-19), which is the whole "delegation result died on the stdio
|
||||
# hop" class. 90KB keeps the line safely under the CLI's ~25K-token output cap with headroom.
|
||||
MAX_IMAGE_B64_BYTES = 90_000
|
||||
MAX_SUMMARY_CHARS = 16_000
|
||||
MAX_ACTION_LOG_ENTRIES = 40
|
||||
REPORT_DIR = os.environ.get(
|
||||
@@ -263,7 +266,12 @@ def compress_screenshot(b64_png: str) -> tuple[str, str] | None:
|
||||
img = img.resize((max_width, int(img.height * ratio)), Image.LANCZOS)
|
||||
buf = BytesIO()
|
||||
img.convert("RGB").save(buf, format="JPEG", quality=45)
|
||||
return base64.b64encode(buf.getvalue()).decode(), "image/jpeg"
|
||||
out = base64.b64encode(buf.getvalue()).decode()
|
||||
if len(out) > MAX_IMAGE_B64_BYTES:
|
||||
buf2 = BytesIO()
|
||||
img.resize((640, int(img.height * 640 / img.width)), Image.LANCZOS).convert("RGB").save(buf2, format="JPEG", quality=35)
|
||||
out = base64.b64encode(buf2.getvalue()).decode()
|
||||
return out, "image/jpeg"
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
@@ -74,11 +74,35 @@ def p_call(mod, tool_name: str, arguments: dict) -> dict:
|
||||
P_STDOUT_LOCK = threading.Lock()
|
||||
|
||||
|
||||
P_MAX_RESPONSE_BYTES = 200_000
|
||||
|
||||
def p_shrink_oversize(result):
|
||||
"""The CLI silently DROPS an MCP response line past its output ceiling; the call then hangs
|
||||
until a watchdog shoots this healthy process (diag-proven 2026-08-19: 340KB written, never
|
||||
resolved, 150s kill). Better an answer without its picture than no answer: strip image blocks
|
||||
first, then hard-elide the text."""
|
||||
content = result.get("content") if isinstance(result, dict) else None
|
||||
if not isinstance(content, list):
|
||||
return result
|
||||
kept = [c for c in content if not (isinstance(c, dict) and c.get("type") == "image")]
|
||||
if len(kept) < len(content):
|
||||
kept.append({"type": "text", "text": "[screenshot omitted: full response exceeded the transport limit]"})
|
||||
out = dict(result)
|
||||
out["content"] = kept
|
||||
if len(json.dumps({"result": out})) > P_MAX_RESPONSE_BYTES:
|
||||
for c in out["content"]:
|
||||
if isinstance(c, dict) and isinstance(c.get("text"), str) and len(c["text"]) > 100_000:
|
||||
c["text"] = c["text"][:80_000] + "\n\n[... elided: response exceeded the transport limit ...]"
|
||||
return out
|
||||
|
||||
|
||||
def send_response(id_, result=None, error=None):
|
||||
msg = {"jsonrpc": "2.0", "id": id_}
|
||||
if error is not None:
|
||||
msg["error"] = error
|
||||
else:
|
||||
if result is not None and len(json.dumps({"jsonrpc": "2.0", "id": id_, "result": result})) > P_MAX_RESPONSE_BYTES:
|
||||
result = p_shrink_oversize(result)
|
||||
msg["result"] = result
|
||||
with P_STDOUT_LOCK:
|
||||
sys.stdout.write(json.dumps(msg) + "\n")
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Pins the transport ceiling contract (found 2026-08-19): a tools/call response line past the
|
||||
CLI's silent drop threshold must be shrunk (image stripped, text elided) rather than written,
|
||||
because an oversized line is WRITTEN successfully and then never resolves, hanging the call
|
||||
until a watchdog shoots the healthy sidecar."""
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"combined_meta_mcp_server",
|
||||
os.path.join(os.path.dirname(__file__), "..", "apps", "agents", "combined_meta_mcp_server.py"),
|
||||
)
|
||||
|
||||
|
||||
def p_load():
|
||||
os.environ.setdefault("OSW_MCP_MODULES", "")
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
|
||||
def test_small_response_untouched():
|
||||
mod = p_load()
|
||||
r = {"content": [{"type": "text", "text": "hello"}]}
|
||||
assert mod.p_shrink_oversize(r) == r
|
||||
|
||||
|
||||
def test_oversize_image_block_stripped_with_note():
|
||||
mod = p_load()
|
||||
r = {"content": [
|
||||
{"type": "text", "text": "summary here"},
|
||||
{"type": "image", "data": "A" * 340_000, "mimeType": "image/png"},
|
||||
]}
|
||||
out = mod.p_shrink_oversize(r)
|
||||
kinds = [c.get("type") for c in out["content"]]
|
||||
assert "image" not in kinds
|
||||
assert any("screenshot omitted" in str(c.get("text", "")) for c in out["content"])
|
||||
assert len(json.dumps({"result": out})) < mod.P_MAX_RESPONSE_BYTES
|
||||
|
||||
|
||||
def test_oversize_text_elided():
|
||||
mod = p_load()
|
||||
r = {"content": [{"type": "text", "text": "B" * 400_000}]}
|
||||
out = mod.p_shrink_oversize(r)
|
||||
assert len(json.dumps({"result": out})) < mod.P_MAX_RESPONSE_BYTES
|
||||
assert "elided" in out["content"][0]["text"]
|
||||
|
||||
|
||||
def test_error_results_keep_flag():
|
||||
mod = p_load()
|
||||
r = {"content": [{"type": "image", "data": "C" * 300_000, "mimeType": "image/png"}], "isError": True}
|
||||
out = mod.p_shrink_oversize(r)
|
||||
assert out.get("isError") is True
|
||||
Reference in New Issue
Block a user