droidrun 0.5.0.dev5__tar.gz → 0.5.0.dev7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/PKG-INFO +1 -1
  2. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/codeact/codeact_agent.py +54 -33
  3. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/codeact/tools_agent.py +60 -34
  4. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/droid/droid_agent.py +18 -7
  5. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/droid/state.py +3 -2
  6. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/executor/executor_agent.py +7 -10
  7. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/manager/manager_agent.py +37 -29
  8. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/chat_utils.py +9 -9
  9. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/inference.py +8 -2
  10. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/pyproject.toml +1 -1
  11. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/uv.lock +1 -1
  12. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/.dockerignore +0 -0
  13. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/.github/workflows/black.yml +0 -0
  14. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/.github/workflows/bounty.yml +0 -0
  15. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/.github/workflows/claude-code-review.yml +0 -0
  16. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/.github/workflows/claude.yml +0 -0
  17. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/.github/workflows/docker.yml +0 -0
  18. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/.github/workflows/publish.yml +0 -0
  19. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/.gitignore +0 -0
  20. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/.python-version +0 -0
  21. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/CHANGELOG.md +0 -0
  22. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/CONTRIBUTING.md +0 -0
  23. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/Dockerfile +0 -0
  24. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/LICENSE +0 -0
  25. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/MANIFEST.in +0 -0
  26. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/README.md +0 -0
  27. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/SKILL.md +0 -0
  28. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/ favicon.svg +0 -0
  29. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/custom.css +0 -0
  30. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/docs.json +0 -0
  31. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/favicon-dark.png +0 -0
  32. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/favicon.png +0 -0
  33. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/logo/dark.png +0 -0
  34. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/logo/light.png +0 -0
  35. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/concepts/agent.mdx +0 -0
  36. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/concepts/android-tools.mdx +0 -0
  37. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/concepts/models.mdx +0 -0
  38. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/concepts/portal-app.mdx +0 -0
  39. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/guides/cli.mdx +0 -0
  40. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/guides/gemini.mdx +0 -0
  41. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/guides/ollama.mdx +0 -0
  42. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/guides/openailike.mdx +0 -0
  43. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/guides/overview.mdx +0 -0
  44. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/guides/telemetry.mdx +0 -0
  45. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/images/portal_apk.png +0 -0
  46. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/overview.mdx +0 -0
  47. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v3/quickstart.mdx +0 -0
  48. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/concepts/architecture.mdx +0 -0
  49. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/concepts/events-and-workflows.mdx +0 -0
  50. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/concepts/prompts.mdx +0 -0
  51. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/concepts/scripter-agent.mdx +0 -0
  52. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/concepts/shared-state.mdx +0 -0
  53. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/features/app-cards.mdx +0 -0
  54. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/features/credentials.mdx +0 -0
  55. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/features/custom-tools.mdx +0 -0
  56. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/features/custom-variables.mdx +0 -0
  57. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/features/structured-output.mdx +0 -0
  58. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/features/telemetry.mdx +0 -0
  59. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/features/tracing.mdx +0 -0
  60. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/guides/cli.mdx +0 -0
  61. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/guides/device-setup.mdx +0 -0
  62. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/guides/docker.mdx +0 -0
  63. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/guides/migration-v3-to-v4.mdx +0 -0
  64. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/guides/overview.mdx +0 -0
  65. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/overview.mdx +0 -0
  66. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/quickstart.mdx +0 -0
  67. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/sdk/adb-tools.mdx +0 -0
  68. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/sdk/base-tools.mdx +0 -0
  69. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/sdk/configuration.mdx +0 -0
  70. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/sdk/droid-agent.mdx +0 -0
  71. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/sdk/ios-tools.mdx +0 -0
  72. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v4/sdk/reference.mdx +0 -0
  73. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/concepts/architecture.mdx +0 -0
  74. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/concepts/events-and-workflows.mdx +0 -0
  75. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/concepts/prompts.mdx +0 -0
  76. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/concepts/scripter-agent.mdx +0 -0
  77. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/concepts/shared-state.mdx +0 -0
  78. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/features/app-cards.mdx +0 -0
  79. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/features/credentials.mdx +0 -0
  80. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/features/custom-tools.mdx +0 -0
  81. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/features/custom-variables.mdx +0 -0
  82. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/features/structured-output.mdx +0 -0
  83. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/features/telemetry.mdx +0 -0
  84. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/features/tracing.mdx +0 -0
  85. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/guides/cli.mdx +0 -0
  86. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/guides/device-setup.mdx +0 -0
  87. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/guides/docker.mdx +0 -0
  88. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/guides/overview.mdx +0 -0
  89. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/overview.mdx +0 -0
  90. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/quickstart.mdx +0 -0
  91. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/sdk/adb-tools.mdx +0 -0
  92. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/sdk/base-tools.mdx +0 -0
  93. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/sdk/configuration.mdx +0 -0
  94. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/sdk/droid-agent.mdx +0 -0
  95. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/sdk/ios-tools.mdx +0 -0
  96. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/docs/v5/sdk/reference.mdx +0 -0
  97. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/__init__.py +0 -0
  98. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/__main__.py +0 -0
  99. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/__init__.py +0 -0
  100. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/action_context.py +0 -0
  101. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/action_result.py +0 -0
  102. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/codeact/__init__.py +0 -0
  103. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/codeact/events.py +0 -0
  104. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/codeact/xml_parser.py +0 -0
  105. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/common/__init__.py +0 -0
  106. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/common/constants.py +0 -0
  107. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/common/events.py +0 -0
  108. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/droid/__init__.py +0 -0
  109. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/droid/events.py +0 -0
  110. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/executor/__init__.py +0 -0
  111. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/executor/events.py +0 -0
  112. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/executor/prompts.py +0 -0
  113. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/external/__init__.py +0 -0
  114. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/external/autoglm.py +0 -0
  115. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/external/mai_ui.py +0 -0
  116. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/manager/__init__.py +0 -0
  117. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/manager/events.py +0 -0
  118. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/manager/prompts.py +0 -0
  119. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/manager/stateless_manager_agent.py +0 -0
  120. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/oneflows/__init__.py +0 -0
  121. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/oneflows/app_starter_workflow.py +0 -0
  122. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/oneflows/structured_output_agent.py +0 -0
  123. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/oneflows/text_manipulator.py +0 -0
  124. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/scripter/__init__.py +0 -0
  125. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/scripter/events.py +0 -0
  126. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/scripter/scripter_agent.py +0 -0
  127. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/tool_registry.py +0 -0
  128. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/trajectory/__init__.py +0 -0
  129. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/trajectory/writer.py +0 -0
  130. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/usage.py +0 -0
  131. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/__init__.py +0 -0
  132. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/actions.py +0 -0
  133. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/code_checker.py +0 -0
  134. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/executer.py +0 -0
  135. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/llm_loader.py +0 -0
  136. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/llm_picker.py +0 -0
  137. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/prompt_resolver.py +0 -0
  138. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/signatures.py +0 -0
  139. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/tracing_setup.py +0 -0
  140. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/agent/utils/trajectory.py +0 -0
  141. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/app_cards/__init__.py +0 -0
  142. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/app_cards/app_card_provider.py +0 -0
  143. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/app_cards/providers/__init__.py +0 -0
  144. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/app_cards/providers/composite_provider.py +0 -0
  145. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/app_cards/providers/local_provider.py +0 -0
  146. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/app_cards/providers/server_provider.py +0 -0
  147. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/__init__.py +0 -0
  148. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/doctor.py +0 -0
  149. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/event_handler.py +0 -0
  150. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/logs.py +0 -0
  151. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/main.py +0 -0
  152. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/__init__.py +0 -0
  153. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/app.py +0 -0
  154. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/commands.py +0 -0
  155. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/css/advanced_tab.tcss +0 -0
  156. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/css/app.tcss +0 -0
  157. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/css/models_tab.tcss +0 -0
  158. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/css/settings_screen.tcss +0 -0
  159. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/settings/__init__.py +0 -0
  160. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/settings/advanced_tab.py +0 -0
  161. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/settings/agent_tab.py +0 -0
  162. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/settings/data.py +0 -0
  163. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/settings/models_tab.py +0 -0
  164. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/settings/section.py +0 -0
  165. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/settings/settings_screen.py +0 -0
  166. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/widgets/__init__.py +0 -0
  167. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/widgets/command_dropdown.py +0 -0
  168. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/widgets/device_picker.py +0 -0
  169. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/widgets/input_bar.py +0 -0
  170. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/widgets/log_view.py +0 -0
  171. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/cli/tui/widgets/status_bar.py +0 -0
  172. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/app_cards/README.md +0 -0
  173. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/app_cards/app_cards.json +0 -0
  174. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/app_cards/gmail.md +0 -0
  175. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/credentials_example.yaml +0 -0
  176. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/prompts/codeact/system.jinja2 +0 -0
  177. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/prompts/codeact/tools_system.jinja2 +0 -0
  178. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/prompts/codeact/tools_user.jinja2 +0 -0
  179. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/prompts/codeact/user.jinja2 +0 -0
  180. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/prompts/executor/rev1.jinja2 +0 -0
  181. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/prompts/executor/system.jinja2 +0 -0
  182. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/prompts/manager/rev1.jinja2 +0 -0
  183. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/prompts/manager/stateless.jinja2 +0 -0
  184. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/prompts/manager/system.jinja2 +0 -0
  185. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/prompts/manager/trained.jinja2 +0 -0
  186. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config/prompts/scripter/system.jinja2 +0 -0
  187. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config_example.yaml +0 -0
  188. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config_manager/__init__.py +0 -0
  189. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config_manager/config_manager.py +0 -0
  190. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config_manager/env_keys.py +0 -0
  191. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config_manager/loader.py +0 -0
  192. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config_manager/migrations/__init__.py +0 -0
  193. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config_manager/migrations/v002_add_code_exec.py +0 -0
  194. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config_manager/path_resolver.py +0 -0
  195. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config_manager/prompt_loader.py +0 -0
  196. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/config_manager/safe_execution.py +0 -0
  197. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/credential_manager/__init__.py +0 -0
  198. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/credential_manager/credential_manager.py +0 -0
  199. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/credential_manager/file_credential_manager.py +0 -0
  200. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/log_handlers.py +0 -0
  201. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/macro/__init__.py +0 -0
  202. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/macro/__main__.py +0 -0
  203. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/macro/cli.py +0 -0
  204. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/macro/replay.py +0 -0
  205. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/mcp/__init__.py +0 -0
  206. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/mcp/adapter.py +0 -0
  207. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/mcp/client.py +0 -0
  208. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/mcp/config.py +0 -0
  209. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/portal.py +0 -0
  210. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/telemetry/__init__.py +0 -0
  211. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/telemetry/events.py +0 -0
  212. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/telemetry/langfuse_processor.py +0 -0
  213. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/telemetry/phoenix.py +0 -0
  214. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/telemetry/tracker.py +0 -0
  215. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/__init__.py +0 -0
  216. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/android/__init__.py +0 -0
  217. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/android/portal_client.py +0 -0
  218. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/driver/__init__.py +0 -0
  219. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/driver/android.py +0 -0
  220. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/driver/base.py +0 -0
  221. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/driver/cloud.py +0 -0
  222. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/driver/ios.py +0 -0
  223. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/driver/recording.py +0 -0
  224. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/driver/stealth.py +0 -0
  225. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/filters/__init__.py +0 -0
  226. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/filters/base.py +0 -0
  227. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/filters/concise_filter.py +0 -0
  228. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/filters/detailed_filter.py +0 -0
  229. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/formatters/__init__.py +0 -0
  230. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/formatters/base.py +0 -0
  231. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/formatters/indexed_formatter.py +0 -0
  232. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/helpers/__init__.py +0 -0
  233. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/helpers/coordinate.py +0 -0
  234. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/helpers/element_search.py +0 -0
  235. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/helpers/geometry.py +0 -0
  236. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/ios/__init__.py +0 -0
  237. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/ui/__init__.py +0 -0
  238. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/ui/ios_provider.py +0 -0
  239. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/ui/provider.py +0 -0
  240. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/ui/state.py +0 -0
  241. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/droidrun/tools/ui/stealth_state.py +0 -0
  242. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/gen-docs-sdk-ref.sh +0 -0
  243. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/setup.py +0 -0
  244. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/static/droidrun-dark.png +0 -0
  245. {droidrun-0.5.0.dev5 → droidrun-0.5.0.dev7}/static/droidrun.png +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: droidrun
