diff --git a/frontend/app.js b/frontend/app.js index f47c0e4a..c68fef76 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -2674,11 +2674,17 @@ class AppController { this._ceoTerm?.appendMessage({ role: 'system', text: '/clear only works in EA chat.', source: 'system' }); return; } - // Forget old conversation and create a new one - this._eaChatConvId = null; - localStorage.removeItem('ea-chat-conv-id'); - await this._ensureEaChatConversation(); - this._ceoTerm?.showChat(this._EA_CHAT, []); + if (!this._eaChatConvId) { + await this._ensureEaChatConversation(); + } + if (!this._eaChatConvId) return; + try { + const resp = await fetch(`/api/conversation/${this._eaChatConvId}/clear`, { method: 'POST' }); + if (!resp.ok) throw new Error(`Server error (${resp.status})`); + this._ceoTerm?.showChat(this._EA_CHAT, []); + } catch (e) { + this._ceoTerm?.appendMessage({ role: 'system', text: `Failed to clear chat: ${e.message}`, source: 'system' }); + } }}, ]; } diff --git a/package.json b/package.json index 21ed7373..304c5aa1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@1mancompany/onemancompany", - "version": "0.7.77", + "version": "0.7.78", "description": "The AI Operating System for One-Person Companies", "bin": { "onemancompany": "bin/cli.js" diff --git a/pyproject.toml b/pyproject.toml index d3de0a5f..0b2023fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "onemancompany" -version = "0.7.77" +version = "0.7.78" description = "A one-man company simulation with pixel art visualization and LangChain AI agents" requires-python = ">=3.12" dependencies = [ diff --git a/src/onemancompany/api/routes.py b/src/onemancompany/api/routes.py index d0c27f9b..56ca1e18 100644 --- a/src/onemancompany/api/routes.py +++ b/src/onemancompany/api/routes.py @@ -6783,14 +6783,14 @@ async def close_conversation(conv_id: str, wait_hooks: bool = False) -> dict: @router.post("/api/conversation/{conv_id}/clear") async def clear_conversation_history(conv_id: str) -> dict: - """Clear all 1-on-1 message history for the current conversation's employee.""" + """Clear message history for all same-type conversations of this employee.""" try: conv = _get_conv_svc().get(conv_id) except ValueError: raise HTTPException(status_code=404, detail="Conversation not found") - if conv.type != "oneonone": - raise HTTPException(status_code=400, detail="Clear history is only supported for oneonone conversations") + if conv.type not in (ConversationType.ONE_ON_ONE.value, ConversationType.EA_CHAT.value): + raise HTTPException(status_code=400, detail="Clear history is only supported for oneonone and ea_chat conversations") if not conv.employee_id: raise HTTPException(status_code=400, detail="Conversation has no employee_id") @@ -6818,7 +6818,7 @@ async def clear_conversation_history(conv_id: str) -> dict: except Exception: logger.warning("[conversation] skip unreadable meta when clearing history: {}", conv_dir) continue - if c.type != "oneonone" or c.employee_id != conv.employee_id: + if c.type != conv.type or c.employee_id != conv.employee_id: continue scanned += 1 msg_path = conv_dir / "messages.yaml" diff --git a/tests/integration/test_conversation_api.py b/tests/integration/test_conversation_api.py index 4ce378fc..b6da4c17 100644 --- a/tests/integration/test_conversation_api.py +++ b/tests/integration/test_conversation_api.py @@ -183,6 +183,56 @@ async def test_clear_current_agent_oneonone_history(client, tmp_path): assert resp.json()["id"] == conv_id_current +@pytest.mark.asyncio +async def test_clear_ea_chat_history_persists_across_reopen(client, tmp_path): + # Old EA chat with history + resp = await client.post("/api/conversation/create", json={ + "type": "ea_chat", + "employee_id": "00004", + "tools_enabled": True, + }) + conv_id_old = resp.json()["id"] + + msg_path_old = tmp_path / "employees" / "00004" / "conversations" / conv_id_old / "messages.yaml" + msg_path_old.parent.mkdir(parents=True, exist_ok=True) + msg_path_old.write_text(yaml.dump([ + {"sender": "ceo", "role": "CEO", "text": "legacy ea", "timestamp": "2026-03-19T00:00:00+00:00", "attachments": []}, + ], allow_unicode=True), encoding="utf-8") + + await client.post(f"/api/conversation/{conv_id_old}/close") + + # Start a new current EA chat + resp = await client.post("/api/conversation/create", json={ + "type": "ea_chat", + "employee_id": "00004", + "tools_enabled": True, + "reuse_existing": False, + }) + conv_id_current = resp.json()["id"] + assert conv_id_current != conv_id_old + + # Clear current EA chat history + resp = await client.post(f"/api/conversation/{conv_id_current}/clear") + assert resp.status_code == 200 + data = resp.json() + assert data["status"] == "cleared" + assert data["employee_id"] == "00004" + + # Old history should be wiped on disk + assert yaml.safe_load(msg_path_old.read_text(encoding="utf-8")) == [] + + # Reopen EA chat should not bring old history back + resp = await client.post("/api/conversation/create", json={ + "type": "ea_chat", + "employee_id": "00004", + "tools_enabled": True, + }) + reopened_id = resp.json()["id"] + resp = await client.get(f"/api/conversation/{reopened_id}/messages") + assert resp.status_code == 200 + assert resp.json()["messages"] == [] + + @pytest.mark.asyncio async def test_create_invalid_type(client): resp = await client.post("/api/conversation/create", json={