ARES

ADR-0006: JaegerAI is reached through its contract, never imported

Status: Accepted Date: 2026-08-12 Extends: ADR-0001 (workers are out-of-process)

“Note on transport” is corrected by ADR-0007. It cited ADR-0005 as settled precedent; ADR-0005 was inaccurate when written. It also applied a browser↔ARES streaming decision to the ARES↔Jaeger boundary — different boundaries. The decision below is unaffected.

The “baseline daemon interface” claim is corrected by ADR-0008. JaegerAI ships no HTTP gateway; that path is vestigial, and ARES↔Jaeger is stdio-bridge-only in practice. The decision below is unaffected.

Context

Three AI agents independently proposed three conflicting architectures for how ARES should reach JaegerAI — protocol-plus-WebSocket, in-process library import of Jaeger’s agent core, and a library/service split — and produced competing in-flight work.

All three re-derived a question ADR-0001 had already answered. That ADR was deleted during the docs reorganization, taking the reasoning with it; without the record, “just import it, it’s faster” looked like a fresh insight rather than a reversal. This ADR exists as much to restore the boundary as to extend it. (The directory has been restored; deleting an accepted ADR is how a deliberate decision gets silently reversed.)

What is genuinely new since ADR-0001: JaegerAI now ships its own Swift client (jaeger_ai/interfaces/swift/Sources/JaegerOS/Bridge/BridgeProcess.swift), which spawns its own bridge process and speaks the same NDJSON frames ARES does. Two independent clients of one agent is a case ADR-0001 did not consider.

Decision

ARES accesses JaegerAI exclusively through its versioned client contract (JrosClient) and does not import its agent core. Jaeger may expose several transport bindings over that one contract; the existing HTTP/SSE gateway (ARES_JAEGER_GATEWAY_URL, bearer auth, GET /v1/health, POST /v1/reset) is the baseline, with stdio retained as the local fallback.

A protocol boundary, a daemon, and a specific transport are three separate decisions. Only the first is settled here.

Consequences

Good:

Costs:

Adopted from the rejected proposal

Layering Jaeger’s agent loop away from PySide6, from llama-cpp loading, and from any specific transport is textbook ports-and-adapters and is endorsed as JaegerAI-internal work. It is what makes multiple transport bindings and a swappable model backend clean. Only its conclusion — that ARES should then import that core — is rejected. Good internal layering and external protocol access are complementary.

Deliberately not decided here

Each needs its own ADR; none is authorised by this one.

Note on transport

ADR-0005 already chose SSE over WebSocket and set the bar for revisiting: streaming becoming “genuinely bidirectional and latency-sensitive.” Mid-turn approval prompts (request frames) are the strongest candidate, but that case has not been made with measurements. Do not add a WebSocket transport on the grounds that Jaeger’s protocol docs mention one. ADR-0005 also warns that realtime.py’s existing WebSocket route “should not be treated as a second parallel implementation without a product decision” — that still holds.

Revisit if

Jaeger stops shipping non-Python clients and the multi-worker requirement in ADR-0001 disappears — both, not either. Startup latency alone is not sufficient grounds; it is an argument for a warm daemon, which this ADR leaves open, not for dissolving the boundary.