3
- Version: 0.5.0.dev5
3
+ Version: 0.5.0.dev7
4
4
  Summary: A framework for controlling Android devices through LLM agents
5
5
  Project-URL: Homepage, https://github.com/droidrun/droidrun
6
6
  Project-URL: Bug Tracker, https://github.com/droidrun/droidrun/issues
@@ -1,8 +1,10 @@
1
1
  import asyncio
2
+ import copy
2
3
  import inspect
3
4
  import logging
4
5
  from typing import TYPE_CHECKING, Optional, Type
5
6
 
7
+ from llama_index.core.base.llms.types import ChatMessage, ImageBlock, TextBlock
6
8
  from llama_index.core.llms.llm import LLM
7
9
  from llama_index.core.workflow import Context, StartEvent, StopEvent, Workflow, step
8
10
  from opentelemetry import trace
@@ -21,7 +23,6 @@ from droidrun.agent.usage import get_usage_from_response
21
23
  from droidrun.agent.utils.chat_utils import (
22
24
  extract_code_and_thought,
23
25
  limit_history,
24
- to_chat_messages,
25
26
  )
26
27
  from droidrun.agent.utils.executer import ExecuterState, SimpleCodeExecutor
27
28
  from droidrun.agent.utils.inference import acall_with_retries
@@ -52,7 +53,7 @@ class CodeActAgent(Workflow):
52
53
  Agent that generates and executes Python code using atomic actions.
