show the model that answered instead of the bare harness string
the picker rendered the literal `claude-code` for any chat that named no
tier. that is DEFAULT_MODEL — the bare harness — and it routes correctly
(no --model is passed, so the cli picks for itself) but names no catalogue
entry, so radix matched no item and the fallback from 773f0e1c printed the
raw string's last segment.
officer cannot answer this because it deliberately did not choose. the
harness does, in its system.init `model` field, which the parser was
discarding. so: emit it as a `model:resolved` chat event, translate it to an
officer id in turn-stream, and forward it to the client only when the turn
named no tier.
that last condition is the load-bearing one. the alias map is lossy in one
direction — opus[1m] and opusplan both answer as claude-opus-5 — so
forwarding unconditionally would silently relabel a user's own choice as
plain opus.
display only, and three ways: not durable (like session:claude), never
recorded to chat_session_models, and never written into the client's
selectedModel or the reattach ref. storing it would pin a session to
whatever the cli defaulted to on its first turn, which is the freedom the
bare harness exists to preserve.
untested against a live turn — the parser and translator have unit tests,
the websocket gate does not.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -415,6 +415,28 @@ function createMessageHandler(sessionId: string, model: string, resumeSessionId?
|
||||
record();
|
||||
}
|
||||
|
||||
/*
|
||||
What the harness reports it is running — forwarded ONLY when the turn did not name a tier.
|
||||
|
||||
`effective` without a `/` is the bare harness, `claude-code`: the client chose nothing, officer passed
|
||||
no `--model`, and the CLI picked. That is the only case where the harness knows something officer does
|
||||
not, and it is the only case where this may speak.
|
||||
|
||||
When the turn DID name a tier, this is dropped on the floor, because the translation back from an API
|
||||
id is lossy in exactly the directions that matter: `claude-code/opus[1m]` and `claude-code/opusplan`
|
||||
both answer as `claude-opus-5`, so forwarding would quietly relabel the user's own choice as plain
|
||||
opus. `model:switched` already covers a real change.
|
||||
|
||||
Not recorded, not folded, not `sessionManager.setModel`. `session.model` is what dispatch and the stop
|
||||
path route on, and it is correct as the bare harness; and `record()` refuses a model without a `/` for
|
||||
a reason worth reading before changing any of this.
|
||||
*/
|
||||
if (msg.type === 'model:resolved') {
|
||||
if (effective.includes('/')) return;
|
||||
for (const socket of session.sockets) sendToClient(socket as ServerWebSocket<WSData>, msg, seq);
|
||||
return;
|
||||
}
|
||||
|
||||
// Follow the harness, never the request — a refused switch (`ok: false`) leaves the session where it was,
|
||||
// and recording the attempt would restore a mode the agent is not in.
|
||||
if (msg.type === 'plan:mode' && msg.ok) {
|
||||
|
||||
Reference in New Issue
Block a user