Change: This is S-2 of the agent prompt-caching build (Epic-2050,
RFD docs/notes/rfd-agent-caching-strategy.md §6.2 / D-6). S-1 landed the
per-seat cache policy as config (cache_policy_for(seat)); S-2 adds the
one wiring point that consumes it — the provider layer. It introduces
model_settings(seat) (the single settings object carrying ZDR and the
seat's cache policy), cache_point(seat) (the breakpoint marker prompt builders
drop into the sandwich), and session_id plumbing, as
byte-identical copies across the agents that carry them (Mira, Vega). Per-agent
divergence stays in config.py/llm.yaml, never in the wiring.
| File | Role |
|---|---|
agents/mira/provider.py, agents/framer/provider.py | The wiring, byte-identical: model_settings(seat, *, session_id=None), cache_point(seat), and the shared _seat_emits_cache_markers(seat) guard. Both delegate to the sibling config.py, so AGENT_IS_AMBIENT and the seat policies (the difference) live in config, not here. |
agents/mira/tests/test_provider.py | Extends the ZDR test: a designed seat (converse) carries the cache breakpoint; ZDR is always present; session_id rides the header (and is capped at 256 chars); cache_point returns a marker; unknown seats fail open to cold. |
agents/framer/tests/test_provider.py (new) | The ambient guard on Vega: off seats are ZDR-only; a seat flipped on over a free-write vendor emits; the same seat over a paid-write vendor stays cold on BOTH surfaces; session_id still plumbs. |
tests/test_provider_cache_parity.py (new) | The byte-compare gate: the three wiring functions are byte-identical (via ast source segments) across Mira and Vega. Fails on a single byte of drift. |
model_settings(seat) is the D-6 "one wiring point". ZDR
(zdr/data_collection=deny) and real-cost usage are
unconditional — a cache flag can never loosen the provider filter. The
instructions-cache breakpoint (openrouter_cache_instructions = the seat's TTL) is
added only when the seat emits markers. This is the ZDR lesson applied to cache: put
both in one place so no call site can carry one and forget the other.
settings = {
"openrouter_provider": {"zdr": True, "data_collection": "deny"},
"openrouter_usage": {"include": True},
}
if _seat_emits_cache_markers(seat):
settings["openrouter_cache_instructions"] = cache_policy_for(seat).ttl
if session_id is not None:
settings["extra_headers"] = {"x-session-id": session_id[:256]}
_seat_emits_cache_markers(seat) is the ONE decision
model_settings and cache_point share — a seat that must stay cold stays
cold on both. It is fail-open (an off/unknown seat never even resolves a model), and it applies
S-1's ambientxpaid-write guard verbatim (CachePolicy.emits_markers): a non-off seat
on an ambient agent whose resolved provider is paid-write emits nothing (a write
premium on a prefix never re-read within TTL loses money, §7.4).
The RFD left the exact SDK surface for S-2 to pin. pydantic-ai 1.107's
OpenRouterModelSettings exposes cache fields
(openrouter_cache_instructions/_messages/_tool_definitions,
each bool | '5m' | '1h') but no first-class session field, so
per §6.2 the sticky id rides the documented x-session-id header via
extra_headers (≤256 chars). Converse passes the chat-session id, panel passes
run_id, ambient passes nothing.
| AC | Proof |
|---|---|
| settings object carries ZDR + cache fields | test_model_settings_always_carries_zdr_and_usage + test_designed_seat_carries_cache_breakpoint (Mira) — one object, both concerns. |
| no per-agent divergence (byte-compare test like today's provider parity) | tests/test_provider_cache_parity.py::test_shared_wiring_functions_are_byte_identical_across_agents. |
cd agents/mira && uv run pytest tests -q # 116 passed cd agents/framer && uv run pytest tests -q # 112 passed uv run pytest tests/ -q -m unit # 171 passed (governance, incl. parity gate) uv run ruff check agents/ tests/ # All checks passed!
Wiring primitive only: model_settings(seat) exists and is tested, but
the call sites still pass ZDR_SETTINGS — swapping converse (S-3) and the panel (S-8)
to it, and placing the cache_point markers in the sandwich, are the downstream
enablement stories that depend on this one. No cache marker reaches the wire yet; ZDR behaviour
is unchanged; no CodeZip rebuild or AgentCore redeploy is required.