53
54
 
54
55
  Uses ReAct cycle: Thought -> Code -> Observation -> repeat until complete().
55
- Messages stored as list[dict], converted to ChatMessage only for LLM calls.
56
+ Messages stored as list[ChatMessage] to preserve thinking tokens across turns.
56
57
  """
57
58
 
58
59
  def __init__(
@@ -90,7 +91,7 @@ class CodeActAgent(Workflow):
90
91
  self.prompt_resolver = prompt_resolver or PromptResolver()
91
92
  self.tracing_config = tracing_config
92
93
 
93
- self.system_prompt: dict | None = None
94
+ self.system_prompt: ChatMessage | None = None
94
95
  self.code_exec_counter = 0
95
96
  self.remembered_info: list[str] | None = None
96
97
 
@@ -150,7 +151,7 @@ class CodeActAgent(Workflow):
150
151
 
151
152
  logger.debug("CodeActAgent initialized.")
152
153
 
153
- async def _build_system_prompt(self) -> dict:
154
+ async def _build_system_prompt(self) -> ChatMessage:
154
155
  """Build system prompt message."""
155
156
  # Build template context with available tools for conditional examples
156
157
  template_context = {
@@ -178,9 +179,9 @@ class CodeActAgent(Workflow):
178
179
  str(PathResolver.resolve(prompt_path, must_exist=True)),
179
180
  template_context,
180
181
  )
181
- return {"role": "system", "content": [{"text": system_text}]}
182
+ return ChatMessage(role="system", content=system_text)
182
183
 
183
- async def _build_user_prompt(self, goal: str) -> dict:
184
+ async def _build_user_prompt(self, goal: str) -> ChatMessage:
184
185
  """Build initial user prompt message."""
185
186
  custom_user_prompt = self.prompt_resolver.get_prompt("fast_agent_user")
186
187
  if custom_user_prompt:
@@ -207,7 +208,7 @@ class CodeActAgent(Workflow):
207
208
  ),
208
209
  },
209
210
  )
210
- return {"role": "user", "content": [{"text": user_text}]}
211
+ return ChatMessage(role="user", content=user_text)
211
212
 
212
213
  @step
213
214
  async def prepare_chat(self, ctx: Context, ev: StartEvent) -> CodeActInputEvent:
@@ -240,8 +241,8 @@ class CodeActAgent(Workflow):
240
241
  for idx, item in enumerate(remembered_info, 1):
241
242
  memory_text += f"{idx}. {item}\n"
242
243
  # Append to first user message
243
- self.shared_state.message_history[0]["content"].append(
244
- {"text": memory_text}
244
+ self.shared_state.message_history[0].blocks.append(
245
+ TextBlock(text=memory_text)
245
246
  )
246
247
 
247
248
  return CodeActInputEvent()
@@ -296,7 +297,10 @@ class CodeActAgent(Workflow):
296
297
  ui_state = await self.state_provider.get_state()
297
298
  self.action_ctx.ui = ui_state
298
299
 
299
- # Update shared state
300
+ # Update shared state (previous ← current, current ← new)
301
+ self.shared_state.previous_formatted_device_state = (
302
+ self.shared_state.formatted_device_state
303
+ )
300
304
  self.shared_state.formatted_device_state = ui_state.formatted_text
301
305
  self.shared_state.focused_text = ui_state.focused_text
302
306
  self.shared_state.a11y_tree = ui_state.elements
@@ -311,11 +315,6 @@ class CodeActAgent(Workflow):
311
315
  # Stream formatted state for trajectory
312
316
  ctx.write_event_to_stream(RecordUIStateEvent(ui_state=ui_state.elements))
313
317
 
314
- # Add device state to last user message
315
- self.shared_state.message_history[-1]["content"].append(
316
- {"text": f"\n{ui_state.formatted_text}\n"}
317
- )
318
-
319
318
  except DeviceDisconnectedError:
320
319
  raise
321
320
  except Exception as e:
@@ -323,27 +322,51 @@ class CodeActAgent(Workflow):
323
322
  if self.debug:
324
323
  logger.error("State retrieval error details:", exc_info=True)
325
324
 
326
- # Add screenshot to message if vision enabled
327
- if self.vision and screenshot:
328
- self.shared_state.message_history[-1]["content"].append(
329
- {"image": screenshot}
330
- )
331
-
332
- # Limit history and prepare for LLM
325
+ # Limit history and build ephemeral copy for LLM
333
326
  limited_history = limit_history(
334
327
  self.shared_state.message_history,
335
328
  LLM_HISTORY_LIMIT * 2,
336
329
  preserve_first=True,
337
330
  )
331
+ messages_to_send = [self.system_prompt] + copy.deepcopy(limited_history)
332
+
333
+ # Inject device state and screenshot into the copy (not the original)
334
+ user_indices = [
335
+ i for i, msg in enumerate(messages_to_send) if msg.role == "user"
336
+ ]
337
+ if user_indices:
338
+ last_user_idx = user_indices[-1]
339
+
340
+ # Current device state → last user message
341
+ current_state = self.shared_state.formatted_device_state.strip()
342
+ if current_state:
343
+ messages_to_send[last_user_idx].blocks.append(
344
+ TextBlock(
345
+ text=f"\n<device_state>\n{current_state}\n</device_state>\n"
346
+ )
347
+ )
348
+
349
+ # Screenshot → last user message
350
+ if self.vision and screenshot:
351
+ messages_to_send[last_user_idx].blocks.append(
352
+ ImageBlock(image=screenshot)
353
+ )
338
354
 
339
- # Build final messages: system + history
340
- messages_to_send = [self.system_prompt] + limited_history
341
- chat_messages = to_chat_messages(messages_to_send)
355
+ # Previous device state second-to-last user message
356
+ if len(user_indices) >= 2:
357
+ second_last_idx = user_indices[-2]
358
+ prev_state = self.shared_state.previous_formatted_device_state.strip()
359
+ if prev_state:
360
+ messages_to_send[second_last_idx].blocks.append(
361
+ TextBlock(
362
+ text=f"\n<previous_device_state>\n{prev_state}\n</previous_device_state>\n"
363
+ )
364
+ )
342
365
 
343
366
  # Call LLM
344
367
  logger.info("CodeAct response:", extra={"color": "yellow"})
345
368
  response = await acall_with_retries(
346
- self.llm, chat_messages, stream=self.agent_config.streaming
369
+ self.llm, messages_to_send, stream=self.agent_config.streaming
347
370
  )
348
371
 
349
372
  if response is None:
@@ -360,11 +383,9 @@ class CodeActAgent(Workflow):
360
383
  except Exception as e:
361
384
  logger.warning(f"Could not get usage: {e}")
362
385
 
363
- # Store assistant response
386
+ # Store assistant response (preserves ThinkingBlock, additional_kwargs, etc.)
387
+ self.shared_state.message_history.append(response.message)
364
388
  response_text = response.message.content
365
- self.shared_state.message_history.append(
366
- {"role": "assistant", "content": [{"text": response_text}]}
367
- )
368
389
 
369
390
  # Extract thought and code
370
391
  code, thought = extract_code_and_thought(response_text)
@@ -391,7 +412,7 @@ class CodeActAgent(Workflow):
391
412
  "Now, describe the next step you will take to address the original goal."
392
413
  )
393
414
  self.shared_state.message_history.append(
394
- {"role": "user", "content": [{"text": no_thoughts_text}]}
415
+ ChatMessage(role="user", content=no_thoughts_text)
395
416
  )
396
417
  else:
397
418
  logger.debug(f"Reasoning: {ev.thought}")
@@ -408,7 +429,7 @@ class CodeActAgent(Workflow):
408
429
  "function within a <python></python> code block."
409
430
  )
410
431
  self.shared_state.message_history.append(
411
- {"role": "user", "content": [{"text": no_code_text}]}
432
+ ChatMessage(role="user", content=no_code_text)
412
433
  )
413
434
  return CodeActInputEvent()
414
435
 
@@ -482,7 +503,7 @@ class CodeActAgent(Workflow):
482
503
  # Add execution output as user message
483
504
  observation_text = f"Execution Result:\n<result>\n{output}\n</result>"
484
505
  self.shared_state.message_history.append(
485
- {"role": "user", "content": [{"text": observation_text}]}
506
+ ChatMessage(role="user", content=observation_text)
486
507
  )
487
508
 
488
509
  return CodeActInputEvent()
@@ -9,9 +9,12 @@ compatibility with DroidAgent's execute_task() method.
9
9
  """
