Ship War Room UX, CEO inbox, mobile chat, and Agents NAS workspace.

Make A2A togglable, keep reply context while reading, route agent data to //10.4.7.11/share/Agents, and polish themes plus export-intel sync cancel.
This commit is contained in:
Aissa
2026-08-16 22:40:32 +00:00
parent 910ba465d7
commit c5ea8cbb63
26 changed files with 2891 additions and 407 deletions
+2 -2
View File
@@ -21,9 +21,9 @@ AGENT_STATUSES = [
@router.get("")
async def agents_page(request: Request):
active_tab = request.query_params.get("tab", "souls")
active_tab = request.query_params.get("tab", "warroom")
if active_tab not in {"souls", "mesh", "terminals", "approvals", "warroom"}:
active_tab = "souls"
active_tab = "warroom"
events: list = []
try:
events = fetch_all(
+47 -1
View File
@@ -551,6 +551,7 @@ def api_execute_approved_request(request_id: int, body: ExecuteBody) -> dict[str
class WarroomMessageBody(BaseModel):
content: str = Field("", max_length=8000)
attachments: list[dict] = Field(default_factory=list)
reply_to_id: Optional[int] = None
@router.get("/warroom/rooms")
@@ -575,9 +576,12 @@ async def warroom_list_messages(room_id: int, limit: int = 100, after_id: int |
from app.services import agent_chat
agent_chat.ensure_warroom()
room = agent_chat.get_room(room_id)
if not room:
raise HTTPException(404, "Room niet gevonden")
items = agent_chat.list_messages(room_id, limit=min(limit, 300), after_id=after_id)
deleted_ids = agent_chat.list_deleted_ids(room_id) if after_id else []
return {"ok": True, "items": items, "deleted_ids": deleted_ids}
return {"ok": True, "room": room, "items": items, "deleted_ids": deleted_ids}
@router.post("/warroom/rooms/{room_id}/messages")
@@ -592,6 +596,7 @@ async def warroom_post_message(request: Request, room_id: int, body: WarroomMess
role=(user.get("role") if isinstance(user, dict) else None),
sender_avatar_url=(user.get("avatar_url") if isinstance(user, dict) else None),
attachments=body.attachments,
reply_to_id=body.reply_to_id,
)
except ValueError as exc:
raise HTTPException(400, str(exc)) from exc
@@ -615,6 +620,7 @@ async def warroom_post_message_stream(request: Request, room_id: int, body: Warr
role=(user.get("role") if isinstance(user, dict) else None),
sender_avatar_url=(user.get("avatar_url") if isinstance(user, dict) else None),
attachments=body.attachments,
reply_to_id=body.reply_to_id,
):
yield f"data: {json.dumps(ev, default=str)}\n\n"
except ValueError as exc:
@@ -715,6 +721,46 @@ async def warroom_continue(request: Request, room_id: int, turns: int = 2):
@router.get("/warroom/ceo-inbox")
async def warroom_ceo_inbox(unread: bool = False, limit: int = 50) -> dict[str, Any]:
"""Direct messages from agents to CEO (@aissa)."""
from app.services import agent_chat
items = agent_chat.list_ceo_inbox(unread_only=unread, limit=min(limit, 100))
return {
"ok": True,
"items": items,
"unread": agent_chat.ceo_inbox_unread_count(),
}
class CeoInboxReadBody(BaseModel):
ids: Optional[list[int]] = None
all: bool = False
@router.post("/warroom/ceo-inbox/read")
async def warroom_ceo_inbox_read(body: CeoInboxReadBody) -> dict[str, Any]:
from app.services import agent_chat
n = agent_chat.mark_ceo_inbox_read(body.ids, all_unread=bool(body.all))
return {"ok": True, "marked": n, "unread": agent_chat.ceo_inbox_unread_count()}
@router.delete("/warroom/rooms/{room_id}/messages")
async def warroom_clear_messages(room_id: int) -> dict[str, Any]:
"""Soft-delete all messages in a War Room / DM."""
from app.services import agent_chat
room = agent_chat.get_room(room_id)
if not room:
raise HTTPException(404, "Room niet gevonden")
n = agent_chat.clear_messages(room_id)
return {"ok": True, "cleared": n}
@router.delete("/warroom/rooms/{room_id}/messages/{message_id}")
async def warroom_delete_message(room_id: int, message_id: int) -> dict[str, Any]:
"""Soft-delete a War Room message."""
+5
View File
@@ -305,6 +305,11 @@ async def api_sync_places(request: Request):
return r.json()
@router.post("/api/export-intel/sync/cancel")
async def api_sync_cancel():
return await _proxy("POST", "/sync/cancel")
@router.get("/api/export-intel/sync/status")
async def api_sync_status():
return await _proxy("GET", "/sync/status")