"""Mixed-task acceptance must see each resource's actual recorded captures.""" import hashlib from types import SimpleNamespace from unittest.mock import Mock from artemis.agents.checker.evidence import final_evidence def engine(tmp_path, steps): return SimpleNamespace( storage=SimpleNamespace(get_steps=lambda _: steps), current_session_id="task", get_image_path=lambda name: tmp_path / f"{name}.jpg", ) def step(n, pre=None, post=None, action=None, execution=None): return SimpleNamespace( step_number=n, timestamp=float(n), pre_image_name=pre, post_image_name=post, action_taken=action or {"action": "swipe"}, last_execution_result={"execution": execution} if execution else {}, ) def image(tmp_path, value): name = hashlib.sha256(value).hexdigest() (tmp_path / f"{name}.jpg").write_bytes(value) return name def pc(post, pre=None, target="browser", width=1280): return { "action": "plug_scroll", "plugin_id": "cua-driver", "source": { "kind": "computer", "target_id": target, "width": width, "captured_at": 7, }, "pre_image_name": pre, "post_image_name": post, "pre_image_valid": bool(pre), } def test_phone_and_unchanged_pc_captures_are_both_sent_with_origin(tmp_path): phone = image(tmp_path, b"phone") browser = image(tmp_path, b"pc") # Old records deduplicated the step's post ref; per-call receipt still has it. steps = [ step(15, post=phone), step(27, action=pc(browser, browser)), step(28, action=pc(browser, browser)), ] blocks = final_evidence(engine(tmp_path, steps)) assert sum(b["type"] == "image_url" for b in blocks) == 2 text = str(blocks) assert "android" in text and "computer" in text and "cua-driver" in text assert "Step 27" in text and "Step 28" in text assert "identical to pre-capture" in text assert "not proof of success" in text assert "saved screenshot file=" in text assert "Distinct capture events may share one file" in text def test_unchanged_android_boundary_capture_is_saved_evidence_not_missing_export(tmp_path): footer = image(tmp_path, b"same-footer") blocks = final_evidence( engine(tmp_path, [step(26, post=footer), step(28, pre=footer, post=footer)]) ) text = str(blocks) assert "Step 26" in text and "Step 28" in text assert str(tmp_path / f"{footer}.jpg") in "\n".join(b.get("text", "") for b in blocks) assert "automatic trace screenshot is recorded evidence" in text assert sum(b["type"] == "image_url" for b in blocks) == 1 def test_native_resolution_change_does_not_hide_previous_same_window_capture(tmp_path): a = image(tmp_path, b"native") b = image(tmp_path, b"scaled") blocks = final_evidence( engine(tmp_path, [step(1, action=pc(a, width=2560)), step(2, action=pc(b))]), max_sources=1 ) assert sum(x["type"] == "image_url" for x in blocks) == 2 assert "Step 1" in str(blocks) and "Step 2" in str(blocks) def test_burst_uses_per_call_sources_and_never_phone_step_fallback(tmp_path): a = image(tmp_path, b"A") b = image(tmp_path, b"B") blocks = final_evidence( engine(tmp_path, [step(4, post="phone", execution=[pc(a, target="A"), pc(b, target="B")])]) ) assert sum(x["type"] == "image_url" for x in blocks) == 2 assert "android" not in str(blocks) def test_missing_images_and_untrusted_paths_are_not_fabricated(tmp_path): e = engine( tmp_path, [step(1, action=pc("a" * 64)), step(2, action=pc("../private", target="B"))] ) e.get_image_path = Mock(side_effect=e.get_image_path) blocks = final_evidence(e) assert "unavailable" in str(blocks) assert not any(x["type"] == "image_url" for x in blocks) e.get_image_path.assert_called_once_with("a" * 64) def test_last_sources_are_selected_by_execution_order_not_string_step_number(tmp_path): name = image(tmp_path, b"frame") steps = [step(9, action=pc(name, target="old")), step(10, action=pc(name, target="new"))] blocks = final_evidence(engine(tmp_path, steps), max_sources=1) assert "Step 10" in str(blocks) and "Step 9" not in str(blocks) assert "Additional resources omitted" in str(blocks) def test_later_call_without_observation_marks_previous_picture_historical(tmp_path): name = image(tmp_path, b"old") blocks = final_evidence(engine(tmp_path, [step(1, action=pc(name)), step(2, action=pc(None))])) assert "Step 2" in str(blocks) and "no post-action image" in str(blocks) assert "Earlier captures do not prove" in str(blocks) assert sum(x["type"] == "image_url" for x in blocks) == 1 def test_file_receipt_is_linked_to_its_resource_image_without_an_extra_image(tmp_path): name = image(tmp_path, b"pc") action = pc(name) action["attachments"] = [{"path": "artifacts/plugins/cua-driver/pc.png", "sha256": name}] blocks = final_evidence(engine(tmp_path, [step(35, action=action)])) assert "runtime-verified saved attachments" in str(blocks) assert "artifacts/plugins/cua-driver/pc.png" in str(blocks) assert sum(x["type"] == "image_url" for x in blocks) == 1