feat: checkpoint device agent runtime milestones
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Observe-Think-Act-Observe execution loop
|
||||
The system SHALL execute a task as a repeating loop: observe the current Scene, decide the next step, act via a capability tool call, then observe the resulting Scene again, continuing until the goal is met, a failure ceiling is hit, or the task is cancelled.
|
||||
|
||||
#### Scenario: Loop continues until goal is met
|
||||
- **WHEN** a task with a natural-language goal (e.g. "open Taobao and search Mac mini") is started
|
||||
- **THEN** the runtime repeats observe→think→act until the Planner/Executor determines the goal has been reached, then marks the task complete
|
||||
|
||||
#### Scenario: Loop stops after max steps
|
||||
- **WHEN** a task exceeds a configured maximum number of steps without reaching its goal
|
||||
- **THEN** the runtime stops the loop and marks the task as failed with a reason, instead of looping indefinitely
|
||||
|
||||
### Requirement: Planner produces a step plan from goal and current Scene
|
||||
The system SHALL provide a Planner that, given a goal and the current Scene, produces an ordered list of intended next steps (e.g. "find search box", "tap it", "type query").
|
||||
|
||||
#### Scenario: Planner emits steps for a new goal
|
||||
- **WHEN** the Planner is invoked with a goal and the current Scene at the start of a task
|
||||
- **THEN** it returns a non-empty ordered list of intended steps for the Executor to attempt
|
||||
|
||||
#### Scenario: Planner re-plans after unexpected Scene
|
||||
- **WHEN** the Executor reports that the Scene after an action does not match what the current step expected
|
||||
- **THEN** the Planner is invoked again with the updated Scene to produce a revised step (or remaining steps)
|
||||
|
||||
### Requirement: Executor performs retry and wait handling around tool calls
|
||||
The system SHALL provide an Executor that translates a planned step into one or more capability tool calls, and SHALL retry with backoff and/or wait-for-element behavior when a step's expected result is not immediately observed, up to a configured retry ceiling.
|
||||
|
||||
#### Scenario: Executor retries a transient failure
|
||||
- **WHEN** a tool call (e.g. `tap`) does not produce the expected Scene change on the first attempt
|
||||
- **THEN** the Executor retries the step up to a configured number of attempts before treating it as failed
|
||||
|
||||
#### Scenario: Executor gives up after retry ceiling
|
||||
- **WHEN** a step has failed for the configured maximum number of retries
|
||||
- **THEN** the Executor records the step as failed and surfaces this to the Planner/task result instead of retrying forever
|
||||
|
||||
### Requirement: Task context/memory available during a run
|
||||
The system SHALL maintain an in-run context (recent Scene history, executed steps, and their results) accessible to the Planner and Executor for the duration of a task, so re-planning decisions can reference what has already been tried.
|
||||
|
||||
#### Scenario: Re-planning uses prior step history
|
||||
- **WHEN** the Planner is re-invoked mid-task
|
||||
- **THEN** it has access to the steps already attempted in this task and their outcomes, not just the current Scene in isolation
|
||||
@@ -0,0 +1,52 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Device discovery and listing
|
||||
The system SHALL provide a Device Manager that can list all known iPhone devices and their current status (`idle`, `busy`, `offline`, `error`).
|
||||
|
||||
#### Scenario: Listing devices returns current status
|
||||
- **WHEN** a caller requests the device list
|
||||
- **THEN** the system returns each known device's id, status, and driver connection info (e.g. WDA port) without contacting the physical device for every field
|
||||
|
||||
#### Scenario: No devices connected
|
||||
- **WHEN** a caller requests the device list and no physical devices are reachable
|
||||
- **THEN** the system returns an empty list rather than raising an error
|
||||
|
||||
### Requirement: Device connect and disconnect lifecycle
|
||||
The system SHALL allow a caller to connect to and disconnect from a specific device by id, transitioning its tracked status accordingly.
|
||||
|
||||
#### Scenario: Successful connect
|
||||
- **WHEN** a caller connects to a device id that is currently `idle` and reachable
|
||||
- **THEN** the Device Manager marks the device `busy`, and it becomes usable for capability calls
|
||||
|
||||
#### Scenario: Connect to unreachable device
|
||||
- **WHEN** a caller connects to a device id that cannot be reached (WDA not responding)
|
||||
- **THEN** the Device Manager marks the device `offline` and returns an error to the caller instead of hanging indefinitely
|
||||
|
||||
#### Scenario: Disconnect releases the device
|
||||
- **WHEN** a caller disconnects from a device it previously connected to
|
||||
- **THEN** the Device Manager releases the underlying driver connection and marks the device `idle`
|
||||
|
||||
### Requirement: Driver-independent capability interface
|
||||
The system SHALL define a single `Driver` interface (`connect`, `disconnect`, `screenshot`, `tap`, `swipe`, `input`, `launch`, `terminate`, `tree`, `home`, `lock`, `unlock`) that any concrete driver implementation (e.g. WDA, and in future Android) must satisfy identically, so callers above the driver layer never depend on a specific automation framework.
|
||||
|
||||
#### Scenario: Capability call is dispatched through the interface
|
||||
- **WHEN** a higher layer (tools/) invokes a capability such as `tap(x, y)` on a connected device
|
||||
- **THEN** the call is routed through the `Driver` interface to the concrete driver instance for that device, with no framework-specific (e.g. Appium/WDA) types or errors surfacing to the caller
|
||||
|
||||
### Requirement: WDA driver implementation
|
||||
The system SHALL provide a concrete `WDADriver` implementing the `Driver` interface using WebDriverAgent (via Appium Python client), supporting at minimum: screenshot capture, tap, swipe, text input, app launch, app terminate, UI tree retrieval, home button, and device lock/unlock.
|
||||
|
||||
#### Scenario: Screenshot via WDA driver
|
||||
- **WHEN** `screenshot()` is called on a device backed by `WDADriver`
|
||||
- **THEN** the driver returns image bytes/path representing the current physical screen contents
|
||||
|
||||
#### Scenario: Launch app via WDA driver
|
||||
- **WHEN** `launch(bundle_id_or_name)` is called on a device backed by `WDADriver`
|
||||
- **THEN** the driver starts the requested app on the physical device and the call returns once the app process is confirmed running (or raises a clear error if launch fails)
|
||||
|
||||
### Requirement: Stateless driver
|
||||
Driver implementations SHALL NOT persist task-level or business state (e.g. current task id, plan progress); any such state SHALL be owned by the Agent Runtime / Task Memory layers, not the driver.
|
||||
|
||||
#### Scenario: Driver restart does not lose task progress
|
||||
- **WHEN** a driver connection is dropped and re-established mid-task
|
||||
- **THEN** the in-progress task's plan, step history, and timeline remain intact because they were never stored in the driver, only the live device connection needs to be re-established
|
||||
@@ -0,0 +1,30 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: MCP tools expose capability layer without driver leakage
|
||||
The system SHALL expose an MCP server whose tools (at minimum: `take_screenshot`, `tap`, `swipe`, `input_text`, `launch_app`, `find_text`, `find_icon`, `get_ui_tree`, `describe_screen`, `list_devices`, `device_status`) map directly to the capability/perception layer, with tool names, parameters, and return values containing no Appium/WDA/XCUIElement-specific concepts.
|
||||
|
||||
#### Scenario: MCP client calls a tool without framework knowledge
|
||||
- **WHEN** an MCP-compatible LLM client (e.g. Claude Desktop) calls the `tap` tool with coordinates
|
||||
- **THEN** the call succeeds by being routed through the capability layer to the underlying driver, and neither the tool schema nor its response exposes Appium/WDA-specific types or errors
|
||||
|
||||
#### Scenario: Tool list is stable across driver changes
|
||||
- **WHEN** the underlying driver for a device changes (e.g. a future non-WDA driver is used) while the MCP tool set is unchanged
|
||||
- **THEN** existing MCP tool calls continue to work without any change to tool names or parameter schemas
|
||||
|
||||
### Requirement: MCP tool errors are semantic, not framework-specific
|
||||
The system SHALL translate driver/framework-level errors (e.g. WDA connection errors, element-not-found) into clear, semantic MCP tool error responses (e.g. "device offline", "element not found") rather than passing raw framework exceptions through to the LLM.
|
||||
|
||||
#### Scenario: Device offline surfaces a clear error
|
||||
- **WHEN** an MCP tool call targets a device that is currently offline
|
||||
- **THEN** the tool call returns a semantic error indicating the device is unavailable, not a raw connection-refused/stack-trace style error
|
||||
|
||||
### Requirement: REST API mirrors the same capability functions
|
||||
The system SHALL provide a REST API (`GET /devices`, `POST /devices/{id}/tap`, `POST /devices/{id}/screenshot`, `POST /devices/{id}/launch`, `POST /agent/task`, `GET /task/{id}`) that calls the same underlying capability functions as the MCP tools, for manual testing and dashboard use.
|
||||
|
||||
#### Scenario: REST and MCP produce consistent results
|
||||
- **WHEN** the same capability (e.g. `screenshot`) is invoked once via the REST API and once via the corresponding MCP tool for the same device
|
||||
- **THEN** both calls exercise the same underlying capability function and produce equivalent results
|
||||
|
||||
#### Scenario: Starting a task via REST
|
||||
- **WHEN** a caller POSTs a goal to `/agent/task`
|
||||
- **THEN** the system creates a new task, returns its task id, and the task becomes queryable via `GET /task/{id}`
|
||||
@@ -0,0 +1,37 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Unified Scene generation
|
||||
The system SHALL generate a single `Scene` object for the current screen by fusing a screenshot, the UI accessibility tree, and an OCR pass, so that no caller above the perception layer ever needs to read raw UI-tree XML or raw OCR output directly.
|
||||
|
||||
#### Scenario: Scene built from a live screen
|
||||
- **WHEN** the perception layer is asked to describe the current screen of a connected device
|
||||
- **THEN** it captures a screenshot, retrieves the UI tree, runs OCR, and returns one `Scene` object containing `screen` (width, height) and a list of `elements`, each with `id`, `type`, `text`, `bounds`, and `confidence`
|
||||
|
||||
#### Scenario: OCR-only element when tree has no match
|
||||
- **WHEN** OCR detects a text region that has no corresponding node in the UI tree
|
||||
- **THEN** the Scene SHALL still include that text as an element (type inferred as `text` or `unknown`), rather than silently dropping it
|
||||
|
||||
### Requirement: Duplicate element reconciliation
|
||||
The system SHALL reconcile elements that are reported by both the UI tree and OCR for the same visual region into a single Scene element, preferring UI-tree-provided bounds/type when both sources overlap significantly.
|
||||
|
||||
#### Scenario: Button text detected by both tree and OCR
|
||||
- **WHEN** a button's label is present both as a UI tree node and as an OCR text box with substantially overlapping bounds
|
||||
- **THEN** the Scene contains one element for that button, using the UI tree's type and bounds, with the OCR text merged in if the tree node lacked a text value
|
||||
|
||||
### Requirement: Semantic lookup helpers over Scene
|
||||
The system SHALL provide `find_text` and `find_icon` helpers that search the current `Scene` for a matching element and return its coordinates, without requiring the caller to parse the Scene JSON manually.
|
||||
|
||||
#### Scenario: Find text present on screen
|
||||
- **WHEN** a caller invokes `find_text("搜索")` while the current Scene contains an element with that text
|
||||
- **THEN** the system returns the tappable coordinates (center point or bounds) of the matching element
|
||||
|
||||
#### Scenario: Find text not present on screen
|
||||
- **WHEN** a caller invokes `find_text(...)` for a string that has no match in the current Scene
|
||||
- **THEN** the system returns a clear "not found" result rather than raising an unhandled exception
|
||||
|
||||
### Requirement: describe_screen returns Scene only
|
||||
The `describe_screen` tool SHALL return the Scene model (or a summarized natural-language rendering of it) and SHALL NOT return raw XCUIElement type names, raw XML, or driver-specific identifiers.
|
||||
|
||||
#### Scenario: describe_screen output is driver-agnostic
|
||||
- **WHEN** `describe_screen()` is called against a device backed by the WDA driver
|
||||
- **THEN** the returned content contains only Scene-model fields (screen size, elements with type/text/bounds) and no WDA/Appium/XCUIElement-specific terms
|
||||
@@ -0,0 +1,30 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Per-step timeline persistence
|
||||
The system SHALL persist a record for every step of a task, containing the screenshot, the Scene JSON, the prompt/goal context, the tool call issued, and its result, written to local storage under a task-specific path.
|
||||
|
||||
#### Scenario: Step record written after each action
|
||||
- **WHEN** the Executor completes a step (successful or failed)
|
||||
- **THEN** the system writes that step's screenshot and a JSON record (Scene, prompt, tool call, result) to the task's timeline directory before proceeding to the next step
|
||||
|
||||
#### Scenario: Timeline survives process restart
|
||||
- **WHEN** the process running a task restarts after a step has already been persisted
|
||||
- **THEN** the previously persisted steps for that task remain readable from disk
|
||||
|
||||
### Requirement: Task metadata storage
|
||||
The system SHALL store task-level metadata (task id, target device id, status, start/end timestamps) in a local database (SQLite for this change), separate from the per-step image/JSON artifacts on the filesystem.
|
||||
|
||||
#### Scenario: Task status queryable during execution
|
||||
- **WHEN** a caller queries a task by id while it is still running
|
||||
- **THEN** the system returns its current status (e.g. `running`) and metadata without needing to scan the filesystem timeline
|
||||
|
||||
#### Scenario: Task status reflects completion
|
||||
- **WHEN** a task finishes (successfully or with failure)
|
||||
- **THEN** its stored metadata status is updated accordingly and its end timestamp is recorded
|
||||
|
||||
### Requirement: Timeline retrievable for future replay
|
||||
The system SHALL store each task's steps in a way that preserves their order and completeness, so that a future replay feature can reconstruct the full sequence of screenshots/Scenes/actions for a task without additional data collection.
|
||||
|
||||
#### Scenario: Steps retrievable in order
|
||||
- **WHEN** a caller requests the full timeline for a completed task
|
||||
- **THEN** the system returns all persisted steps in the order they occurred, each with its screenshot and JSON record
|
||||
Reference in New Issue
Block a user