Files
agentic-mobile-control/openspec/changes/cloud-runtime/proposal.md
T

9.7 KiB

Why

Every capability built through Milestone 9 — device-management/driver-registry (apex-agent-mvp, device-agent-runtime-foundation), agent-runtime, workflow-orchestration, multi-agent-collaboration — still assumes one process, one DeviceManager, and a caller who already knows which device_id to target. That is fine for a single developer driving a handful of physical devices from one machine, but it does not scale to "many devices across many hosts, tasks submitted by external systems without knowing which device is free, and new drivers/tools/skills added without editing this repository's core packages." Milestone 10 (Cloud Runtime) is the platform-completion step: it introduces a device pool that aggregates device state across hosts, a scheduler that assigns queued tasks to idle devices instead of requiring callers to pick one, a plugin mechanism so a second driver (or a tool/skill) can register itself without touching driver/, device/, or tools/, and a versioned public SDK/API for external integrators that is distinct from the LLM-facing MCP tool server. None of this replaces the existing single-process, single-goal execution path — it composes it, the same way workflow-orchestration-runtime composed TaskRunner rather than rewriting it.

What Changes

  • Add a new cloud/ package, sibling to agents/, workflow/, semantic/, world/, skills_learning/, holding all four new capabilities below. No existing package (driver/, device/, runtime/, tools/, workflow/, agents/, storage/, api/console.py, api/mcp.py) is modified.
  • Add cloud/pool.py: a DevicePool that aggregates device state across multiple hosts, each still running its own unmodified single-process DeviceManager (device-management). A per-host sync call (DevicePool.sync_host_devices(host_id, snapshot)) pushes that host's DeviceManager.list_devices() result into the pool on a heartbeat interval; the pool tracks HostRegistration (host id, address, last-seen) and PooledDevice (device id, owning host id, driver_type, status, capability tags) records, and marks a host's devices unreachable (a new pool-level status, distinct from device-management's own idle/busy/offline/error) once its heartbeat goes stale — never raises or blocks on a missing host.
  • Add cloud/scheduler.py: a TaskScheduler that accepts a queued task submission (goal + optional device constraints: driver_type, required capability tags), holds it in a bounded FIFO queue backed by cloud/store.py, and assigns it to the first matching idle PooledDevice the DevicePool reports, via a pluggable AssignmentStrategy registry (default: FIFO + constraint match; a priority/capability-scoring strategy can be added later without changing TaskScheduler's control flow).
  • Add cloud/dispatch.py: a TaskDispatcher that takes a TaskScheduler assignment (task + device_id + host_id) and actually runs it by composing the existing, unmodified agent-runtime TaskRunner (single-goal tasks) or workflow-orchestration's WorkflowRunner (multi-step WorkflowDefinitions), strictly through their existing public run(task) -> Task / run(definition) -> WorkflowRun entry points — closing the loop from "assigned" to "executed" without a caller ever hand-picking a device_id again.
  • Add cloud/plugins.py: a PluginManifest schema (name, version, entry_point_kind: driver | tool | skill, module/callable reference) and a PluginRegistry that discovers manifests via Python entry_points (group device_agent_runtime.plugins) and/or a local plugins/*/plugin.json directory scan, validates them, and — for driver-kind manifests only — registers them into driver-registry's existing driver_type -> factory extension point. tool- and skill-kind manifests are accepted and validated by the same schema but are declared-not-yet-wired in this change (see Non-Goals in design.md), since tools/ and the skill stores (skill-catalog-subscription, skill-learning-runtime) do not yet expose a comparable public registration hook to compose against.
  • Add cloud/sdk/ (api.py, client.py, models.py): a versioned (/v1/...) public REST API plus a thin Python client, for external integrators to submit tasks/workflows to the pool+scheduler, poll task/device/host status, and list/register plugins — a fleet-facing surface distinct from mcp-tool-server's per-device, LLM-facing tool calls and from web-console's operator-facing status/config UI (console-status-api/console-config-api), which both continue to talk to one process's own DeviceManager directly.
  • Add cloud/store.py: a new, independently-owned SQLite file (cloud/cloud.sqlite3) with host_registrations, pooled_devices, scheduled_tasks, and plugins tables — mirrors workflow-orchestration-runtime's precedent of a capability-owned store rather than adding tables to storage/task_metadata.py.
  • Add cloud/config.py: heartbeat interval, host-staleness threshold, queue depth limit, default AssignmentStrategy name, SDK API version prefix — all with conservative defaults so this change is inert until a caller actually registers a second host or submits through the new SDK.
  • BREAKING: none. driver/, device/, runtime/, tools/, workflow/, agents/, storage/, api/console.py, and api/mcp.py are not modified; cloud/ is purely additive and composes all of them by import.

Capabilities

New Capabilities

  • device-pool: A multi-host device registry/discovery layer (DevicePool, HostRegistration, PooledDevice) aggregating the device state each host's existing single-process DeviceManager already tracks, with heartbeat-based staleness detection so an unreachable host degrades its devices' pool-visible status rather than blocking or crashing the pool.
  • task-scheduler: A queue (TaskScheduler) plus pluggable AssignmentStrategy matching submitted tasks (goal + device constraints) to an idle PooledDevice, and a TaskDispatcher that executes an assignment by composing the existing agent-runtime/workflow-orchestration execution paths as black boxes.
  • plugin-system: A registration mechanism (PluginManifest + PluginRegistry, entry-points or manifest-file discovery) for new drivers/tools/skills to register themselves without editing driver/, device/, tools/, or the skill stores; concretely wired for driver-kind plugins into driver-registry's existing extension point, with tool/skill-kind plugins schema-defined for forward compatibility.
  • platform-sdk: A versioned, public-facing REST API and Python client for external integrators to submit tasks/workflows, query device/host/task status, and manage plugins across the device pool — separate from mcp-tool-server (LLM-facing) and console-status-api/console-config-api (operator-facing, single-process).

Modified Capabilities

(none — openspec/specs/ is currently empty and none of device-management, driver-registry, agent-runtime, workflow-orchestration, mcp-tool-server, console-status-api, console-config-api have an applied baseline to diff against; this change composes all of them by import/prose dependency only and does not alter their specified behavior.)

Impact

  • New package: cloud/pool.py (DevicePool, HostRegistration, PooledDevice), scheduler.py (TaskScheduler, AssignmentStrategy registry, ScheduledTask), dispatch.py (TaskDispatcher), plugins.py (PluginManifest, PluginRegistry), store.py (CloudStore, schema for cloud/cloud.sqlite3), config.py, and cloud/sdk/ (api.py, client.py, models.py).
  • No changes to core/ (or driver//device/ once device-agent-runtime-foundation is applied), runtime/, tools/, storage/, workflow/, agents/, api/console.py, api/mcp.py, api/rest.py — every integration point is by import/composition, matching the precedent set by workflow-orchestration-runtime (D1, D8) and multi-agent-runtime (CollaborativeTaskRunner composing TaskRunner).
  • Reads from pending capabilities (composition only, no spec changes to them): device-management/driver-registry for the per-host DeviceManager/driver-type extension point device-pool aggregates and plugin-system registers into; agent-runtime and workflow-orchestration for the TaskRunner/WorkflowRunner entry points task-scheduler's TaskDispatcher invokes; mcp-tool-server as the sibling LLM-facing surface platform-sdk sits alongside without replacing; console-status-api/console-config-api (web-console) as the sibling operator-facing surface this change does not extend or duplicate.
  • Config: pyproject.toml gains a cloud* entry in [tool.setuptools.packages.find].include; no new third-party dependency is required for the pool/scheduler/plugin data models (plugin discovery uses the standard-library importlib.metadata), though cloud/sdk/api.py reuses the already-declared fastapi/uvicorn dependencies and cloud/sdk/client.py will need an HTTP client (reuse httpx, already a dev dependency — promote to a runtime dependency in tasks.md).
  • Out of scope: no actual multi-host deployment/infra config (Docker/Kubernetes manifests, service discovery infra) — only the runtime's internal pool/scheduler/plugin/SDK model; no billing, multi-tenant auth, or rate limiting on platform-sdk (a later change's concern); no cross-host network dispatch mechanism for actually invoking a remote host's DeviceManager over the wire (TaskDispatcher composes TaskRunner/WorkflowRunner only when the assignment lands on the local host — see design.md's Non-Goals and Open Questions for the remote-dispatch gap); no changes to skill-catalog-subscription, skill-learning-runtime, web-console, or multi-agent-runtime.