feat: add multi-device concurrency, server lifecycle CLI, and unified execution stream

- Implement DevicePool and DeviceExecutionLock concurrency modes (per-device FIFO vs global serialization)
- Extend ArtemisClient SDK and CLI with explicit device targeting and concurrency strategy configuration
- Add server lifecycle management (artemis restart, stop, status) with cross-platform PID discovery
- Update MCP server tools to support multi-device dispatch, trace tagging, and per-device execution
- Enhance Showcase UI with step-by-step visual replay, playback speed controls, and device serial badges
- Add comprehensive unit test coverage and edge-case verification scripts
This commit is contained in:
somew1nd committed 2026-08-26 15:53:14 -07:00
1 parent a36a66ce6d
commit c2c90a6378
84 files changed
+5612 -645

No files matched your search

+14
View File
@@ -44,9 +44,22 @@ class Task(BaseModel):
goal: str = Field(..., description="High-level goal description")
profile: Literal["flash", "pro"] = Field(default="pro", description="Execution profile")
device_id: str = Field(default="default-device", description="Target device serial")
device_serial: str | None = Field(
default=None, description="Target device serial (alias for device_id)"
)
concurrency_mode: Literal["global", "per_device"] | None = Field(
default=None,
description="Concurrency mode: 'global' (1 across all devices) or 'per_device' (1 per device)",
)
locked_package: str | None = Field(default=None, description="Package lock restriction")
max_turns: int = Field(default=30, description="Turn cutoff limit")
def model_post_init(self, __context: Any) -> None:
if self.device_serial and self.device_id == "default-device":
self.device_id = self.device_serial
elif self.device_id != "default-device" and not self.device_serial:
self.device_serial = self.device_id
class TaskResult(BaseModel):
"""Final structured outcome of a task execution."""
@@ -56,3 +69,4 @@ class TaskResult(BaseModel):
turns: int
output: Any = None
error: str | None = None
device_id: str | None = None