## 1. Device config & settings store - [x] 1.1 Add `storage/device_config.py` with a SQLite-backed `DeviceConfigStore` (`device_id, name, driver_type, connection_info json`) following the `TaskMetadataStore` pattern, with `add`, `remove`, `list`, and `get` methods - [x] 1.2 Add a `settings(key, value)` table to the same store with `get_setting`/`set_setting` helpers, seeded with a default `max_steps` - [x] 1.3 Add unit tests for `DeviceConfigStore` (add/remove/list, settings get/set, unknown key/device handling) ## 2. Console status API - [x] 2.1 Add `api/console.py` with an `APIRouter`; implement `GET /console/devices` (list + status via `DeviceManager.list_devices()`) - [x] 2.2 Implement `GET /console/tasks` (via `TaskMetadataStore.list_tasks()`) with optional `device_id`/`status` query filters - [x] 2.3 Implement `GET /console/tasks/{task_id}` (404 on unknown id) - [x] 2.4 Implement `GET /console/tasks/{task_id}/timeline` reading `Timeline.read(task_id)` / `ArtifactStore`, inlining screenshots as base64 - [x] 2.5 Add tests for all four endpoints covering populated and empty states, and the 404 case ## 3. Console config API - [x] 3.1 Implement `POST /console/devices`: validate `driver_type` against a supported-driver registry (`{"wda": ...}`), build the `driver_factory`, call `DeviceManager.register_device`, persist via `DeviceConfigStore`; return `400` for unsupported driver types - [x] 3.2 Implement `DELETE /console/devices/{device_id}`: call `DeviceManager.unregister_device` and remove the persisted config; return `404` for unknown device ids - [x] 3.3 Implement `GET /console/config` and `PUT /console/config` for `max_steps`, validating positive values (`400` otherwise), applying the change to the live `TaskRunner.config` and persisting it via the settings table - [x] 3.4 Wire startup reload: in `create_app()`, read all persisted device configs and the persisted `max_steps` setting, register devices and construct `TaskRunnerConfig` accordingly, before returning the app - [x] 3.5 Mount the `console` router into `create_app()` in `api/rest.py` and enable permissive CORS for local frontend development - [x] 3.6 Add tests for register/unregister (success + validation errors), config get/update (success + invalid value), and startup reload of persisted devices/settings ## 4. Web console frontend (independent SPA) - [x] 4.1 Scaffold an independent Vue 3 + Vite SPA project (in-repo `console/` per design's default) with its own `package.json`/build tooling - [x] 4.2 Implement the device status dashboard view (list + empty state) against `GET /console/devices` - [x] 4.3 Implement the task list view (with device/status filters) and task detail + timeline replay view (screenshots) against the console-status-api endpoints - [x] 4.4 Implement the device configuration screen (add/remove device forms, surfacing API validation errors) against the console-config-api endpoints - [x] 4.5 Implement the runtime parameter settings form (`max_steps`) against `GET/PUT /console/config` - [x] 4.6 Document how to run the frontend dev server against the local backend (base URL config, CORS expectations) ## 5. Verification - [x] 5.1 Run the full backend test suite (`pytest`) and confirm no regressions to existing REST/MCP endpoints - [ ] 5.2 Manually verify end-to-end: register a device via the UI, run a task via the existing `/agent/task` endpoint, and confirm its status/timeline/screenshots render correctly in the console