10
10
 
11
11
  import asyncio
12
+ import copy
12
13
  import logging
14
+ import os
13
15
  from typing import TYPE_CHECKING, Optional, Type
14
16
 
17
+ from llama_index.core.base.llms.types import ChatMessage, ImageBlock, TextBlock
15
18
  from llama_index.core.llms.llm import LLM
16
19
  from llama_index.core.workflow import Context, StartEvent, StopEvent, Workflow, step
17
20
  from opentelemetry import trace
@@ -35,7 +38,7 @@ from droidrun.agent.codeact.xml_parser import (
35
38
  from droidrun.agent.common.constants import LLM_HISTORY_LIMIT
36
39
  from droidrun.agent.common.events import RecordUIStateEvent, ScreenshotEvent
37
40
  from droidrun.agent.usage import get_usage_from_response
38
- from droidrun.agent.utils.chat_utils import limit_history, to_chat_messages
41
+ from droidrun.agent.utils.chat_utils import limit_history
39
42
  from droidrun.agent.utils.inference import acall_with_retries
40
43
  from droidrun.agent.utils.prompt_resolver import PromptResolver
41
44
  from droidrun.agent.utils.tracing_setup import record_langfuse_screenshot
@@ -56,7 +59,7 @@ class FastAgent(Workflow):
56
59
  """Agent that uses XML tool-calling instead of code generation.
57
60
 
58
61
  Uses ReAct cycle: Thought -> Tool Call -> Observation -> repeat until complete().
59
- Messages stored as list[dict], converted to ChatMessage only for LLM calls.
62
+ Messages stored as list[ChatMessage] to preserve thinking tokens across turns.
60
63
  """
61
64
 
62
65
  def __init__(
@@ -88,12 +91,15 @@ class FastAgent(Workflow):
88
91
  self.action_ctx = action_ctx
89
92
  self.state_provider = state_provider
90
93
  self.save_trajectory = save_trajectory
94
+ self._stream_screenshots = os.environ.get(
95
+ "DROIDRUN_STREAM_SCREENSHOTS", ""
96
+ ).lower() in ("1", "true")
91
97
  self.shared_state = shared_state
92
98
  self.output_model = output_model
93
99
  self.prompt_resolver = prompt_resolver or PromptResolver()
94
100
  self.tracing_config = tracing_config
95
101
 
96
- self.system_prompt: dict | None = None
102
+ self.system_prompt: ChatMessage | None = None
97
103
  self.tool_call_counter = 0
98
104
  self.remembered_info: list[str] | None = None
99
105
 
@@ -108,7 +114,7 @@ class FastAgent(Workflow):
108
114
 
109
115
  logger.debug("FastAgent initialized.")
110
116
 
111
- async def _build_system_prompt(self) -> dict:
117
+ async def _build_system_prompt(self) -> ChatMessage:
112
118
  """Build system prompt message."""
113
119
  template_context = {
114
120
  "tool_descriptions": self.tool_descriptions,
@@ -133,9 +139,9 @@ class FastAgent(Workflow):
133
139
  self.agent_config.get_fast_agent_system_prompt_path(),
134
140
  template_context,
135
141
  )
136
- return {"role": "system", "content": [{"text": system_text}]}
142
+ return ChatMessage(role="system", content=system_text)
137
143
 
138
- async def _build_user_prompt(self, goal: str) -> dict:
144
+ async def _build_user_prompt(self, goal: str) -> ChatMessage:
139
145
  """Build initial user prompt message."""
140
146
  custom_user_prompt = self.prompt_resolver.get_prompt("fast_agent_user")
141
147
  if custom_user_prompt:
@@ -158,7 +164,7 @@ class FastAgent(Workflow):
158
164
  ),
159
165
  },
