fix(host-agent): thread device manager into task runner observer/screenshot

create_task_runner() built TaskRunner's observer/screenshot_provider by
calling describe_screen(device_id)/take_screenshot(device_id) without
manager=, so both silently fell back to the process-global DEFAULT_MANAGER
singleton instead of the Host Agent's real, device-populated DeviceManager.
DEFAULT_MANAGER never has any device registered, so every task's first step
raised DeviceNotFoundError even though the console (which does pass
manager=) showed the same device as connected. Deterministic on every task,
independent of process count.

Add regression tests confirming both lambdas now resolve devices via the
configured manager; verified each fails with the original DeviceNotFoundError
symptom when the fix is reverted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 16:35:48 +08:00
co-authored by Claude Sonnet 5
parent 82567fd248
commit 08cef7ca3c
2 changed files with 104 additions and 0 deletions
@@ -14,6 +14,8 @@ from runtime.planner_config import PlannerConfig, load_config as load_planner_co
from runtime.task import TaskRunner
from storage.task_metadata import TaskMetadataStore
from storage.timeline import Timeline
from tools.describe_screen import describe_screen
from tools.screenshot import take_screenshot
from workflow.runner import WorkflowRunner
from workflow.store import WorkflowStore
@@ -39,6 +41,10 @@ def create_execution_factories(
def create_task_runner() -> TaskRunner:
return TaskRunner(
executor=Executor(tools=default_tool_registry(manager=manager)),
observer=lambda device_id: describe_screen(device_id, manager=manager),
screenshot_provider=lambda device_id: take_screenshot(
device_id, manager=manager
),
metadata_store=metadata_store,
timeline=timeline,
planner=_host_agent_planner(resolved_host_agent_config),