diff --git a/backend/apps/agents/apps_mcp_server.py b/backend/apps/agents/apps_mcp_server.py index f1914e6a..e112f734 100644 --- a/backend/apps/agents/apps_mcp_server.py +++ b/backend/apps/agents/apps_mcp_server.py @@ -94,16 +94,16 @@ def handle_tool_call(tool_name: str, arguments: dict) -> dict: }) if "error" in result: return {"content": [{"type": "text", "text": f"Error: {result['error']}"}], "isError": True} + path = result.get("path") + # Point at SKILL.md instead of inlining the ~6.5k-token reference: it's written to the workspace at seed time, so a one-time Read keeps it out of the transcript (where an inline copy would rot for the rest of the session). Read it BEFORE building. lines = [ f"App '{name}' created and its live preview card is now on the user's dashboard.", - f"- workspace: {result.get('path')}", + f"- workspace: {path}", f"- output_id: {result.get('output_id')}", "", - "Build it by writing files under the workspace path; the preview hot-reloads on save.", + f"NEXT: read {path}/SKILL.md now — it's the full App Builder reference (stack, layout, rules); follow it.", + "Then build by writing files under the workspace path; the preview hot-reloads on save.", "Housekeeping: write meta.json (name/description) first; `bash restart.sh` restarts the runtime; `.openswarm/terminal.log` is the live terminal (check it before declaring done).", - "The full App Builder reference follows — FOLLOW IT (it also lives at SKILL.md in the workspace):", - "", - str(result.get("skill") or ""), ] return {"content": [{"type": "text", "text": "\n".join(lines)}]} diff --git a/backend/apps/outputs/outputs.py b/backend/apps/outputs/outputs.py index 8b2791d6..62dfd17c 100644 --- a/backend/apps/outputs/outputs.py +++ b/backend/apps/outputs/outputs.py @@ -274,11 +274,11 @@ async def agent_create_app(body: AgentCreateAppRequest): }) except Exception: logger.exception("agent-create output_upserted broadcast failed") + # The reference isn't returned here: it's written to /SKILL.md at seed time and the agent reads it on demand, keeping the ~6.5k-token blob out of both this response and the agent transcript. return { "ok": True, "output_id": output_id, "path": os.path.abspath(folder), - "skill": load_app_builder_skill(), } diff --git a/backend/tests/test_agent_create_app.py b/backend/tests/test_agent_create_app.py index 67ccfd71..e2e8aece 100644 --- a/backend/tests/test_agent_create_app.py +++ b/backend/tests/test_agent_create_app.py @@ -34,7 +34,11 @@ async def test_agent_create_registers_named_output_and_broadcasts(tmp_path, monk )) assert res["ok"] is True assert os.path.isfile(os.path.join(res["path"], "run.sh")) - assert "App Builder" in res["skill"] or len(res["skill"]) > 500 + # The reference is NOT inlined in the response (context-rot fix) — it's on disk as SKILL.md for the agent to read on demand. + assert "skill" not in res + skill_path = os.path.join(res["path"], "SKILL.md") + assert os.path.isfile(skill_path) + assert len(open(skill_path, encoding="utf-8").read()) > 500 output = load_output(res["output_id"]) assert output.name == "Pomodoro Timer"