160
166
  )
161
- return {"role": "user", "content": [{"text": user_text}]}
167
+ return ChatMessage(role="user", content=user_text)
162
168
 
163
169
  @step
164
170
  async def prepare_chat(self, ctx: Context, ev: StartEvent) -> FastAgentInputEvent:
@@ -190,8 +196,8 @@ class FastAgent(Workflow):
190
196
  memory_text = "\n### Remembered Information:\n"
191
197
  for idx, item in enumerate(remembered_info, 1):
192
198
  memory_text += f"{idx}. {item}\n"
193
- self.shared_state.message_history[0]["content"].append(
194
- {"text": memory_text}
199
+ self.shared_state.message_history[0].blocks.append(
200
+ TextBlock(text=memory_text)
195
201
  )
196
202
 
197
203
  return FastAgentInputEvent()
@@ -218,7 +224,7 @@ class FastAgent(Workflow):
218
224
 
219
225
  # Capture screenshot if needed
220
226
  screenshot = None
221
- if self.vision or self.save_trajectory != "none":
227
+ if self.vision or self._stream_screenshots or self.save_trajectory != "none":
222
228
  try:
223
229
  screenshot = await self.action_ctx.driver.screenshot()
224
230
 
@@ -246,7 +252,10 @@ class FastAgent(Workflow):
246
252
  ui_state = await self.state_provider.get_state()
247
253
  self.action_ctx.ui = ui_state
248
254
 
249
- # Update shared state
255
+ # Update shared state (previous ← current, current ← new)
256
+ self.shared_state.previous_formatted_device_state = (
257
+ self.shared_state.formatted_device_state
258
+ )
250
259
  self.shared_state.formatted_device_state = ui_state.formatted_text
251
260
  self.shared_state.focused_text = ui_state.focused_text
252
261
  self.shared_state.a11y_tree = ui_state.elements
@@ -261,11 +270,6 @@ class FastAgent(Workflow):
261
270
  # Stream formatted state for trajectory
262
271
  ctx.write_event_to_stream(RecordUIStateEvent(ui_state=ui_state.elements))
263
272
 
264
- # Add device state to last user message
265
- self.shared_state.message_history[-1]["content"].append(
266
- {"text": f"\n{ui_state.formatted_text}\n"}
267
- )
268
-
269
273
  except DeviceDisconnectedError:
270
274
  raise
271
275
  except Exception as e:
@@ -273,27 +277,51 @@ class FastAgent(Workflow):
273
277
  if self.debug:
274
278
  logger.error("State retrieval error details:", exc_info=True)
275
279
 
276
- # Add screenshot to message if vision enabled
277
- if self.vision and screenshot:
278
- self.shared_state.message_history[-1]["content"].append(
279
- {"image": screenshot}
280
- )
281
-
282
- # Limit history and prepare for LLM
280
+ # Limit history and build ephemeral copy for LLM
283
281
  limited_history = limit_history(
284
282
  self.shared_state.message_history,
285
283
  LLM_HISTORY_LIMIT * 2,
286
284
  preserve_first=True,
287
285
  )
286
+ messages_to_send = [self.system_prompt] + copy.deepcopy(limited_history)
287
+
288
+ # Inject device state and screenshot into the copy (not the original)
289
+ user_indices = [
290
+ i for i, msg in enumerate(messages_to_send) if msg.role == "user"
291
+ ]
292
+ if user_indices:
293
+ last_user_idx = user_indices[-1]
294
+
295
+ # Current device state → last user message
296
+ current_state = self.shared_state.formatted_device_state.strip()
297
+ if current_state:
298
+ messages_to_send[last_user_idx].blocks.append(
299
+ TextBlock(
300
+ text=f"\n<device_state>\n{current_state}\n</device_state>\n"
301
+ )
302
+ )
303
+
304
+ # Screenshot → last user message
305
+ if self.vision and screenshot:
306
+ messages_to_send[last_user_idx].blocks.append(
307
+ ImageBlock(image=screenshot)
308
+ )
288
309
 
289
- # Build final messages: system + history
290
- messages_to_send = [self.system_prompt] + limited_history
291
- chat_messages = to_chat_messages(messages_to_send)
310
+ # Previous device state second-to-last user message
311
+ if len(user_indices) >= 2:
312
+ second_last_idx = user_indices[-2]
313
+ prev_state = self.shared_state.previous_formatted_device_state.strip()
314
+ if prev_state:
315
+ messages_to_send[second_last_idx].blocks.append(
316
+ TextBlock(
317
+ text=f"\n<previous_device_state>\n{prev_state}\n</previous_device_state>\n"
318
+ )
319
+ )
292
320
 
293
321
  # Call LLM
294
322
  logger.info("FastAgent response:", extra={"color": "yellow"})
295
323
  response = await acall_with_retries(
296
- self.llm, chat_messages, stream=self.agent_config.streaming
324
+ self.llm, messages_to_send, stream=self.agent_config.streaming
297
325
  )
298
326
 
299
327
  if response is None:
@@ -310,11 +338,9 @@ class FastAgent(Workflow):
310
338
  except Exception as e:
311
339
  logger.warning(f"Could not get usage: {e}")
312
340
 
313
- # Store assistant response
341
+ # Store assistant response (preserves ThinkingBlock, additional_kwargs, etc.)
342
+ self.shared_state.message_history.append(response.message)
314
343
  response_text = response.message.content
315
- self.shared_state.message_history.append(
316
- {"role": "assistant", "content": [{"text": response_text}]}
317
- )
318
344
 
319
345
  # Parse tool calls from response
320
346
  thought, tool_calls = parse_tool_calls(response_text, self.param_types)
@@ -360,7 +386,7 @@ class FastAgent(Workflow):
360
386
  "Now, describe the next step you will take to address the original goal."
361
387
  )
