45646f4d09
Portal will serve anthropic/* from more than one upstream (OpenRouter passthrough today; GMI/Vertex once it is back online). The native Messages wire is the better transport but is only safe where the upstream keeps prompt-cache routing sticky: measured false on the OpenRouter path (14-20% of consecutive calls re-write the previous turn; #104284 moved the default to chat), untested on GMI. Hermes cannot see the upstream in the request, only in the response: OpenRouter stamps `provider` (chat wire) and mints `gen-<unix>-<rand>` ids; GMI/Vertex returns Anthropic-native `msg_...` ids and no provider. `auto` therefore starts every session on chat (correct on both upstreams), classifies the first response, and switches that session to native only when the upstream is GMI AND `agent/nous_wire.py::GMI_NATIVE_WIRE_CLEARED` is True. The switch is scheduled at response time and applied at the start of the next iteration (turn_iteration_prep), so nothing is rebuilt while a response is being consumed; it goes through switch_model so the client, cache policy and _primary_runtime stay consistent. One decision per session, call 1 only; unknown upstream never switches; a failed switch logs and stays. GMI_NATIVE_WIRE_CLEARED is False: until the 20x6 concurrency probe (evals/postmortem/live_ab) is clean on a GMI-served anthropic/* id on the native wire, `auto` behaves exactly like `chat`. Flipping it is the whole rollout once GMI is measured. Default stays `chat`. Tests (17): classifier on real Portal response shapes from both wires and both upstreams; chat for openrouter/unknown, GMI gated on the flag; one decision per session, call 1 only, explicit chat/native never auto-switch, other providers/models untouched, switch failure swallowed and final; record_response_usage on a real AIAgent invokes the hook once. Live (auto, real Portal, Fable 5.1): arm A, real classification (OpenRouter today) - stays on chat through a tool loop and a second turn, cache 97-99%. Arm B, classifier forced to gmi with the flag on - call 1 on chat, switch applied before call 2, calls 2-3 on the native wire in the same session, tool result and both turns correct, cache 97-99%. An earlier shape that switched inside the response path broke call 1 (SimpleNamespace has no .content); the scheduled apply is why.