## 1. Cloud API: planner configuration - [ ] 1.1 Add Cloud API-side planner configuration (provider, model, timeout, provider API keys) analogous to `runtime/planner_config.py`, loaded from the Cloud API's own process environment (e.g. `AI_PLANNER_PROVIDER`/`AI_PLANNER_MODEL`/`AI_PLANNER_TIMEOUT_SECONDS`, `ANTHROPIC_API_KEY`/`OPENAI_API_KEY`), living in `packages/cloud-platform/cloud` or `apps/cloud-api`. - [ ] 1.2 Add `anthropic`/`openai` as explicit dependencies of the package that hosts this configuration (confirm whether `packages/cloud-platform` or `apps/cloud-api` is the right home, matching where the new endpoint handler will live). ## 2. Cloud API: planner-decision internal endpoint - [ ] 2.1 Add request/response Pydantic models to `cloud.internal_api.models` for a planner-decision call: request carries `system_prompt`, `user_prompt`, optional base64 screenshot, tool specs, timeout; response carries resolved `tool_name`/`arguments` or a structured error. - [ ] 2.2 Add the internal route (e.g. `POST /internal/v1/planner/decide`) to the existing internal router, reusing the current host-scoped bearer auth dependency used by heartbeat/claim/renew/result -- no new scope. - [ ] 2.3 Implement the route handler by constructing `runtime.tool_calling_client.AnthropicToolCallingClient` or `OpenAIToolCallingClient` (per D1) from the Cloud API's own planner configuration, calling `.decide(...)`, and translating `ToolCallDecision`/`ToolCallUnavailable` into the response model. - [ ] 2.4 Ensure the handler does not log or persist raw prompt text or screenshot bytes; only metadata (host id, resolved tool name, latency, error class) may be logged. ## 3. Host Agent: cloud-proxy transport - [ ] 3.1 Add `AI_PLANNER_TRANSPORT` (`direct` default | `cloud`) to `apps/device-host-agent/host_agent/config.py`. - [ ] 3.2 Add a `request_planner_decision(...)` method to `host_agent/client.py::HostAgentClient` that calls the new Cloud API endpoint using the existing authenticated `httpx` session, importing the new request/response models from `cloud.internal_api.models`. - [ ] 3.3 Add `host_agent/cloud_planner_client.py::CloudProxyToolCallingClient` implementing the `runtime.tool_calling_client.ToolCallingClient` Protocol structurally (no import of `host_agent`/`cloud` from `runtime`), wrapping `HostAgentClient.request_planner_decision(...)` and raising `ToolCallUnavailable` on any failure (network error, auth rejection, non-2xx, provider error, timeout) -- matching D7. ## 4. Host Agent: wiring - [ ] 4.1 Update `apps/device-host-agent/host_agent/execution.py`'s `_host_agent_planner_config()`/`create_task_runner()` so that when `AI_PLANNER_TRANSPORT=cloud`, the constructed `TaskRunner`'s `AIPlanner` is built with a `CloudProxyToolCallingClient` instead of the default local provider client, while leaving the existing default-enabled/direct-transport behavior unchanged when `AI_PLANNER_TRANSPORT` is unset or `direct`. - [ ] 4.2 Confirm `apps/device-host-agent/tests/test_execution.py::test_runtime_owned_packages_do_not_import_host_or_cloud_concerns`-style boundary checks still pass with the new module in place. ## 5. Tests - [ ] 5.1 Unit tests for Cloud API planner configuration loading (defaults, provider/model/timeout parsing) mirroring `tests/test_planner_config.py`. - [ ] 5.2 Unit tests for the planner-decision endpoint: authorized request resolves a decision, unauthenticated/foreign-host request is rejected, provider failure returns a structured error without crashing. - [ ] 5.3 Unit tests for `CloudProxyToolCallingClient`: successful decision round-trip, and each failure mode raises `ToolCallUnavailable`. - [ ] 5.4 Unit tests for Host Agent wiring: `AI_PLANNER_TRANSPORT=cloud` constructs an `AIPlanner` using `CloudProxyToolCallingClient`; `AI_PLANNER_TRANSPORT` unset or `direct` preserves existing direct-to-provider construction (no regression to the already-implemented default-enabled behavior). - [ ] 5.5 Full non-integration suite (`uv run --all-packages pytest -m "not integration"`) passes with no regressions. ## 6. Documentation - [ ] 6.1 Update `docs/CLOUD_DEPLOYMENT.md`'s Runtime AI Planner section with the `cloud` transport configuration path, its trade-offs (latency, cloud-availability coupling, expanded data path for screenshots/prompts), and the credential split (Cloud API holds provider keys for `cloud` transport; Host Agent holds them for `direct` transport). ## 7. Validation - [ ] 7.1 `openspec validate --strict` passes for this change. - [ ] 7.2 Ruff check/format and `compileall` pass for all touched packages.