diff --git a/src/everos_mcp/server.py b/src/everos_mcp/server.py index 827c6b2..3c519be 100644 --- a/src/everos_mcp/server.py +++ b/src/everos_mcp/server.py @@ -61,6 +61,13 @@ log = logging.getLogger("everos_mcp") +# Extraction names each speaker by sender_name, falling back to sender_id, so +# without these the stored memories would read "default-user said ..." or, on +# the HTTP server, quote the user's opaque account id. +USER_SENDER_NAME = "User" +ASSISTANT_SENDER_NAME = "Assistant" + + def _data(body: str) -> str: """Fence recalled text: it was written in earlier sessions, possibly from untrusted content, so it must read as data. Everything the server itself @@ -226,12 +233,19 @@ async def add_memory( messages = [] if user_message: messages.append( - {"sender_id": s.user_id, "role": "user", "timestamp": ts, "content": user_message} + { + "sender_id": s.user_id, + "sender_name": USER_SENDER_NAME, + "role": "user", + "timestamp": ts, + "content": user_message, + } ) if assistant_message: messages.append( { "sender_id": s.assistant_sender_id, + "sender_name": ASSISTANT_SENDER_NAME, "role": "assistant", "timestamp": ts + 1, "content": assistant_message, @@ -434,6 +448,8 @@ async def record_trajectory(messages: list[dict[str, Any]], ctx: Context | None "timestamp": ts + i, "content": m.get("content") or "", } + if role != "tool": + item["sender_name"] = USER_SENDER_NAME if role == "user" else ASSISTANT_SENDER_NAME if m.get("tool_calls"): item["tool_calls"] = _normalize_tool_calls(m["tool_calls"]) if role == "tool": diff --git a/tests/test_everos_mcp.py b/tests/test_everos_mcp.py index b6d4373..3deb9a4 100644 --- a/tests/test_everos_mcp.py +++ b/tests/test_everos_mcp.py @@ -267,6 +267,17 @@ def test_notices_are_attached_once(tool_client): assert "boom" in first and "boom" not in second +def test_add_memory_names_both_speakers(tool_client): + # Extraction falls back to sender_id when there is no name, which puts + # "default-user" or an opaque account id into the stored memory text. + seen = tool_client(routes={"add": {"data": {"status": "extracted"}}}) + run(server.add_memory(user_message="u", assistant_message="a", wait=True)) + user, assistant = sent(seen[0])["messages"] + assert (user["sender_id"], user["sender_name"]) == (SETTINGS.user_id, "User") + assert assistant["sender_id"] == SETTINGS.assistant_sender_id + assert assistant["sender_name"] == "Assistant" + + def test_add_memory_refuses_secret_without_network(tool_client): seen = tool_client() reply = run(call_and_drain(server.add_memory(user_message="key sk-abcdefghijklmnop123"))) @@ -312,6 +323,8 @@ def test_record_trajectory_lands_in_the_conversation_session(tool_client): assert payload["session_id"] == SETTINGS.session_id assert paths(seen) == ["add", "flush"] assert payload["messages"][1]["tool_calls"][0]["function"]["name"] == "t" + names = [m.get("sender_name") for m in payload["messages"]] + assert names == ["User", "Assistant", None, "Assistant"] def test_record_trajectory_validates_tool_messages(tool_client):