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

35 lines
9.7 KiB
Markdown

## 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 `WorkflowDefinition`s), 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`.