from __future__ import annotations import io import json import stat import sys from pathlib import Path import pytest SCRIPTS = Path(__file__).resolve().parents[1] / "python" sys.path.insert(0, str(SCRIPTS)) import handoff_engine # noqa: E402 def _write(path: Path, records: list[dict]) -> None: path.write_text( "".join(json.dumps(record) + "\n" for record in records), encoding="utf-8", ) def _record( uuid: str, parent: str | None, record_type: str, *, content=None, **extra, ) -> dict: record = { "uuid": uuid, "parentUuid": parent, "sessionId": "session-1", "cwd": "/tmp", "isSidechain": False, "type": record_type, **extra, } if content is not None: record["message"] = {"role": record_type, "content": content} return record def test_build_plan_uses_latest_compaction_and_active_branch(tmp_path): session = tmp_path / "session-1.jsonl" records = [ { "type": "custom-title", "customTitle": "Memory Testing", "sessionId": "session-1", }, _record("old-user", None, "user", content="Discarded request"), _record( "old-answer", "old-user", "assistant", content=[{"type": "text", "text": "Discarded answer"}], ), _record( "boundary", "old-answer", "system", subtype="compact_boundary", content=None, ), _record( "summary", "boundary", "user", content="Claude's own compact summary", isCompactSummary=True, ), _record( "preserved", "summary", "assistant", content=[{"type": "text", "text": "Preserved conclusion"}], ), _record("new-user", "preserved", "user", content="Continue the task"), _record( "new-answer", "new-user", "assistant", content=[{"type": "text", "text": "Current answer"}], ), _record( "abandoned", "old-answer", "assistant", content=[{"type": "text", "text": "Abandoned branch"}], ), _record( "leaf", "new-answer", "assistant", content=[{"type": "text", "text": "Active leaf"}], ), ] _write(session, records) plan = handoff_engine.build_plan(str(session), tmp_path) assert plan.source.title == "Memory Testing" assert plan.source.leaf_uuid == "leaf" assert plan.source.compact_boundary_uuid == "boundary" serialized = json.dumps(plan.items) assert "Claude's own compact summary" in serialized assert "Preserved conclusion" in serialized assert "Active leaf" in serialized assert "Discarded request" not in serialized assert "Abandoned branch" not in serialized def test_build_plan_keeps_starting_project_when_tool_changes_cwd(tmp_path): session = tmp_path / "session-1.jsonl" records = [ _record("u1", None, "user", content="Work in this project"), _record( "a1", "u1", "assistant", content=[ { "type": "tool_use", "id": "call-1", "name": "Bash", "input": {"command": "cd /tmp/nested && pwd"}, } ], ), _record( "r1", "a1", "user", content=[ { "type": "tool_result", "tool_use_id": "call-1", "content": "/tmp/nested", } ], cwd="/tmp/nested", ), _record( "a2", "r1", "assistant", content=[{"type": "text", "text": "Done"}], cwd="/tmp/nested", ), ] _write(session, records) plan = handoff_engine.build_plan(str(session), tmp_path) assert plan.source.cwd == str(Path("/tmp").resolve()) def test_tool_calls_results_attachments_and_hidden_reasoning(tmp_path): session = tmp_path / "session-1.jsonl" records = [ {"type": "custom-title", "customTitle": "Tools", "sessionId": "session-1"}, _record("u1", None, "user", content="Inspect the file"), _record( "a1", "u1", "assistant", content=[ {"type": "thinking", "thinking": "private reasoning"}, {"type": "text", "text": "I will inspect it."}, { "type": "tool_use", "id": "call-1", "name": "Read", "input": {"file_path": "a.py"}, }, ], ), _record( "r1", "a1", "user", content=[ { "type": "tool_result", "tool_use_id": "call-1", "content": "print('ok')", } ], ), { "uuid": "attachment", "parentUuid": "r1", "sessionId": "session-1", "cwd": "/tmp", "isSidechain": False, "type": "attachment", "attachment": { "type": "file", "filename": "a.py", "content": { "type": "text", "file": {"filePath": "/tmp/a.py", "content": "print('ok')"}, }, }, }, _record( "a2", "attachment", "assistant", content=[{"type": "text", "text": "The file prints ok."}], ), ] _write(session, records) plan = handoff_engine.build_plan(str(session), tmp_path) assert plan.hidden_reasoning_blocks_skipped == 1 assert not any("private reasoning" in json.dumps(item) for item in plan.items) call = next(item for item in plan.items if item["type"] == "function_call") result = next(item for item in plan.items if item["type"] == "function_call_output") assert call == { "type": "function_call", "call_id": "call-1", "name": "Read", "arguments": '{"file_path":"a.py"}', } assert result["call_id"] == "call-1" assert result["output"] == "print('ok')" assert any("