diff --git a/api/routes.py b/api/routes.py index 877f8a8c..f55aeefe 100644 --- a/api/routes.py +++ b/api/routes.py @@ -12746,6 +12746,8 @@ def handle_post(handler, parsed) -> bool: if "name" not in body: return bad(handler, "Missing required field: name") sid = body["session_id"] + if _session_is_subagent_view_only(sid): + return bad(handler, "Subagent sessions are view-only and cannot be modified from WebUI", 400) name = body["name"].strip() try: s = get_session(sid) @@ -12832,6 +12834,8 @@ def handle_post(handler, parsed) -> bool: except ValueError as e: return bad(handler, str(e)) sid = body["session_id"] + if _session_is_subagent_view_only(sid): + return bad(handler, "Subagent sessions are view-only and cannot store a draft from WebUI", 400) text = body.get("text") files = body.get("files") # Stage-326 hardening (per Opus advisor): size + type validation on @@ -19222,6 +19226,8 @@ def _normalize_chat_attachments(raw_attachments): def _handle_chat_sync(handler, body): """Fallback synchronous chat endpoint (POST /api/chat). Not used by frontend.""" + if _session_is_subagent_view_only(str(body.get("session_id") or "")): + return bad(handler, "Subagent sessions are view-only and cannot be written from WebUI", 400) s = get_session(body["session_id"]) msg = str(body.get("message", "")).strip() if not msg: @@ -21658,6 +21664,8 @@ def _handle_handoff_summary(handler, body): from api.models import get_cli_session_messages, count_conversation_rounds, CONVERSATION_ROUND_THRESHOLD + if _session_is_subagent_view_only(sid): + return bad(handler, "Subagent sessions are view-only and cannot be summarized from WebUI", 400) rounds = count_conversation_rounds(sid, since=since) if rounds < CONVERSATION_ROUND_THRESHOLD: return bad(handler, "Not enough conversation rounds to generate a summary.", 400)