diff --git a/openspec/changes/cloud-planner-proxy/.openspec.yaml b/openspec/changes/archive/2026-07-14-cloud-planner-proxy/.openspec.yaml similarity index 100% rename from openspec/changes/cloud-planner-proxy/.openspec.yaml rename to openspec/changes/archive/2026-07-14-cloud-planner-proxy/.openspec.yaml diff --git a/openspec/changes/cloud-planner-proxy/design.md b/openspec/changes/archive/2026-07-14-cloud-planner-proxy/design.md similarity index 100% rename from openspec/changes/cloud-planner-proxy/design.md rename to openspec/changes/archive/2026-07-14-cloud-planner-proxy/design.md diff --git a/openspec/changes/cloud-planner-proxy/proposal.md b/openspec/changes/archive/2026-07-14-cloud-planner-proxy/proposal.md similarity index 100% rename from openspec/changes/cloud-planner-proxy/proposal.md rename to openspec/changes/archive/2026-07-14-cloud-planner-proxy/proposal.md diff --git a/openspec/changes/cloud-planner-proxy/specs/agent-runtime/spec.md b/openspec/changes/archive/2026-07-14-cloud-planner-proxy/specs/agent-runtime/spec.md similarity index 100% rename from openspec/changes/cloud-planner-proxy/specs/agent-runtime/spec.md rename to openspec/changes/archive/2026-07-14-cloud-planner-proxy/specs/agent-runtime/spec.md diff --git a/openspec/changes/cloud-planner-proxy/specs/cloud-planner-proxy/spec.md b/openspec/changes/archive/2026-07-14-cloud-planner-proxy/specs/cloud-planner-proxy/spec.md similarity index 100% rename from openspec/changes/cloud-planner-proxy/specs/cloud-planner-proxy/spec.md rename to openspec/changes/archive/2026-07-14-cloud-planner-proxy/specs/cloud-planner-proxy/spec.md diff --git a/openspec/changes/cloud-planner-proxy/tasks.md b/openspec/changes/archive/2026-07-14-cloud-planner-proxy/tasks.md similarity index 100% rename from openspec/changes/cloud-planner-proxy/tasks.md rename to openspec/changes/archive/2026-07-14-cloud-planner-proxy/tasks.md diff --git a/openspec/specs/agent-runtime/spec.md b/openspec/specs/agent-runtime/spec.md index fa9003c..7006859 100644 --- a/openspec/specs/agent-runtime/spec.md +++ b/openspec/specs/agent-runtime/spec.md @@ -42,9 +42,16 @@ tool. ### Requirement: Pluggable dual-provider tool-calling abstraction The system SHALL support at least two interchangeable LLM providers -(Anthropic native tool use and OpenAI function calling) for the AI Planner's -decision calls, selectable via configuration, with both providers -constrained to return exactly one tool call per request. +(Anthropic native tool use and OpenAI function calling) for the AI +Planner's decision calls, selectable via configuration, with both providers +constrained to return exactly one tool call per request. Independently of +provider selection, the system SHALL support at least two transports for +making that decision call -- direct-to-provider (the tool-calling client +calls the provider's SDK itself, using locally configured credentials) and +cloud-proxy (the tool-calling client calls the Cloud Control Plane's +planner-decision endpoint, which calls the provider using cloud-held +credentials) -- selectable via configuration without requiring any change +to `AIPlanner`'s own decision logic. #### Scenario: Provider selected via configuration - **WHEN** the AI Planner is configured with a given provider identifier @@ -57,6 +64,29 @@ constrained to return exactly one tool call per request. - **THEN** the response is parsed into exactly one tool name and one arguments object, regardless of which provider produced it +#### Scenario: Transport selected via configuration +- **WHEN** the Host Agent is configured with a given transport (direct or + cloud-proxy) +- **THEN** `AIPlanner` is constructed with the tool-calling client for that + transport, and its own decision logic is unchanged regardless of which + transport is in effect + +#### Scenario: Cloud-proxy transport is the default +- **WHEN** no transport is explicitly configured +- **THEN** the AI Planner uses the cloud-proxy transport and the Cloud + Control Plane's planner-decision endpoint + +#### Scenario: Direct transport remains available by explicit configuration +- **WHEN** the Host Agent is configured with the direct transport +- **THEN** the AI Planner uses the direct-to-provider transport with locally + configured credentials + +#### Scenario: Cloud-proxy transport resolves a decision without a local provider client +- **WHEN** the Host Agent is configured with the cloud-proxy transport +- **THEN** its tool-calling client sends the decision request to the Cloud + Control Plane's planner-decision endpoint instead of constructing a local + Anthropic or OpenAI SDK client + ### Requirement: AI Planner is disabled by default and additive to the existing Planner The system SHALL default to the existing non-LLM Planner unless the AI Planner is explicitly enabled via configuration, and SHALL NOT alter the diff --git a/openspec/specs/cloud-planner-proxy/spec.md b/openspec/specs/cloud-planner-proxy/spec.md new file mode 100644 index 0000000..416710a --- /dev/null +++ b/openspec/specs/cloud-planner-proxy/spec.md @@ -0,0 +1,67 @@ +# cloud-planner-proxy Specification + +## Purpose +Define the Cloud Control Plane's planner-decision proxy endpoint, which +allows a Host Agent to delegate AI Planner LLM calls to the cloud using +cloud-held provider credentials, removing the need for Host Agent-held +provider API keys. + +## Requirements + +### Requirement: Cloud Control Plane exposes an authenticated planner-decision endpoint +The Cloud Control Plane SHALL expose an internal endpoint that accepts an AI Planner tool-calling decision request from an authenticated Host Agent, and SHALL require the same host-scoped bearer credential already used for heartbeat, claim, lease renewal, and result reporting -- no separate credential or enrollment step. + +#### Scenario: Authenticated host requests a planner decision +- **WHEN** a Host Agent presents its existing valid host-scoped credentials + with a planner-decision request +- **THEN** the Cloud Control Plane accepts and processes the request for + that host + +#### Scenario: Unauthenticated or foreign-host request is rejected +- **WHEN** a request omits valid host-scoped credentials, or presents + credentials bound to a different host than the one referenced in the + request +- **THEN** the Cloud Control Plane rejects the request without invoking any + LLM provider + +### Requirement: Endpoint resolves exactly one tool-call decision using cloud-held provider configuration +The Cloud Control Plane SHALL use its own configured LLM provider, model, and credentials -- not any value supplied by the requesting Host Agent -- to resolve a planner-decision request to exactly one tool name and one arguments object, within the request's timeout. + +#### Scenario: Provider returns a usable decision +- **WHEN** the configured provider responds to a planner-decision request + with a tool call +- **THEN** the Cloud Control Plane returns exactly one resolved tool name + and arguments object to the requesting Host Agent + +#### Scenario: Configured provider is unreachable or misconfigured +- **WHEN** the Cloud Control Plane's configured provider call fails (for + example, invalid credentials, provider error, or timeout) +- **THEN** the endpoint returns a structured failure response rather than a + fabricated decision, and does not crash the Cloud Control Plane process + +### Requirement: Cloud-proxy transport removes the need for Host Agent-held provider credentials +A Host Agent using the cloud-proxy transport for its AI Planner SHALL be able to execute AI-planned tasks without any locally configured LLM provider API key. + +#### Scenario: Host configured for cloud-proxy transport has no local provider key +- **WHEN** a Host Agent is configured to use the cloud-proxy transport and + has no `ANTHROPIC_API_KEY`/`OPENAI_API_KEY` set in its own environment +- **THEN** it can still obtain AI Planner decisions by calling the Cloud + Control Plane's planner-decision endpoint + +### Requirement: Planner-decision requests are not durably persisted +The Cloud Control Plane SHALL process planner-decision requests, including any screenshot and prompt text they carry, without durably persisting that screenshot or prompt content; only request metadata (such as host identifier, resolved tool name, latency, and error classification) may be retained for observability. + +#### Scenario: Request handling completes without storing prompt or screenshot content +- **WHEN** the Cloud Control Plane finishes handling a planner-decision + request +- **THEN** the raw prompt text and screenshot bytes from that request are + not present in any durable store or log the Cloud Control Plane retains + +### Requirement: Planner-proxy failures do not silently substitute a default action +The Cloud Control Plane SHALL report a failure to the requesting Host Agent when it cannot resolve a planner-decision request to a valid tool call, rather than returning a default, guessed, or previously cached decision. + +#### Scenario: Endpoint cannot resolve a decision +- **WHEN** the configured provider does not return a usable tool call for a + planner-decision request +- **THEN** the Cloud Control Plane's response indicates failure, and the + requesting Host Agent treats the planner call for that turn as failed