Remove v3, v4, and v5 directories. Move latest (v5) docs to docs root.
Update docs.json navigation to use flat tabs instead of versioned layout.
Fix all internal links to use unversioned paths.
- Fix send_user_message example to use asyncio.create_task()
- Add description for --agent flag and external agents
- Document stealth and wpm params on input_text
- Fix fd leak in screenshot command (wrap os.write in try/finally)
- Check iOS flag before connecting in long-press command
- Remove dead except ValueError in press (click.Choice already validates)
- Move os import to top of file
Add `droidrun device` subgroup with 9 commands: screenshot, ui, tap,
swipe, long-press, type, press, apps, start. These bypass the LLM agent
and talk directly to the device driver.
Replace the Android-centric `press_key(keycode)` interface with a
platform-agnostic `press_button(name)` abstraction across all drivers
(Android, iOS, Cloud, Stealth, Recording). Each driver declares its own
`supported_buttons` set and maps button names internally.
Remove `DroidRunCLI` auto-routing that implicitly routed unknown args
to the `run` command.
BREAKING CHANGE: `DeviceDriver.press_key(keycode)` is removed in favor
of `DeviceDriver.press_button(button)`. Macro recordings now emit
`button_press` action type instead of `key_press`.
- Allow agents with no config section (use DEFAULT_CONFIG or env vars)
- Validate run() is async, reject sync functions with clear error
- Wrap list_agents() at CLI import to prevent crashes on scan failure
- Simplify migration to only remove example entries
- Add README.md with full contract, AdbDevice API, and setup guide
External agents now receive a raw async_adbutils.AdbDevice instead of
internal DroidRun tools. This makes them fully self-contained — zero
imports from droidrun, just copy-paste a file or folder and it works.
- Remove mai_ui and autoglm agents (depended on internals)
- Pass raw AdbDevice directly, skip Portal/driver/registry setup
- Add --agent CLI flag with dynamic discovery from external/ dir
- Add list_agents() for runtime agent enumeration
- Add config migration v005 to clean up legacy agent entries
- Update config_example.yaml to remove old agent references
BREAKING CHANGE: external agent run() signature changed from
(tools, instruction, config, max_steps) to
(device, instruction, config, max_steps) where device is an
async_adbutils.AdbDevice
Remove three deprecated agents and all associated dead code:
- CodeActAgent (Python code-gen + exec)
- ScripterAgent (off-device Python execution)
- TextManipulatorAgent (text field editing via code-gen)
Only FastAgent (XML tool-calling) remains as the direct-execution
agent. Rename agent/codeact/ to agent/fast_agent/ and
config/prompts/codeact/ to config/prompts/fast_agent/.
Also removes: SimpleCodeExecutor, SafeExecutionConfig,
extract_code_and_thought, code_checker, and all related config
fields (codeact, scripter, text_manipulator LLM profiles).
Adds v004 config migration to strip removed keys and update
prompt paths in existing user configs.
BREAKING CHANGE: `ScripterConfig`, `SafeExecutionConfig`, and
`codeact` config field are removed. The `text_manipulator` and
`scripter` LLM profiles are no longer used. Users with custom
imports of these classes or configs referencing removed fields
will need to update their code.
- Set _RECOVERY_AFTER_ATTEMPT to 5 so recovery fires after ~11s as intended
- Use Optional[List[float]] consistently instead of mixing old/new style hints
- Simplify nested f-string in retry warning log
- Update docstring to reflect actual fallback behavior for short delay lists
Restart Portal's TCP socket server alongside the accessibility service
during mid-retry recovery. This handles the case where the TCP server
crashes mid-run, causing state requests to fail on both TCP and content
provider paths.
Extract retry logic into fetch_state_with_retry() helper with 7 attempts,
increasing backoff (1-10s, ~29s total), and mid-retry accessibility service
restart after ~11s of failures. Fixes intermittent "Portal returned error:
Unknown error" by logging the full Portal response dict.
CloudDriver.__init__ accepts stealth=True which passes
{"stealth": true} via extra_body on tap, swipe, and input_text
calls to devices-api. No stealth logic added — purely passthrough.
- Manager and StatelessManager now emit ScreenshotEvent before
RecordUIStateEvent, matching FastAgent/CodeAct ordering
- DroidAgent finalize step now emits RecordUIStateEvent after the
final screenshot so every screenshot has a paired UI state
WorkflowValidationError: llama-index validates that all consumed events
are produced by some step. ExternalUserMessageEvent is external-only,
so the @step approach fails validation.
Replace with DroidAgent.send_user_message(text) -> QueuedUserMessage.
Directly queues on shared state, no event routing needed. Drain points
in FastAgent and Manager still emit Applied/Dropped stream events.
- QueuedUserMessage dataclass with uuid4 ID and queued_at_step
- Events now carry message IDs for end-to-end tracking
- ExternalUserMessageDroppedEvent for max-steps scenario
- FastAgent complete() with pending messages: reset and continue loop
so LLM sees its own complete() result alongside the user's message
- Manager request_accomplished with pending messages: loop back to
ManagerInputEvent instead of finalizing
- Max steps: drain queue, emit DroppedEvent, finalize as normal
- workflow_completed flag on shared state, guarding queue_user_message
- Fix device-state injection bug: merge external messages into tool
results user message instead of appending separate ChatMessages
Allow callers to inject user messages into a running DroidAgent via
handler.send_event(ExternalUserMessageEvent(message="...")).
Messages are queued in shared state and drained at safe checkpoints:
- FastAgent: after tool results, before next LLM call
- ManagerAgent: merged into synthesized user turn in prepare_context
Adds ExternalUserMessageEvent, QueuedEvent, and AppliedEvent for
observability. Updates FastAgent and Manager system prompts to
instruct the LLM to honor mid-execution user corrections.