Skip to content

Session resume restores the model from the last assistant message's echoed name, not from model_change #9243

Description

@pwguler

What happened?

Resuming a session restores the wrong model when the provider's response echoes a model name that differs from the id pi routed to.

getSessionContextSettings (packages/coding-agent/src/core/session-manager.ts:366-374) lets every assistant message overwrite model, so the last assistant message beats the last model_change. For anthropic-messages, message.model is whatever the upstream put in message_start (packages/ai/src/api/anthropic-messages.ts:598), not the catalog id. Behind a relay that namespaces its ids (catalog anthropic/claude-opus-5, forwarded upstream as claude-opus-5, Anthropic echoes claude-opus-5, relay passes it through) the session file ends up as

{"type":"model_change","provider":"relay","modelId":"anthropic/claude-opus-5"}
{"type":"message","message":{"role":"assistant","provider":"relay","model":"claude-opus-5", ...}}

and pi -c looks up the bare id, misses, and switches models:

Warning: Could not restore model relay/claude-opus-5. Using relay/anthropic/claude-opus-5

Only interactive mode shows that warning; -p and RPC fall back silently. I hit it on my own relay (pengepul) after it prefixed its ids to tell two upstreams apart that both serve a bare claude-opus-5. The relay could rewrite the echo too, but pi already records its own routing decision in model_change, and #9188 shows other proxies (new-api, one-api) echo the same way. Same root cause as #9188, different symptom: pinning message.model as proposed there fixes sessions written from then on, while files that already carry the echoed name keep restoring wrong. So I think the read side should stop treating message.model as the routing record either way.

Steps to reproduce

  1. models.json: provider relay, api: anthropic-messages, one model anthropic/claude-opus-5, baseUrl pointing at a stub that serves a normal SSE stream but sets message_start.message.model to claude-opus-5 (30-line node script, can attach).
  2. pi -ne -p "say ok"
  3. pi -ne -c prints the warning above. Validated with -ne; no extension involved.

Expected behavior

Restore uses the last model_change in the path and only falls back to the assistant message when the path has none (files older than 98c85bf, or embedders that never append one). As far as I can tell the CLI always writes one at session start (sdk.ts:381-384), so that covers it, but if there is a first-turn path without a model_change I would like to know before writing the fix. The alternative is the write side: keep model as the requested id and put the echo in responseModel, as openai-completions does. I can do either as a PR once approved. build-context.test.ts currently asserts the opposite precedence and would flip.

Version

0.85.1 (still present on main at 9767ba2)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    last-readA marker label for triaging

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions