Files
Artemis-Eapil/artemis/agents/checker/evidence.py
T
chenmingxuan e1ea099592 feat: add official Codex skill and harden task execution
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.
2026-10-18 14:18:37 +08:00

123 lines
5.3 KiB
Python

"""Bounded, source-labelled end-of-run evidence from the existing step ledger."""
import base64
from pathlib import Path
import re
from artemis.data_engine.step_evidence import step_image_name
def final_evidence(engine, *, max_sources: int = 8) -> list[dict]:
"""Attach the last two independently captured frames per resource.
These are historical captures, never advertised as live device state.
Equal hashes retain both capture labels but share one image block. No
screenshots from another resource are substituted for missing evidence.
"""
if engine is None:
return []
steps = engine.storage.get_steps(engine.current_session_id) or []
sources = {}
for step in steps:
result = step.last_execution_result
execution = result.get("execution") if isinstance(result, dict) else None
actions = execution if isinstance(execution, list) else step.action_taken
actions = actions if isinstance(actions, list) else [actions]
for action in actions:
if not isinstance(action, dict):
continue
plugin = action.get("plugin_id")
source = action.get("source") or {}
if plugin:
identity = (plugin, source.get("kind", "other"), source.get("target_id", ""))
name = action.get("post_image_name")
if len(actions) == 1:
name = name or step_image_name(step, "post")
pre = action.get("pre_image_name") if action.get("pre_image_valid") else None
else:
# A pure helper/report's decision frame is not a fresh post-action observation.
if str(action.get("action", "")).startswith("plug_"):
continue
identity = ("device", "android", "primary")
name, pre = step.post_image_name, step.pre_image_name
if not name and not plugin:
continue
label = f"Step {step.step_number} post-action; {identity}; {source.get('label', '')}"
label += f"; captured_at={source.get('captured_at') or step.timestamp}"
attachments = action.get("attachments")
if isinstance(attachments, list):
paths = [
item.get("path")
for item in attachments
if isinstance(item, dict) and item.get("path")
]
if paths:
label += f"; runtime-verified saved attachments from this call={paths[:8]} (storage evidence, not proof of visual correctness)"
if pre and pre == name:
label += (
"; fresh post-capture identical to pre-capture (not proof of success by itself)"
)
frames = sources.pop(identity, [])
sources[identity] = [*frames[-1:], (name, label)]
selected = list(sources.values())[-max_sources:]
blocks = []
attached = set()
for frames in selected:
for name, label in frames[-2:]:
if not name:
blocks.append(
{
"type": "text",
"text": f"Recorded evidence: {label}; no post-action image. Earlier captures do not prove the state after this call.",
}
)
continue
if not isinstance(name, str) or not re.fullmatch(r"[0-9a-f]{64}", name):
continue
path = Path(engine.get_image_path(name))
blocks.append({"type": "text", "text": f"Recorded evidence: {label}; image={name}"})
if name in attached:
blocks.append(
{
"type": "text",
"text": f"Same image as the preceding capture with this hash; saved screenshot file={path}. "
"Distinct capture events may share one file when their bytes are identical.",
}
)
continue
try:
image = path.read_bytes()
except OSError:
blocks.append(
{
"type": "text",
"text": "Recorded image unavailable; do not infer its contents.",
}
)
continue
blocks.append(
{
"type": "text",
"text": f"Saved screenshot file={path}; readable, bytes={len(image)}. "
"This automatic trace screenshot is recorded evidence even without a separately named export.",
}
)
mime = "image/png" if image.startswith(b"\x89PNG") else "image/jpeg"
if image.startswith(b"RIFF") and image[8:12] == b"WEBP":
mime = "image/webp"
blocks.append(
{
"type": "image_url",
"image_url": {"url": f"data:{mime};base64,{base64.b64encode(image).decode()}"},
}
)
attached.add(name)
if len(sources) > max_sources:
blocks.append(
{
"type": "text",
"text": "Additional resources omitted from this bounded preview; inspect them with get_step_screenshot before judging.",
}
)
return blocks