droidrun 0.3.10.dev8__tar.gz → 0.3.10.dev10__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.
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/PKG-INFO +1 -1
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/codeact/codeact_agent.py +14 -15
- droidrun-0.3.10.dev10/droidrun/agent/common/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/droid/droid_agent.py +31 -73
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/droid/events.py +52 -10
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/executor/executor_agent.py +3 -5
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/manager/manager_agent.py +31 -10
- droidrun-0.3.10.dev10/droidrun/agent/oneflows/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/utils/executer.py +16 -10
- droidrun-0.3.10.dev10/droidrun/app_cards/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/cli/main.py +2 -2
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10/droidrun}/config/app_cards/README.md +5 -3
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10/droidrun}/config/prompts/codeact/system.jinja2 +0 -1
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/config_manager/config_manager.py +2 -2
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/config_manager/path_resolver.py +14 -14
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/macro/cli.py +2 -2
- droidrun-0.3.10.dev10/droidrun/telemetry/__init__.py +15 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/telemetry/events.py +14 -5
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/telemetry/tracker.py +36 -8
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/tools/adb.py +2 -2
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/pyproject.toml +1 -1
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/uv.lock +1 -1
- droidrun-0.3.10.dev8/droidrun/telemetry/__init__.py +0 -4
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/.github/workflows/bounty.yml +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/.github/workflows/publish.yml +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/.gitignore +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/.python-version +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/CHANGELOG.md +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/CONTRIBUTING.md +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/LICENSE +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/MANIFEST.in +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/README.md +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/.generated-files.txt +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/docs.json +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/favicon.png +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/logo/dark.svg +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/logo/light.svg +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v1/concepts/agent.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v1/concepts/android-control.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v1/concepts/portal-app.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v1/overview.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v1/quickstart.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v2/concepts/agent.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v2/concepts/android-control.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v2/concepts/planning.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v2/concepts/portal-app.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v2/concepts/tracing.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v2/overview.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v2/quickstart.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/concepts/agent.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/concepts/android-tools.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/concepts/models.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/concepts/portal-app.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/guides/cli.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/guides/gemini.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/guides/ollama.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/guides/openailike.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/guides/overview.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/guides/telemetry.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/images/portal_apk.png +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/overview.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/quickstart.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/sdk/adb-tools.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/sdk/base-tools.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/sdk/droid-agent.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/docs/v3/sdk/ios-tools.mdx +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/__main__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/codeact/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/codeact/events.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/common/constants.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/common/events.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/context/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/context/episodic_memory.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/context/task_manager.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/droid/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/executor/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/executor/events.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/executor/prompts.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/manager/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/manager/events.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/manager/prompts.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/oneflows/app_starter_workflow.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/oneflows/text_manipulator.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/usage.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/utils/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/utils/async_utils.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/utils/chat_utils.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/utils/device_state_formatter.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/utils/inference.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/utils/llm_picker.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/utils/message_utils.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/utils/tools.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/agent/utils/trajectory.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/app_cards/app_card_provider.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/app_cards/providers/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/app_cards/providers/composite_provider.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/app_cards/providers/local_provider.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/app_cards/providers/server_provider.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/cli/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/cli/logs.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10/droidrun}/config/app_cards/app_cards.json +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10/droidrun}/config/app_cards/gmail.md +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10/droidrun}/config/prompts/codeact/user.jinja2 +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10/droidrun}/config/prompts/executor/rev1.jinja2 +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10/droidrun}/config/prompts/executor/system.jinja2 +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10/droidrun}/config/prompts/manager/rev1.jinja2 +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10/droidrun}/config/prompts/manager/system.jinja2 +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10/droidrun}/config_example.yaml +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/config_manager/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/config_manager/prompt_loader.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/macro/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/macro/__main__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/macro/replay.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/portal.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/telemetry/phoenix.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/tools/__init__.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/tools/ios.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/tools/portal_client.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/droidrun/tools/tools.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/gen-docs-sdk-ref.sh +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/setup.py +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/static/droidrun-dark.png +0 -0
- {droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10}/static/droidrun.png +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: droidrun
|
|
3
|
-
Version: 0.3.10.
|
|
3
|
+
Version: 0.3.10.dev10
|
|
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
|
|
@@ -75,7 +75,6 @@ class CodeActAgent(Workflow):
|
|
|
75
75
|
self.remembered_info = None
|
|
76
76
|
|
|
77
77
|
self.goal = None
|
|
78
|
-
self.steps_counter = 0
|
|
79
78
|
self.code_exec_counter = 0
|
|
80
79
|
|
|
81
80
|
# Build tool list
|
|
@@ -164,7 +163,7 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
164
163
|
assert len(chat_history) > 0, "Chat history cannot be empty."
|
|
165
164
|
ctx.write_event_to_stream(ev)
|
|
166
165
|
|
|
167
|
-
if self.
|
|
166
|
+
if self.shared_state.step_number + 1 > self.max_steps:
|
|
168
167
|
ev = TaskEndEvent(
|
|
169
168
|
success=False,
|
|
170
169
|
reason=f"Reached max step count of {self.max_steps} steps",
|
|
@@ -172,8 +171,7 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
172
171
|
ctx.write_event_to_stream(ev)
|
|
173
172
|
return ev
|
|
174
173
|
|
|
175
|
-
self.
|
|
176
|
-
logger.info(f"🧠 Step {self.steps_counter}: Thinking...")
|
|
174
|
+
logger.info(f"🧠 Step {self.shared_state.step_number + 1}: Thinking...")
|
|
177
175
|
|
|
178
176
|
model = self.llm.class_name()
|
|
179
177
|
|
|
@@ -200,15 +198,17 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
200
198
|
formatted_text, focused_text, a11y_tree, phone_state = format_device_state(raw_state)
|
|
201
199
|
|
|
202
200
|
# Update shared_state if available
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
201
|
+
assert self.shared_state is not None, "Shared state is not set"
|
|
202
|
+
self.shared_state.formatted_device_state = formatted_text
|
|
203
|
+
self.shared_state.focused_text = focused_text
|
|
204
|
+
self.shared_state.a11y_tree = a11y_tree
|
|
205
|
+
self.shared_state.phone_state = phone_state
|
|
206
|
+
|
|
207
|
+
# Extract and store package/app name (using unified update method)
|
|
208
|
+
self.shared_state.update_current_app(
|
|
209
|
+
package_name=phone_state.get('packageName', 'Unknown'),
|
|
210
|
+
activity_name=phone_state.get('currentApp', 'Unknown')
|
|
211
|
+
)
|
|
212
212
|
|
|
213
213
|
# Stream formatted state for trajectory
|
|
214
214
|
ctx.write_event_to_stream(RecordUIStateEvent(ui_state=a11y_tree))
|
|
@@ -235,6 +235,7 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
235
235
|
usage = None
|
|
236
236
|
|
|
237
237
|
await self.chat_memory.aput(response.message)
|
|
238
|
+
self.shared_state.step_number += 1
|
|
238
239
|
|
|
239
240
|
code, thoughts = chat_utils.extract_code_and_thought(response.message.content)
|
|
240
241
|
|
|
@@ -359,8 +360,6 @@ Now, describe the next step you will take to address the original goal: {goal}""
|
|
|
359
360
|
{
|
|
360
361
|
"success": ev.success,
|
|
361
362
|
"reason": ev.reason,
|
|
362
|
-
"output": ev.reason,
|
|
363
|
-
"codeact_steps": self.steps_counter,
|
|
364
363
|
"code_executions": self.code_exec_counter,
|
|
365
364
|
}
|
|
366
365
|
)
|
|
File without changes
|
|
@@ -124,7 +124,11 @@ class DroidAgent(Workflow):
|
|
|
124
124
|
"""
|
|
125
125
|
|
|
126
126
|
self.user_id = kwargs.pop("user_id", None)
|
|
127
|
-
|
|
127
|
+
self.runtype = kwargs.pop("runtype", "developer")
|
|
128
|
+
self.shared_state = DroidAgentState(
|
|
129
|
+
instruction=goal,
|
|
130
|
+
err_to_manager_thresh=2
|
|
131
|
+
)
|
|
128
132
|
base_config = config
|
|
129
133
|
|
|
130
134
|
self.config = DroidRunConfig(
|
|
@@ -154,7 +158,6 @@ class DroidAgent(Workflow):
|
|
|
154
158
|
" • If installed via pip: `uv pip install droidrun[phoenix]`\n"
|
|
155
159
|
)
|
|
156
160
|
|
|
157
|
-
self.goal = goal
|
|
158
161
|
self.timeout = timeout
|
|
159
162
|
self.custom_tools = custom_tools or {}
|
|
160
163
|
|
|
@@ -180,8 +183,7 @@ class DroidAgent(Workflow):
|
|
|
180
183
|
self.app_opener_llm = llms
|
|
181
184
|
|
|
182
185
|
|
|
183
|
-
self.
|
|
184
|
-
self.trajectory = Trajectory(goal=goal)
|
|
186
|
+
self.trajectory = Trajectory(goal=self.shared_state.instruction)
|
|
185
187
|
self.task_manager = TaskManager()
|
|
186
188
|
self.task_iter = None
|
|
187
189
|
self.current_episodic_memory = None
|
|
@@ -202,10 +204,6 @@ class DroidAgent(Workflow):
|
|
|
202
204
|
# Set app_opener_llm on tools instance for open_app custom tool
|
|
203
205
|
self.tools_instance.app_opener_llm = self.app_opener_llm
|
|
204
206
|
|
|
205
|
-
self.shared_state = DroidAgentState(
|
|
206
|
-
instruction=goal,
|
|
207
|
-
err_to_manager_thresh=2
|
|
208
|
-
)
|
|
209
207
|
|
|
210
208
|
if self.config.agent.reasoning:
|
|
211
209
|
logger.info("📝 Initializing Manager and Executor Agents...")
|
|
@@ -236,7 +234,7 @@ class DroidAgent(Workflow):
|
|
|
236
234
|
|
|
237
235
|
capture(
|
|
238
236
|
DroidAgentInitEvent(
|
|
239
|
-
goal=
|
|
237
|
+
goal=self.shared_state.instruction,
|
|
240
238
|
llms={
|
|
241
239
|
"manager": self.manager_llm.class_name() if self.manager_llm else "None",
|
|
242
240
|
"executor": self.executor_llm.class_name() if self.executor_llm else "None",
|
|
@@ -257,6 +255,7 @@ class DroidAgent(Workflow):
|
|
|
257
255
|
enable_tracing=self.config.tracing.enabled,
|
|
258
256
|
debug=self.config.logging.debug,
|
|
259
257
|
save_trajectories=self.config.logging.save_trajectory,
|
|
258
|
+
runtype=self.runtype,
|
|
260
259
|
),
|
|
261
260
|
self.user_id,
|
|
262
261
|
)
|
|
@@ -269,35 +268,6 @@ class DroidAgent(Workflow):
|
|
|
269
268
|
"""
|
|
270
269
|
return super().run(*args, **kwargs)
|
|
271
270
|
|
|
272
|
-
def _create_finalize_event(
|
|
273
|
-
self,
|
|
274
|
-
success: bool,
|
|
275
|
-
reason: str,
|
|
276
|
-
output: str
|
|
277
|
-
) -> FinalizeEvent:
|
|
278
|
-
"""
|
|
279
|
-
Single source of truth for creating FinalizeEvent.
|
|
280
|
-
|
|
281
|
-
This helper ensures all FinalizeEvent creation is consistent
|
|
282
|
-
across the workflow.
|
|
283
|
-
|
|
284
|
-
Args:
|
|
285
|
-
success: Whether the task succeeded
|
|
286
|
-
reason: Reason for completion (deprecated, use output)
|
|
287
|
-
output: Output message
|
|
288
|
-
|
|
289
|
-
Returns:
|
|
290
|
-
FinalizeEvent ready to be returned
|
|
291
|
-
"""
|
|
292
|
-
return FinalizeEvent(
|
|
293
|
-
success=success,
|
|
294
|
-
reason=reason,
|
|
295
|
-
output=output,
|
|
296
|
-
task=[], # TODO: use the final plan as the tasks and the goal as task
|
|
297
|
-
tasks=[],
|
|
298
|
-
steps=self.step_counter
|
|
299
|
-
)
|
|
300
|
-
|
|
301
271
|
@step
|
|
302
272
|
async def execute_task(self, ctx: Context, ev: CodeActExecuteEvent) -> CodeActResultEvent:
|
|
303
273
|
"""
|
|
@@ -339,14 +309,12 @@ class DroidAgent(Workflow):
|
|
|
339
309
|
success=True,
|
|
340
310
|
reason=result["reason"],
|
|
341
311
|
task=task,
|
|
342
|
-
steps=result["codeact_steps"],
|
|
343
312
|
)
|
|
344
313
|
else:
|
|
345
314
|
return CodeActResultEvent(
|
|
346
315
|
success=False,
|
|
347
316
|
reason=result["reason"],
|
|
348
317
|
task=task,
|
|
349
|
-
steps=result["codeact_steps"],
|
|
350
318
|
)
|
|
351
319
|
|
|
352
320
|
except Exception as e:
|
|
@@ -354,35 +322,25 @@ class DroidAgent(Workflow):
|
|
|
354
322
|
if self.config.logging.debug:
|
|
355
323
|
import traceback
|
|
356
324
|
logger.error(traceback.format_exc())
|
|
357
|
-
return CodeActResultEvent(success=False, reason=f"Error: {str(e)}", task=task
|
|
325
|
+
return CodeActResultEvent(success=False, reason=f"Error: {str(e)}", task=task)
|
|
358
326
|
|
|
359
327
|
@step
|
|
360
328
|
async def handle_codeact_execute(
|
|
361
329
|
self, ctx: Context, ev: CodeActResultEvent
|
|
362
330
|
) -> FinalizeEvent:
|
|
363
331
|
try:
|
|
364
|
-
task = ev.task
|
|
365
332
|
return FinalizeEvent(
|
|
366
333
|
success=ev.success,
|
|
367
|
-
reason=ev.reason
|
|
368
|
-
output=ev.reason,
|
|
369
|
-
task=[task],
|
|
370
|
-
tasks=[task],
|
|
371
|
-
steps=ev.steps,
|
|
334
|
+
reason=ev.reason
|
|
372
335
|
)
|
|
373
336
|
except Exception as e:
|
|
374
337
|
logger.error(f"❌ Error during DroidAgent execution: {e}")
|
|
375
338
|
if self.config.logging.debug:
|
|
376
339
|
import traceback
|
|
377
340
|
logger.error(traceback.format_exc())
|
|
378
|
-
tasks = self.task_manager.get_task_history()
|
|
379
341
|
return FinalizeEvent(
|
|
380
342
|
success=False,
|
|
381
343
|
reason=str(e),
|
|
382
|
-
output=str(e),
|
|
383
|
-
task=tasks,
|
|
384
|
-
tasks=tasks,
|
|
385
|
-
steps=self.step_counter,
|
|
386
344
|
)
|
|
387
345
|
|
|
388
346
|
@step
|
|
@@ -395,16 +353,14 @@ class DroidAgent(Workflow):
|
|
|
395
353
|
Returns:
|
|
396
354
|
Event to trigger next step based on reasoning mode
|
|
397
355
|
"""
|
|
398
|
-
logger.info(f"🚀 Running DroidAgent to achieve goal: {self.
|
|
356
|
+
logger.info(f"🚀 Running DroidAgent to achieve goal: {self.shared_state.instruction}")
|
|
399
357
|
ctx.write_event_to_stream(ev)
|
|
400
358
|
|
|
401
|
-
self.step_counter = 0
|
|
402
|
-
self.retry_counter = 0
|
|
403
359
|
|
|
404
360
|
if not self.config.agent.reasoning:
|
|
405
|
-
logger.info(f"🔄 Direct execution mode - executing goal: {self.
|
|
361
|
+
logger.info(f"🔄 Direct execution mode - executing goal: {self.shared_state.instruction}")
|
|
406
362
|
task = Task(
|
|
407
|
-
description=self.
|
|
363
|
+
description=self.shared_state.instruction,
|
|
408
364
|
status=self.task_manager.STATUS_PENDING,
|
|
409
365
|
agent_type="Default",
|
|
410
366
|
)
|
|
@@ -429,15 +385,14 @@ class DroidAgent(Workflow):
|
|
|
429
385
|
Pre-flight checks for termination before running manager.
|
|
430
386
|
The Manager analyzes current state and creates a plan with subgoals.
|
|
431
387
|
"""
|
|
432
|
-
if self.
|
|
388
|
+
if self.shared_state.step_number >= self.config.agent.max_steps:
|
|
433
389
|
logger.warning(f"⚠️ Reached maximum steps ({self.config.agent.max_steps})")
|
|
434
|
-
return
|
|
390
|
+
return FinalizeEvent(
|
|
435
391
|
success=False,
|
|
436
|
-
reason=f"Reached maximum steps ({self.config.agent.max_steps})"
|
|
437
|
-
output=f"Reached maximum steps ({self.config.agent.max_steps})"
|
|
392
|
+
reason=f"Reached maximum steps ({self.config.agent.max_steps})"
|
|
438
393
|
)
|
|
439
394
|
|
|
440
|
-
logger.info(f"📋 Running Manager for planning... (step {self.
|
|
395
|
+
logger.info(f"📋 Running Manager for planning... (step {self.shared_state.step_number}/{self.config.agent.max_steps})")
|
|
441
396
|
|
|
442
397
|
# Run Manager workflow
|
|
443
398
|
handler = self.manager_agent.run()
|
|
@@ -472,10 +427,9 @@ class DroidAgent(Workflow):
|
|
|
472
427
|
logger.info(f"💬 Manager provided answer: {ev.manager_answer}")
|
|
473
428
|
self.shared_state.progress_status = f"Answer: {ev.manager_answer}"
|
|
474
429
|
|
|
475
|
-
return
|
|
430
|
+
return FinalizeEvent(
|
|
476
431
|
success=True,
|
|
477
|
-
reason=ev.manager_answer
|
|
478
|
-
output=ev.manager_answer
|
|
432
|
+
reason=ev.manager_answer
|
|
479
433
|
)
|
|
480
434
|
|
|
481
435
|
# Continue to Executor with current subgoal
|
|
@@ -533,7 +487,7 @@ class DroidAgent(Workflow):
|
|
|
533
487
|
Checks for error escalation and loops back to Manager.
|
|
534
488
|
Note: Max steps check is now done in run_manager pre-flight.
|
|
535
489
|
"""
|
|
536
|
-
# Check error escalation
|
|
490
|
+
# Check error escalation and reset flag when errors are resolved
|
|
537
491
|
err_thresh = self.shared_state.err_to_manager_thresh
|
|
538
492
|
|
|
539
493
|
if len(self.shared_state.action_outcomes) >= err_thresh:
|
|
@@ -542,9 +496,13 @@ class DroidAgent(Workflow):
|
|
|
542
496
|
if error_count == err_thresh:
|
|
543
497
|
logger.warning(f"⚠️ Error escalation: {err_thresh} consecutive errors")
|
|
544
498
|
self.shared_state.error_flag_plan = True
|
|
499
|
+
else:
|
|
500
|
+
if self.shared_state.error_flag_plan:
|
|
501
|
+
logger.info("✅ Error resolved - resetting error flag")
|
|
502
|
+
self.shared_state.error_flag_plan = False
|
|
545
503
|
|
|
546
|
-
self.
|
|
547
|
-
logger.info(f"🔄 Step {self.
|
|
504
|
+
self.shared_state.step_number += 1
|
|
505
|
+
logger.info(f"🔄 Step {self.shared_state.step_number}/{self.config.agent.max_steps} complete, looping to Manager")
|
|
548
506
|
|
|
549
507
|
return ManagerInputEvent()
|
|
550
508
|
|
|
@@ -557,10 +515,11 @@ class DroidAgent(Workflow):
|
|
|
557
515
|
ctx.write_event_to_stream(ev)
|
|
558
516
|
capture(
|
|
559
517
|
DroidAgentFinalizeEvent(
|
|
560
|
-
tasks=",".join([f"{t.agent_type}:{t.description}" for t in ev.task]),
|
|
561
518
|
success=ev.success,
|
|
562
|
-
|
|
563
|
-
steps=
|
|
519
|
+
reason=ev.reason,
|
|
520
|
+
steps=self.shared_state.step_number,
|
|
521
|
+
unique_packages_count=len(self.shared_state._visited_packages),
|
|
522
|
+
unique_activities_count=len(self.shared_state._visited_activities),
|
|
564
523
|
),
|
|
565
524
|
self.user_id,
|
|
566
525
|
)
|
|
@@ -569,8 +528,7 @@ class DroidAgent(Workflow):
|
|
|
569
528
|
result = {
|
|
570
529
|
"success": ev.success,
|
|
571
530
|
"reason": ev.reason,
|
|
572
|
-
"
|
|
573
|
-
"steps": ev.steps,
|
|
531
|
+
"steps": self.shared_state.step_number,
|
|
574
532
|
}
|
|
575
533
|
|
|
576
534
|
if self.trajectory and self.config.logging.save_trajectory != "none":
|
|
@@ -26,18 +26,11 @@ class CodeActResultEvent(Event):
|
|
|
26
26
|
success: bool
|
|
27
27
|
reason: str
|
|
28
28
|
task: Task
|
|
29
|
-
steps: int
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
class FinalizeEvent(Event):
|
|
33
32
|
success: bool
|
|
34
|
-
# deprecated. use output instead.
|
|
35
33
|
reason: str
|
|
36
|
-
output: str
|
|
37
|
-
# deprecated. use tasks instead.
|
|
38
|
-
task: List[Task]
|
|
39
|
-
tasks: List[Task]
|
|
40
|
-
steps: int = 1
|
|
41
34
|
|
|
42
35
|
class TaskRunnerEvent(Event):
|
|
43
36
|
pass
|
|
@@ -50,9 +43,12 @@ class DroidAgentState(BaseModel):
|
|
|
50
43
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
51
44
|
# Task context
|
|
52
45
|
instruction: str = ""
|
|
46
|
+
step_number: int = 0
|
|
53
47
|
# App Cards
|
|
54
48
|
app_card: str = ""
|
|
55
49
|
app_card_loading_task: asyncio.Task[str] | None = None
|
|
50
|
+
_app_card_package: str = "" # Track which package the loading task is for
|
|
51
|
+
_app_card_instruction: str = "" # Track which instruction the loading task is for
|
|
56
52
|
# Formatted device state for prompts (complete text)
|
|
57
53
|
formatted_device_state: str = ""
|
|
58
54
|
|
|
@@ -63,9 +59,11 @@ class DroidAgentState(BaseModel):
|
|
|
63
59
|
a11y_tree: List[Dict] = Field(default_factory=list)
|
|
64
60
|
phone_state: Dict = Field(default_factory=dict)
|
|
65
61
|
|
|
66
|
-
#
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
# Private fields
|
|
63
|
+
_current_package_name: str = ""
|
|
64
|
+
_current_activity_name: str = ""
|
|
65
|
+
_visited_packages: set = Field(default_factory=set)
|
|
66
|
+
_visited_activities: set = Field(default_factory=set)
|
|
69
67
|
|
|
70
68
|
# Previous device state (for before/after comparison in Manager)
|
|
71
69
|
previous_formatted_device_state: str = ""
|
|
@@ -108,6 +106,50 @@ class DroidAgentState(BaseModel):
|
|
|
108
106
|
# Output
|
|
109
107
|
output_dir: str = ""
|
|
110
108
|
|
|
109
|
+
@property
|
|
110
|
+
def current_package_name(self) -> str:
|
|
111
|
+
"""Get current package name"""
|
|
112
|
+
return self._current_package_name
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def current_activity_name(self) -> str:
|
|
116
|
+
"""Get current activity name"""
|
|
117
|
+
return self._current_activity_name
|
|
118
|
+
|
|
119
|
+
def update_current_app(self, package_name: str, activity_name: str):
|
|
120
|
+
"""
|
|
121
|
+
Update package and activity together, capturing telemetry event only once.
|
|
122
|
+
|
|
123
|
+
This prevents duplicate PackageVisitEvents when both package and activity change.
|
|
124
|
+
"""
|
|
125
|
+
# Check if either changed
|
|
126
|
+
package_changed = package_name != self._current_package_name
|
|
127
|
+
activity_changed = activity_name != self._current_activity_name
|
|
128
|
+
|
|
129
|
+
if not (package_changed or activity_changed):
|
|
130
|
+
return # No change, nothing to do
|
|
131
|
+
|
|
132
|
+
# Update tracking sets
|
|
133
|
+
if package_changed and package_name:
|
|
134
|
+
self._visited_packages.add(package_name)
|
|
135
|
+
if activity_changed and activity_name:
|
|
136
|
+
self._visited_activities.add(activity_name)
|
|
137
|
+
|
|
138
|
+
# Update values
|
|
139
|
+
self._current_package_name = package_name
|
|
140
|
+
self._current_activity_name = activity_name
|
|
141
|
+
|
|
142
|
+
# Capture telemetry event for any change
|
|
143
|
+
# This ensures we track when apps close or transitions to empty state occur
|
|
144
|
+
from droidrun.telemetry import PackageVisitEvent, capture
|
|
145
|
+
capture(
|
|
146
|
+
PackageVisitEvent(
|
|
147
|
+
package_name=package_name or "Unknown",
|
|
148
|
+
activity_name=activity_name or "Unknown",
|
|
149
|
+
step_number=self.step_number,
|
|
150
|
+
)
|
|
151
|
+
)
|
|
152
|
+
|
|
111
153
|
|
|
112
154
|
# ============================================================================
|
|
113
155
|
# Manager/Executor coordination events
|
|
@@ -39,7 +39,7 @@ if TYPE_CHECKING:
|
|
|
39
39
|
logger = logging.getLogger("droidrun")
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
class ExecutorAgent(Workflow):
|
|
42
|
+
class ExecutorAgent(Workflow):
|
|
43
43
|
"""
|
|
44
44
|
Action execution agent that performs specific actions.
|
|
45
45
|
|
|
@@ -115,7 +115,7 @@ class ExecutorAgent(Workflow): # TODO: Fix a bug in bad prompt
|
|
|
115
115
|
self.agent_config.get_executor_system_prompt_path(),
|
|
116
116
|
{
|
|
117
117
|
"instruction": self.shared_state.instruction,
|
|
118
|
-
"app_card": "", # TODO:
|
|
118
|
+
"app_card": "", # TODO: optionally implement app card loader
|
|
119
119
|
"device_state": self.shared_state.formatted_device_state,
|
|
120
120
|
"plan": self.shared_state.plan,
|
|
121
121
|
"subgoal": subgoal,
|
|
@@ -196,9 +196,7 @@ class ExecutorAgent(Workflow): # TODO: Fix a bug in bad prompt
|
|
|
196
196
|
|
|
197
197
|
outcome, error, summary = await self._execute_action(action_dict, ev.description)
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
# Available via: self.agent_config.after_sleep_action
|
|
201
|
-
# await asyncio.sleep(self.agent_config.after_sleep_action)
|
|
199
|
+
await asyncio.sleep(self.agent_config.after_sleep_action)
|
|
202
200
|
|
|
203
201
|
logger.info(f"{'✅' if outcome else '❌'} Execution complete: {summary}")
|
|
204
202
|
|
|
@@ -328,21 +328,38 @@ class ManagerAgent(Workflow):
|
|
|
328
328
|
self.shared_state.phone_state = phone_state
|
|
329
329
|
|
|
330
330
|
# Extract and store package/app name
|
|
331
|
-
self.shared_state.
|
|
332
|
-
|
|
331
|
+
self.shared_state.update_current_app(
|
|
332
|
+
package_name=phone_state.get('packageName', 'Unknown'),
|
|
333
|
+
activity_name=phone_state.get('currentApp', 'Unknown')
|
|
334
|
+
)
|
|
333
335
|
|
|
334
336
|
# ====================================================================
|
|
335
|
-
# Step 1.5: Start loading app card in background
|
|
337
|
+
# Step 1.5: Start loading app card in background (only if package/instruction changed)
|
|
336
338
|
# ====================================================================
|
|
337
339
|
if self.app_card_config.enabled:
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
340
|
+
current_package = self.shared_state.current_package_name
|
|
341
|
+
current_instruction = self.shared_state.instruction
|
|
342
|
+
|
|
343
|
+
# Check if we need to start a new loading task
|
|
344
|
+
package_changed = current_package != self.shared_state._app_card_package
|
|
345
|
+
instruction_changed = current_instruction != self.shared_state._app_card_instruction
|
|
346
|
+
|
|
347
|
+
if package_changed or instruction_changed:
|
|
348
|
+
# Cancel old task if it exists and is still running (non-blocking)
|
|
349
|
+
if (self.shared_state.app_card_loading_task and
|
|
350
|
+
not self.shared_state.app_card_loading_task.done()):
|
|
351
|
+
self.shared_state.app_card_loading_task.cancel()
|
|
352
|
+
|
|
353
|
+
# Start new loading task
|
|
354
|
+
loading_task = asyncio.create_task(
|
|
355
|
+
self.app_card_provider.load_app_card(
|
|
356
|
+
package_name=current_package,
|
|
357
|
+
instruction=current_instruction
|
|
358
|
+
)
|
|
342
359
|
)
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
360
|
+
self.shared_state.app_card_loading_task = loading_task
|
|
361
|
+
self.shared_state._app_card_package = current_package
|
|
362
|
+
self.shared_state._app_card_instruction = current_instruction
|
|
346
363
|
# ====================================================================
|
|
347
364
|
# Step 2: Capture screenshot if vision enabled
|
|
348
365
|
# ====================================================================
|
|
@@ -433,6 +450,10 @@ class ManagerAgent(Workflow):
|
|
|
433
450
|
except asyncio.TimeoutError:
|
|
434
451
|
# Task not ready yet, use empty string
|
|
435
452
|
self.shared_state.app_card = ""
|
|
453
|
+
except asyncio.CancelledError:
|
|
454
|
+
# Task was cancelled (app/instruction changed), use empty string
|
|
455
|
+
logger.debug("App card task was cancelled")
|
|
456
|
+
self.shared_state.app_card = ""
|
|
436
457
|
except Exception as e:
|
|
437
458
|
logger.warning(f"Error getting app card: {e}")
|
|
438
459
|
self.shared_state.app_card = ""
|
|
File without changes
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import contextlib
|
|
2
3
|
import io
|
|
3
4
|
import logging
|
|
@@ -109,7 +110,7 @@ class SimpleCodeExecutor:
|
|
|
109
110
|
|
|
110
111
|
return output
|
|
111
112
|
|
|
112
|
-
async def execute(self, state: ExecuterState, code: str) -> str:
|
|
113
|
+
async def execute(self, state: ExecuterState, code: str, timeout: float = 10.0) -> str:
|
|
113
114
|
"""
|
|
114
115
|
Execute Python code and capture output and return values.
|
|
115
116
|
|
|
@@ -118,6 +119,7 @@ class SimpleCodeExecutor:
|
|
|
118
119
|
Args:
|
|
119
120
|
state: ExecuterState containing ui_state and other execution context.
|
|
120
121
|
code: Python code to execute
|
|
122
|
+
timeout: Maximum execution time in seconds (default: 30.0)
|
|
121
123
|
|
|
122
124
|
Returns:
|
|
123
125
|
str: Output from the execution, including print statements.
|
|
@@ -125,12 +127,16 @@ class SimpleCodeExecutor:
|
|
|
125
127
|
# Get UI state from the state object
|
|
126
128
|
ui_state = state.ui_state
|
|
127
129
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
130
|
+
try:
|
|
131
|
+
output = await asyncio.wait_for(
|
|
132
|
+
self.loop.run_in_executor(
|
|
133
|
+
None,
|
|
134
|
+
self._execute_in_thread,
|
|
135
|
+
code,
|
|
136
|
+
ui_state
|
|
137
|
+
),
|
|
138
|
+
timeout=timeout
|
|
139
|
+
)
|
|
140
|
+
return output
|
|
141
|
+
except asyncio.TimeoutError:
|
|
142
|
+
return f"Error: Execution timed out after {timeout} seconds"
|
|
File without changes
|
|
@@ -300,6 +300,7 @@ async def run_command(
|
|
|
300
300
|
tracing_config=tracing_cfg,
|
|
301
301
|
excluded_tools=excluded_tools,
|
|
302
302
|
timeout=1000,
|
|
303
|
+
runtype="cli"
|
|
303
304
|
)
|
|
304
305
|
|
|
305
306
|
# ================================================================
|
|
@@ -563,7 +564,6 @@ def run(
|
|
|
563
564
|
device_obj.shell("ime disable com.droidrun.portal/.DroidrunKeyboardIME")
|
|
564
565
|
except Exception:
|
|
565
566
|
click.echo("Failed to disable DroidRun keyboard")
|
|
566
|
-
pass
|
|
567
567
|
|
|
568
568
|
|
|
569
569
|
@cli.command()
|
|
@@ -624,7 +624,7 @@ def setup(path: str | None, device: str | None, debug: bool):
|
|
|
624
624
|
"""Install and enable the DroidRun Portal on a device."""
|
|
625
625
|
from droidrun.config_manager.path_resolver import PathResolver
|
|
626
626
|
|
|
627
|
-
# Ensure config.yaml exists (check working dir, then
|
|
627
|
+
# Ensure config.yaml exists (check working dir, then package dir)
|
|
628
628
|
try:
|
|
629
629
|
config_path = PathResolver.resolve("config.yaml")
|
|
630
630
|
console.print(f"[blue]Using existing config: {config_path}[/]")
|
|
@@ -60,13 +60,15 @@ App cards support three path types:
|
|
|
60
60
|
```json
|
|
61
61
|
{"com.google.gm": "gmail.md"}
|
|
62
62
|
```
|
|
63
|
-
Resolves to: `config/app_cards/gmail.md`
|
|
63
|
+
Resolves to: `config/app_cards/gmail.md` (in package)
|
|
64
64
|
|
|
65
|
-
2. **Relative
|
|
65
|
+
2. **Relative paths with PathResolver**:
|
|
66
66
|
```json
|
|
67
67
|
{"com.google.gm": "config/custom_cards/gmail.md"}
|
|
68
68
|
```
|
|
69
|
-
|
|
69
|
+
Checks working directory first, then package directory
|
|
70
|
+
- Working dir: `./config/custom_cards/gmail.md`
|
|
71
|
+
- Package dir: `<package>/config/custom_cards/gmail.md`
|
|
70
72
|
|
|
71
73
|
3. **Absolute path**:
|
|
72
74
|
```json
|
{droidrun-0.3.10.dev8 → droidrun-0.3.10.dev10/droidrun}/config/prompts/codeact/system.jinja2
RENAMED
|
@@ -45,7 +45,6 @@ click(1)
|
|
|
45
45
|
click(5)
|
|
46
46
|
complete(success=True, reason="Successfully navigated to Wi-Fi settings and initiated connection to HomeNetwork")
|
|
47
47
|
```
|
|
48
|
-
```
|
|
49
48
|
|
|
50
49
|
## Tools:
|
|
51
50
|
In addition to the Python Standard Library and any functions you have already written, you can use the following functions:
|
|
@@ -431,7 +431,7 @@ class ConfigManager:
|
|
|
431
431
|
# Resolution order:
|
|
432
432
|
# 1) Explicit path arg
|
|
433
433
|
# 2) DROIDRUN_CONFIG env var
|
|
434
|
-
# 3) Default "config.yaml" (checks working dir, then
|
|
434
|
+
# 3) Default "config.yaml" (checks working dir, then package dir)
|
|
435
435
|
if path:
|
|
436
436
|
self.path = PathResolver.resolve(path)
|
|
437
437
|
else:
|
|
@@ -439,7 +439,7 @@ class ConfigManager:
|
|
|
439
439
|
if env:
|
|
440
440
|
self.path = PathResolver.resolve(env)
|
|
441
441
|
else:
|
|
442
|
-
# Default: checks CWD first, then
|
|
442
|
+
# Default: checks CWD first, then package dir
|
|
443
443
|
self.path = PathResolver.resolve("config.yaml")
|
|
444
444
|
|
|
445
445
|
# Initialize with default config
|