feat(core): consolidate config hub, unify daemon scheduling across all entrypoints, and fix step replay

- Unified configuration and paths under artemis.config as single source of truth
- Consolidated execution across CLI, SDK, MCP, and Admin Console to default to Artemis Daemon
- Fixed frontend step replay to preserve failure steps with proper sequential numbering
- Implemented task deduplication, debouncing, and multi-device lock isolation
- Added comprehensive end-to-end multi-port integration tests for live phone and emulator
This commit is contained in:
somew1nd committed 2026-08-27 10:21:40 -07:00
1 parent e984d17635
commit 5f1ee18bc1
38 files changed
+1426 -203

No files matched your search

+59
View File
@@ -37,6 +37,8 @@ async def run_batch_tasks(
delay_seconds: float = 5.0,
) -> None:
"""Executes a list of automation tasks sequentially."""
if not os.environ.get("ARTEMIS_TASK_INGRESS"):
os.environ["ARTEMIS_TASK_INGRESS"] = "cli"
llm_config = initialize_llm_config()
profile = AgentProfile(name="default", llm_config=llm_config)
config = Builders.AgentConfig.with_default_profile(profile).build()
@@ -116,6 +118,13 @@ def batch_command(
help="Delay in seconds between successive tasks.",
),
] = 5.0,
standalone: Annotated[
bool,
typer.Option(
"--standalone",
help="Run in standalone embedded mode without routing through Artemis Daemon.",
),
] = False,
) -> None:
"""Execute multiple automation tasks in sequence."""
task_list: list[str] = []
@@ -157,4 +166,54 @@ def batch_command(
)
raise typer.Exit(1)
is_standalone = standalone or os.environ.get("ARTEMIS_STANDALONE") == "1"
if not is_standalone:
try:
from artemis.runtime import (
ensure_daemon_running,
submit_batch_to_daemon,
wait_for_daemon_task,
)
console = Console()
console.print("[dim]Connecting to Artemis Daemon scheduler...[/dim]")
is_running, base_url = ensure_daemon_running(timeout=8.0, wait_ready=True)
if is_running and base_url:
console.print(f"[bold green]✓[/bold green] Artemis Daemon active at [cyan]{base_url}[/cyan]")
resp = submit_batch_to_daemon(task_list, profile=profile, base_url=base_url)
if resp and resp.get("tasks"):
console.print(f"[bold green]✓[/bold green] Enqueued {len(task_list)} batch tasks in Daemon scheduler.\n")
results = []
for idx, t_item in enumerate(resp["tasks"], 1):
sid = t_item.get("session_id")
goal = t_item.get("goal")
console.print(f"[cyan]Waiting for Task {idx}/{len(task_list)}: '{goal}' (Session: {sid})...[/cyan]")
final_sess = wait_for_daemon_task(sid, base_url=base_url, timeout=1800.0)
st = final_sess.get("status")
err = final_sess.get("error") or ""
results.append({
"task": goal,
"status": "SUCCESS" if st in ("completed", "success") else "FAILED",
"error": err,
})
table = Table(title="Batch Execution Summary")
table.add_column("#", justify="right", style="cyan")
table.add_column("Task Goal", style="white")
table.add_column("Status", justify="center")
table.add_column("Notes", style="red")
for i, res in enumerate(results, 1):
status_styled = (
"[bold green]PASS[/bold green]"
if res["status"] == "SUCCESS"
else "[bold red]FAIL[/bold red]"
)
table.add_row(str(i), res["task"], status_styled, res["error"])
console.print(table)
return
except Exception as exc:
logger.debug(f"Batch daemon routing notice: {exc}")
asyncio.run(run_batch_tasks(task_list, profile_name=profile, delay_seconds=delay))