362
388
  self.shared_state.message_history.append(
363
- {"role": "user", "content": [{"text": no_thoughts_text}]}
389
+ ChatMessage(role="user", content=no_thoughts_text)
364
390
  )
365
391
  else:
366
392
  logger.debug(f"Reasoning: {ev.thought}")
@@ -382,7 +408,7 @@ class FastAgent(Workflow):
382
408
  "</function_calls>"
383
409
  )
384
410
  self.shared_state.message_history.append(
385
- {"role": "user", "content": [{"text": no_tools_text}]}
411
+ ChatMessage(role="user", content=no_tools_text)
386
412
  )
387
413
  return FastAgentInputEvent()
388
414
 
@@ -469,7 +495,7 @@ class FastAgent(Workflow):
469
495
 
470
496
  # Add results as user message
471
497
  self.shared_state.message_history.append(
472
- {"role": "user", "content": [{"text": output}]}
498
+ ChatMessage(role="user", content=output)
473
499
  )
474
500
 
475
501
  return FastAgentInputEvent()
@@ -8,6 +8,7 @@ Architecture:
8
8
  """
9
9
 
10
10
  import logging
11
+ import os
11
12
  import traceback
12
13
  from typing import TYPE_CHECKING, Awaitable, Type, Union
13
14
 
@@ -208,6 +209,10 @@ class DroidAgent(Workflow):
208
209
  # Check if using external agent - skip LLM loading
209
210
  self._using_external_agent = self.config.agent.name != "droidrun"
210
211
 
212
+ self._stream_screenshots = os.environ.get(
213
+ "DROIDRUN_STREAM_SCREENSHOTS", ""
214
+ ).lower() in ("1", "true")
215
+
211
216
  self.timeout = timeout
212
217
 
213
218
  # Store user custom tools
@@ -1039,17 +1044,21 @@ class DroidAgent(Workflow):
1039
1044
  if self.config.logging.debug:
1040
1045
  logger.error(traceback.format_exc())
1041
1046
 
1042
- # Capture final screenshot before saving trajectory
1043
- if self.config.logging.save_trajectory != "none":
1047
+ # Capture final screenshot (independent of trajectory persistence)
1048
+ vision_any = (
1049
+ self.config.agent.manager.vision
1050
+ or self.config.agent.executor.vision
1051
+ or self.config.agent.fast_agent.vision
1052
+ )
1053
+ if (
1054
+ vision_any
1055
+ or self._stream_screenshots
1056
+ or self.config.logging.save_trajectory != "none"
1057
+ ):
1044
1058
  try:
1045
1059
  screenshot = await self.action_ctx.driver.screenshot()
1046
1060
  if screenshot:
1047
1061
  ctx.write_event_to_stream(ScreenshotEvent(screenshot=screenshot))
1048
- vision_any = (
1049
- self.config.agent.manager.vision
1050
- or self.config.agent.executor.vision
1051
- or self.config.agent.fast_agent.vision
1052
- )
1053
1062
  parent_span = trace.get_current_span()
1054
1063
  record_langfuse_screenshot(
1055
1064
  screenshot,
@@ -1061,6 +1070,8 @@ class DroidAgent(Workflow):
1061
1070
  except Exception as e:
1062
1071
  logger.warning(f"Failed to capture final screenshot: {e}")
1063
1072
 
1073
+ # Save trajectory to disk
1074
+ if self.config.logging.save_trajectory != "none":
1064
1075
  # Populate macro data from RecordingDriver log
1065
1076
  if isinstance(self.driver, RecordingDriver):
1066
1077
  self.trajectory.macro = list(self.driver.log)
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Dict, List, Optional
4
4
 
5
+ from llama_index.core.base.llms.types import ChatMessage
5
6
  from pydantic import BaseModel, ConfigDict, Field
6
7
 
7
8
  from droidrun.telemetry import PackageVisitEvent, capture
@@ -86,9 +87,9 @@ class DroidAgentState(BaseModel):
86
87
  success: Optional[bool] = None
87
88
 
88
89
  # ========================================================================
89
- # Message History (for stateful agents - list of dicts)
90
+ # Message History (for stateful agents - preserves ChatMessage blocks)
90
91
  # ========================================================================
91
- message_history: List[Dict] = Field(default_factory=list)
92
+ message_history: List[ChatMessage] = Field(default_factory=list)
92
93
 
93
94
  # ========================================================================
94
95
  # Error Handling
@@ -14,18 +14,18 @@ import json
14
14
  import logging
15
15
  from typing import TYPE_CHECKING, Optional
16
16
 
17
+ from llama_index.core.base.llms.types import ChatMessage, ImageBlock, TextBlock
17
18
  from llama_index.core.llms.llm import LLM
18
19
  from llama_index.core.workflow import Context, StartEvent, StopEvent, Workflow, step
19
20
 
20
21
  from droidrun.agent.executor.events import (
21
22
  ExecutorActionEvent,
23
+ ExecutorActionResultEvent,
22
24
  ExecutorContextEvent,
23
25
  ExecutorResponseEvent,
24
- ExecutorActionResultEvent,
25
26
  )
26
27
  from droidrun.agent.executor.prompts import parse_executor_response
27
28
  from droidrun.agent.usage import get_usage_from_response
28
- from droidrun.agent.utils.chat_utils import to_chat_messages
29
29
  from droidrun.agent.utils.inference import acall_with_retries
30
30
  from droidrun.agent.utils.prompt_resolver import PromptResolver
31
31
  from droidrun.config_manager.config_manager import AgentConfig
@@ -44,7 +44,7 @@ class ExecutorAgent(Workflow):
44
44
  Action execution agent that performs specific actions.
45
45
 
46
46
  Single-turn agent: receives subgoal, selects action, executes it.
47
- Uses dict messages, converts to ChatMessage at LLM call time.
47
+ Uses ChatMessage objects directly for LLM calls.
48
48
  """
