Tests / Test passed: 863
Fixes issue 3: the host-agent console showed OCR results but never real UI-tree data, because _ui_tree_nodes() checked for a get_ui_tree/ui_tree tool action that has never existed anywhere in the codebase. - storage/timeline.py: add a ui_tree_results field to TimelineRecord and Timeline.append(), mirroring the existing ocr_results field. - runtime/task.py: _append_timeline() now extracts scene.elements with source == "ui" into ui_tree_results (scene_builder.build_scene() already preserved these; they were just never persisted). - host_agent/web/app.py: _ui_tree_nodes() reads the new field directly instead of the dead tool-action check. New _overlay_payload() exposes each step's scene dimensions and fused element list for client-side rendering. - task_detail.html: adds a toggle to overlay OCR (orange) and UI-tree (blue) bounding boxes on the before-action screenshot, plus a visual marker for the actually executed action (tap circle, or an animated swipe path) using an SVG viewBox so no manual coordinate-scaling JS is needed. Legacy/incomplete records degrade to no overlay, never an error. Also corrects openspec/specs/runtime-task-evidence and host-agent-console-task-pages, which had encoded the same nonexistent-tool assumption, via the new host-agent-console-visual-evidence change. 600 tests passing; ruff/compileall/openspec validate all clean.
5.4 KiB
5.4 KiB
1. Timeline — persist UI-tree elements per step
- 1.1 Add
ui_tree_results: list[dict[str, Any]] = field(default_factory=list)toTimelineRecordinstorage/timeline.py, mirroringocr_results - 1.2 Add
ui_tree_results: list[dict[str, Any]] | None = Noneparameter toTimeline.append(), stored the same way asocr_results - 1.3 Add unit tests:
Timeline.append()withui_tree_results→ record round-trips it; without it → record defaults to[]; a record serialized before this field existed → reads as[]without error
2. Runtime — extract UI-tree elements into the Timeline
- 2.1 In
runtime/task.py::_append_timeline(), computeui_tree_results = [element.to_dict() for element in scene.elements if element.source == "ui"]and pass it toself.timeline.append(..., ui_tree_results=ui_tree_results) - 2.2 Add unit tests: a scene with
source == "ui"elements →ui_tree_resultspopulated in the appended record; a scene with no UI elements →ui_tree_resultsis[]; a scene with mixedui/ocr/merged elements → onlysource == "ui"elements appear inui_tree_resultsand onlysource == "ocr"(or fallback) elements appear inocr_results
3. Host Agent console — render real UI-tree data
- 3.1 Rewrite
_ui_tree_nodes()inapps/device-host-agent/host_agent/web/app.pyto readrecord.get("ui_tree_results", [])directly (list of dicts), removing the deadtool_call.get("action") in {"get_ui_tree", "ui_tree"}check - 3.2 Confirm
_timeline_step_context()passes the correctedui_tree_nodesthrough unchanged; notask_detail.htmlmarkup changes needed for the list view itself (existingstep.ui_tree_nodesblock already renders it) - 3.3 Add/update unit tests for
_ui_tree_nodes(): a record withui_tree_results→ returns those dicts; a record without the field (legacy) → returns[]; a record with non-dict entries → those entries are filtered out
4. Overlay toggle — OCR/UI-tree bounding boxes on the before-screenshot
- 4.1 Add
_overlay_payload(record)toapp.pyexposing each step'sscenescreen dimensions (width/height) and its fused element list (bounds/text/source, straight fromscene.elements— already the combined OCR+UI-tree view) asstep.overlay; embedded per-step as an inline<script type="application/json">blob intask_detail.html - 4.2 Add a single page-level toggle control (checkbox
#overlay-boxes-toggle) intask_detail.htmlthat adds/removes ashow-boxesclass on every step's overlay<svg> - 4.3 Render each step's overlay as an inline
<svg viewBox="0 0 {scene.width} {scene.height}">absolutely positioned over the before-screenshot<img>; boxes are drawn as<rect>elements directly in scene-pixel coordinates, so the SVG viewBox scaling handles image-size scaling natively — no manual JS scale computation or resize listener needed (simpler than the originally planned separate static JS file with manual scaling) - 4.4 OCR boxes (
source="ocr") and UI-tree boxes (source="ui") get distinct stroke colors (orange vs. blue) via CSS classes - 4.5 Verified:
_overlay_payload()degrades to{"width": 0, "height": 0, "elements": []}for a record with no/malformedscene; the template only emits the<svg>whenoverlay.width/overlay.heightare truthy, so legacy records render the screenshot with no overlay markup at all
5. Action-effect visualization — tap marker / swipe path
- 5.1 Inline JS in
task_detail.htmlreads each step's already-embeddedtool_call.action/args; draws an SVG<circle>marker fortap, or an SVG<line>plus an<animateMotion>-animated dot forswipe; any other action draws nothing - 5.2 Action markers are drawn in the same
<svg viewBox>as the overlay boxes (task 4.3), so no separate scaling logic was needed - 5.3 Verified:
buildActionGroup()checksisFinite()on every required coordinate before drawing; missing/non-numeric args (or no<svg>at all, for a legacy record) render nothing, no error - 5.4 Rendered a real task-detail page (via the FastAPI test client with fabricated
tap/swipeTimeline records) and inspected the generated HTML/JSON:viewBox, box coordinates, tap marker coordinates, and swipe line/path coordinates all match the source data; opened the page in a browser for visual confirmation
6. Spec corrections
- 6.1
openspec/specs/runtime-task-evidence/spec.md(via this change's spec delta): UI-tree requirement no longer references aget_ui_tree/ui_treetool call - 6.2
openspec/specs/host-agent-console-task-pages/spec.md(via this change's spec delta): UI-tree scenario corrected; overlay toggle and action-effect requirements added
7. End-to-end verification
- 7.1 Run full non-integration test suite (
uv run --all-packages pytest -m "not integration") and confirm no regressions - 7.2 Run
ruff checkandruff format --checkon all modified Python files - 7.3 Run
python -m compileallon modified packages - 7.4 Run
openspec validate --strict host-agent-console-visual-evidenceand confirm all artifacts pass validation - 7.5 Manual smoke test (requires Host Agent + Appium + a device): run a live task, open its task-detail page, confirm UI-tree elements render, the overlay toggle shows/hides boxes correctly on the before-screenshot, and the tap/swipe action-effect marker/path renders correctly