import asyncio from types import SimpleNamespace import pytest from litellm import Message import agent.main as main_mod from agent.core.agent_loop import process_submission from agent.core.session import Event, OpType, Session from agent.tools import plan_tool class _FakeConfig: model_name = "openai/gpt-5.5:fal-ai" save_sessions = False session_dataset_repo = "fake/repo" auto_save_interval = 1 heartbeat_interval_s = 60 max_iterations = 10 yolo_mode = False confirm_cpu_jobs = False auto_file_upload = False reasoning_effort = None share_traces = False personal_trace_repo_template = None mcpServers: dict = {} class _FakeContext: def __init__(self) -> None: self.items = [ Message(role="system", content="system prompt"), Message(role="user", content="old task"), Message(role="assistant", content="old answer"), ] self.model_max_tokens = 200_000 self.running_context_usage = 123 self.on_message_added = None def _make_session() -> Session: return Session( event_queue=asyncio.Queue(), config=_FakeConfig(), tool_router=None, context_manager=_FakeContext(), hf_token=None, user_id="user-a", local_mode=True, ) def test_start_new_conversation_rotates_session_state(monkeypatch): plan_tool.reset_current_plan() session = _make_session() session.config.save_sessions = True session.turn_count = 2 session.last_auto_save_turn = 2 session.logged_events = [{"event_type": "turn_complete", "data": {}}] session._local_save_path = "session_logs/old.json" session.pending_approval = {"tool_calls": ["pending"]} session.auto_approval_estimated_spend_usd = 1.25 session.current_plan = [ {"id": "old-session", "content": "old session item", "status": "pending"} ] monkeypatch.setattr( plan_tool, "_current_plan", [{"id": "global", "content": "global item", "status": "in_progress"}], ) old_session_id = session.session_id uploads: list[str] = [] def fake_upload(repo_id: str) -> str: uploads.append(repo_id) return "session_logs/saved.json" monkeypatch.setattr(session, "save_and_upload_detached", fake_upload) result = session.start_new_conversation() assert uploads == ["fake/repo"] assert result["previous_session_id"] == old_session_id assert result["previous_turn_count"] == 2 assert result["saved_path"] == "session_logs/saved.json" assert session.session_id != old_session_id assert [msg.role for msg in session.context_manager.items] == ["system"] assert session.context_manager.items[0].content == "system prompt" assert session.context_manager.running_context_usage == 0 assert session.turn_count == 0 assert session.last_auto_save_turn == 0 assert session.logged_events == [] assert session._local_save_path is None assert session.pending_approval is None assert session.auto_approval_estimated_spend_usd == 0.0 assert session.current_plan == [] assert plan_tool.get_current_plan() == [] @pytest.mark.asyncio async def test_new_submission_resets_context_and_reports_clear_flag(): session = _make_session() submission = SimpleNamespace( operation=SimpleNamespace( op_type=OpType.NEW, data={"clear_screen": True}, ) ) should_continue = await process_submission(session, submission) event: Event = await session.event_queue.get() assert should_continue is True assert event.event_type == "new_complete" assert event.data["clear_screen"] is True assert event.data["session_id"] == session.session_id assert [msg.role for msg in session.context_manager.items] == ["system"] assert [e["event_type"] for e in session.logged_events] == ["new_complete"] @pytest.mark.asyncio async def test_new_and_clear_slash_commands_share_operation_with_distinct_clear_flags(): submission_id = [0] holder = [object()] new_submission = await main_mod._handle_slash_command( "/new", _FakeConfig(), holder, asyncio.Queue(), submission_id, ) clear_submission = await main_mod._handle_slash_command( "/clear", _FakeConfig(), holder, asyncio.Queue(), submission_id, ) assert new_submission is not None assert new_submission.operation.op_type == OpType.NEW assert new_submission.operation.data == {"clear_screen": False} assert clear_submission is not None assert clear_submission.operation.op_type == OpType.NEW assert clear_submission.operation.data == {"clear_screen": True} assert submission_id == [2]