HELIX

ADR-0003 — JSON-RPC 2.0 over HTTP + SSE, with capability negotiation

Context

With the Runtime/Embedder split fixed, the wire format had to be chosen. The constraint from G6 (“implementable in about a week”) argues against anything exotic; the constraint from G1/G2 (portability of both sides) argues for primitives that existing infrastructure — proxies, gateways, observability — already understands.

Decision

Be deliberately boring on the wire. Boring is portable.

JSON-RPC 2.0 + SSE is the same family of primitives MCP and A2A already use, so AEP slots into existing infrastructure without special handling.

Capability negotiation

A session opens with an initialize handshake: the embedder requests a capability set, the runtime responds with what it actually supports. v1 defines eight negotiated booleans:

auth.oauth2, compliance.gdpr, tools.mcp_external, self.modify_prompt, self.spawn_subagent, self.learning_memory, messaging.peer, streaming.sse.

Using a capability the runtime did not advertise fails cleanly with capability_not_supported (-32002).

This is the mechanism that lets a minimal runtime (tool execution only) and a full runtime (self-improving, multi-agent, GDPR-compliant) speak the same protocol and degrade predictably. A runtime without GDPR support advertises compliance.gdpr: false and rejects every subject.* call — no guesswork for the embedder, no capability sniffing.

Event durability

Run events carry a monotonically increasing event_id per run and are retained at least 24 hours, so an embedder can reconnect with ?since=<event_id> and miss nothing. This is what makes “the agent ran for two days, here is everything it did” a protocol guarantee rather than a product feature.

Consequences