49
49
 
50
50
  # Flow-control tools hidden from executor's LLM prompt
@@ -123,14 +123,14 @@ class ExecutorAgent(Workflow):
123
123
  variables,
124
124
  )
125
125
 
126
- # Build message as dict
127
- messages = [{"role": "user", "content": [{"text": prompt_text}]}]
126
+ # Build message
127
+ messages = [ChatMessage(role="user", blocks=[TextBlock(text=prompt_text)])]
128
128
 
129
129
  # Add screenshot if vision enabled
130
130
  if self.vision:
131
131
  screenshot = self.shared_state.screenshot
132
132
  if screenshot is not None:
133
- messages[0]["content"].append({"image": screenshot})
133
+ messages[0].blocks.append(ImageBlock(image=screenshot))
134
134
  logger.debug("📸 Using screenshot for Executor")
135
135
  else:
136
136
  logger.warning("⚠️ Vision enabled but no screenshot available")
@@ -149,13 +149,10 @@ class ExecutorAgent(Workflow):
149
149
  # Get messages from context
150
150
  messages = await ctx.store.get("executor_messages")
151
151
 
152
- # Convert to ChatMessage and call LLM
153
- chat_messages = to_chat_messages(messages)
154
-
155
152
  try:
156
153
  logger.info("Executor response:", extra={"color": "green"})
157
154
  response = await acall_with_retries(
158
- self.llm, chat_messages, stream=self.agent_config.streaming
155
+ self.llm, messages, stream=self.agent_config.streaming
159
156
  )
160
157
  response_text = str(response)
161
158
  except ValueError as e: