Change: This is S-8 of the agent prompt-caching build (Epic-2050,
RFD docs/notes/rfd-agent-caching-strategy.md §7.3 / §4). S-2 landed the
wiring primitive (model_settings(seat), cache_point(seat),
session_id plumbing) but left the panel call sites on the plain
ZDR_SETTINGS. S-8 flips Mira's command panel onto that wiring: the
four panel seats (the proposer roster, the critic, the synthesizer) now carry the
within_run cache policy on every call, keyed by session_id = run_id.
Ambient agents (Vega/Blake) are untouched — they never pay cache writes (that is S-9).
Caches are per-provider-per-model, and a panel run is one call per model — so cross-seat
reads on today's diverse roster are ~zero. The two real within-run wins are: a
same-model pair (e.g. minimax as both a proposer and the synthesizer) reading
the shared context prefix warm, and a retried seat re-sending its identical
prompt. This story ships the wiring for both; it does not promise a hit-rate (the S-4 live
probe measures realized reads). What pinning always buys the panel is correctness-shaped: ONE
nonce and ONE session_id per run, so every seat pins one frame — provenance
never forks between draft and final.
| File | Role |
|---|---|
agents/mira/panel_prompts.py | The three seat builders (build_proposer_prompt/build_critic_prompt/build_synthesizer_prompt) now return a list[str | CachePoint] sandwich: the shared context prefix, a cache_point(seat) breakpoint, then the per-seat suffix (drafts / critique / instruction). prompt_text() is the plain-string view for structural tests. |
agents/mira/panel.py | Each seat call passes model_settings(seat, session_id=run_id) (ZDR + the seat's cache breakpoint + the sticky x-session-id header); the @cached role-agents bake model_settings(role) instead of ZDR_SETTINGS. run_command_panel(data, *, session_id=None) threads the run's sticky key to every seat and its retries. |
agents/mira/command.py, agents/mira/runtime.py | run_command(data, *, run_id=None) passes the agent_run_id down as the panel session_id. The broker seam already carries run_id; it just reaches the panel now. |
agents/mira/tests/test_panel_cache.py (new) | The acceptance suite: a CachePoint on every seat prompt (absent when the seat is off); session_id = run_id + ZDR on every call; a retried seat re-sends the same cacheable prompt under the same session; a different run gets a different session (cold across runs); the shared context prefix is byte-identical across seats. |
agents/mira/tests/conftest.py, agents/mira/evals/adversarial.py | _user_text now flattens a [str, CachePoint, str] UserPromptPart (pydantic-ai stores a sequence prompt as one part with list content), so the panel stub + the injection-wall check still read all walled text through the sandwich shape. |
The breakpoint sits at the stable/volatile boundary: the shared context (matter meta,
command, current frame, locks, notes, docs) is byte-identical across every seat and across a
seat's retries; the per-seat suffix is not. cache_point(seat) returns
None when the seat is cold (policy off), so the prompt shape is
unchanged when caching is off — caching is economics, not correctness.
def _sandwich(seat, context_parts, suffix_parts):
seq = ["\n\n".join(context_parts)]
bp = cache_point(seat) # None when the seat is cold -> no marker, no drift
if bp is not None:
seq.append(bp) # closes the shared, cacheable prefix
seq.append("\n\n".join(suffix_parts))
return seq
One session_id per run (the agent_run_id) rides the sticky
x-session-id header on every seat call and its retries, so a re-send lands on the
same provider endpoint and reads the cached prefix warm. It is never carried across runs —
each run has a fresh agent_run_id — so cross-run seats stay cold. That is the
within_run policy, not designed cross-run caching (§4). ZDR rides the same
settings object, so cache can never be split from ZDR.
| AC | Proof |
|---|---|
provenance still one version_hash across seats | test_shared_context_prefix_is_identical_across_seats — one nonce, one context prefix for proposer/critic/synth, so every seat pins one frame. |
| measured within-run reads reported (even if ~0 — honesty over hope) | The wiring is asserted, not a hit-rate: test_session_id_is_run_id_on_every_seat (marker + sticky key + ZDR on every seat) and test_retry_re_sends_the_same_cacheable_prompt_within_the_run (the retry re-uses the identical CachePoint prompt under the same session). Realized reads are the S-4 live probe's job; the S-5 model.usage cache fields (merged, pramaan-agents#201) carry them on the wire when they land. |
uv run --package mira pytest agents/mira/tests -q # 139 passed uv run --package mira python agents/mira/evals/run.py adversarial # 13 held / 0 failures uv run pytest tests -q # 175 passed (governance) uv run ruff check agents/mira tests/ scripts/ # All checks passed!
Enablement only, no new egress: no CodeZip rebuild or AgentCore redeploy needed
for the wiring; the panel emits cache markers on the wire now, but reads are earned only where a
provider endpoint serves a cache (S-4). Ambient agents (Vega/Blake) are untouched by this story
— their no-paid-writes audit is S-9. llm.yaml already carries the panel seats'
within_run policy from S-1; this story consumes it.