Files
openswarm/backend/tests/test_read_agent_work.py
T

151 lines
7.8 KiB
Python

"""ENG-389: a parent reads a child's work off our own record instead of asking the child model to
say it again.
The class this closes is not a wording bug. Asking a model to restate its prior output IS a
reproduction request, on a lane whose filter looks for exactly that; delegation-bearing chats block
at 13.0% against a 2.7% baseline. `defuse_extraction_ask` lowers the rate and cannot close the
class, because the 4th real blocked prompt was already well-worded ("Quick handoff: what project
were you working on... Summarize the build plan you landed on"). The fix is that the prompt is
never written."""
import json
from backend.apps.agents import invoke_agent_mcp_server as inv
from backend.apps.agents.core.models import AgentSession, Message
from backend.apps.agents.manager.session.history_compaction import (
get_branch_messages,
render_agent_trail,
)
P_TOOL_NAMES = [t["name"] for t in inv.TOOLS]
def p_session() -> AgentSession:
s = AgentSession(id="child1", name="Build agent", status="completed")
s.messages = [
Message(role="user", content="Port the exporter to the new schema", branch_id="main"),
Message(role="tool_call", content={"tool": "Bash", "input": {"command": "pytest backend/tests"}}, branch_id="main"),
Message(role="tool_result", content={"tool_name": "Bash", "text": "3 failed, 20 passed"}, branch_id="main"),
Message(role="assistant", content="Three tests fail on the date column; I stopped there.", branch_id="main"),
]
return s
def test_the_tool_exists_and_is_the_one_the_description_points_at():
assert "ReadAgentWork" in P_TOOL_NAMES
invoke = next(t for t in inv.TOOLS if t["name"] == "InvokeAgent")
assert "ReadAgentWork" in invoke["description"], "InvokeAgent must name the read tool as the alternative"
def test_invoke_agent_no_longer_teaches_the_extraction_ask():
"""The tool description used to say 'query another agent about its prior work', which is the
exact shape the filter refuses. A tool that teaches the bad prompt makes every wording gate
downstream a losing game."""
d = next(t for t in inv.TOOLS if t["name"] == "InvokeAgent")["description"].lower()
assert "query another agent about its prior work" not in d
for shape in ("restate", "reproduce", "verbatim", "say it again", "repeat what"):
assert shape not in d, f"InvokeAgent's description still asks a model to {shape}"
def test_no_delegation_prompt_anywhere_asks_a_model_to_restate_its_output():
"""The issue's acceptance criterion, as a grep with teeth. It reads the delegation sources
rather than one function, because the shape only has to survive in ONE of them to keep the
class alive."""
p_files = [
"backend/apps/agents/invoke_agent_mcp_server.py",
"backend/apps/agents/spawn_agent_mcp_server.py",
"backend/apps/agents/manager/AgentLaunch.py",
]
p_bad = ("verbatim", "word for word", "word-for-word", "exactly as you", "repeat what you",
"restate your", "reproduce your", "dump of your")
for path in p_files:
body = open(path, encoding="utf-8").read().lower()
for shape in p_bad:
assert shape not in body, f"{path} still carries an extraction-shaped phrase: {shape!r}"
def test_the_work_it_returns_is_the_trail_we_already_store():
"""Reused, not reinvented: what is safe to send another model has one definition, shared with
the session recap and the workflow transcript."""
s = p_session()
trail = render_agent_trail(get_branch_messages(s))
assert "Port the exporter" in trail
assert "pytest backend/tests" in trail, "the tool trail is the point; it must survive"
assert "3 failed, 20 passed" in trail
assert "Three tests fail on the date column" in trail, "the run's own outcome must come home"
def test_it_never_emits_a_role_tagged_replay():
"""The shape ENG-358 removed from the recap and ENG-396 found in two more renderers. A third
door for it is how the class comes back."""
trail = render_agent_trail(get_branch_messages(p_session()))
for line in trail.splitlines():
assert not line.lstrip().startswith(("USER:", "ASSISTANT:", "User:", "Assistant:"))
def test_a_missing_session_is_an_error_not_an_empty_success():
"""A read that quietly returns nothing reads to a model as 'that agent did nothing', which is a
lying status, not a missing one."""
out = inv.handle_read_agent_work({"session_id": ""})
assert out.get("isError") and "session_id is required" in out["content"][0]["text"]
def test_a_session_with_no_work_says_so_rather_than_looking_empty():
inv.read_work = lambda sid: {"session_id": sid, "name": "Idle", "status": "completed", "work": ""}
out = inv.handle_read_agent_work({"session_id": "x"})
assert not out.get("isError")
assert "has not done any work yet" in out["content"][0]["text"]
def test_read_agent_work_inherits_a_denied_invoke_policy():
"""Never widen a tool surface silently: a user who denied delegation denied this too, unless
they say otherwise."""
from backend.apps.agents.manager.register_builtin_mcp_servers import register_builtin_mcp_servers
s = p_session()
perms = {"InvokeAgent": "deny"}
servers = {}
register_builtin_mcp_servers(servers, s, perms, None, None)
assert perms["ReadAgentWork"] == "deny"
mods = servers["openswarm-core"]["env"]["OSW_MCP_MODULES"].split(",")
assert "invoke" not in mods, "denying InvokeAgent must not leave the module loaded for the read tool"
def test_it_is_reachable_by_default():
"""The liveness half: a guard that never fires and a tool nobody can call look identical."""
from backend.apps.agents.manager.register_builtin_mcp_servers import register_builtin_mcp_servers
servers = {}
perms = {}
register_builtin_mcp_servers(servers, p_session(), perms, None, None)
assert "invoke" in servers["openswarm-core"]["env"]["OSW_MCP_MODULES"].split(",")
assert perms["ReadAgentWork"] == "always_allow"
def test_the_route_reads_a_session_that_is_only_on_DISK():
"""The bug the packaged drill found: the route used the in-memory map alone, so it 404'd on
every session the dashboard had not restored -- which is most of them, and exactly the ones
worth reading. GET /sessions/{id} already had this fallback; the read tool did not."""
src = open("backend/apps/agents/agents.py", encoding="utf-8").read()
i = src.index('async def get_session_work')
body = src[i:src.index("@agents.router", i + 10)]
assert "resume_session" in body, "a read tool that 404s on a stored session defeats its purpose"
assert body.index("agent_manager.get_session") < body.index("resume_session"), \
"memory first, disk second: resuming every read would be a pointless load"
def test_only_the_close_route_marks_a_session_dismissed():
"""ENG-421, found on the packaged build: `closed_at` cannot mean "the user put this away",
because the workflow executor stamps it at the end of every step including a stopped one. Stop
and Close were therefore indistinguishable and a card the user stopped to READ vanished ~2s
later. The flag belongs at the close DOOR, not in agent_manager.close_session, which the
executor also calls."""
src = open("backend/apps/agents/agents.py", encoding="utf-8").read()
i_close = src.index("async def close_session(")
i_stop = src.index("async def stop_agent(")
close_body = src[i_close:i_close + 700]
stop_body = src[i_stop:i_stop + 700]
assert "dismissed_by_user = True" in close_body, "Close must say the user put it away"
assert "dismissed_by_user" not in stop_body, "Stop must NOT read as a dismissal"
helper = open("backend/apps/agents/manager/SessionControl.py", encoding="utf-8").read()
assert "dismissed_by_user" not in helper, \
"the executor calls this helper for bookkeeping; a flag here would dismiss stopped runs again"