Install and update artemis-use with task diagnostics and plugin guidance. Preserve cross-device file receipts, review canonical reports, guard stage transitions and reduce small history summaries. Improve exploration lifecycle, request recovery and browser live connections.
138 lines
5.2 KiB
Python
138 lines
5.2 KiB
Python
"""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
|