You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
session.get() does not expose the session model #54895
Some way to read the current model, e.g. session.model: Model.Ref ({ providerID, id }), matching what ctx.session.switchModel({ sessionID, model }) accepts.
Why this matters
Plugins cannot implement "if the session is not on the pinned model, switch it" safely. The current workaround is to call switchModel unconditionally on every prompt, which issues a redundant model switch per message. A more serious symptom of the same gap: a pin plugin written against v1 that checked session.model would become a silent no-op forever, with no error — session.model is simply undefined, so every guard clause trips.
Type definition
@opencode/plugin@2.0.27 — Session has readonly agent, cost, tokens, time, location, but no model.
Session.Info in @opencode/schema/session does have model?: Model.Ref ({ providerID, id, variant? }), and ctx.session.get() returns it unchanged. It is only stored once a model has been picked explicitly for the session, through switchModel or session.create({ model }). A session running on its agent's model or the default model has no stored model, so you get undefined.
ctx.session.switchModel() does nothing if the session is already on that model (same provider, id and variant), so no event or extra switch happens. Calling it on every prompt only switches once, so a pin plugin like yours works today without the guard:
The part that is really missing: a plugin can't read the model a session will actually use when none was picked explicitly. Today it would have to work that out itself, from session.model, then the agent's model, then ctx.model.default(). I've marked this as a feature request to expose that resolved model, e.g. on the prompt hook input or through a session method. Maintainers will consider it.
Confirmed — my report was imprecise and this correction makes the report accurate.
Session.Info does have model?: Model.Ref and ctx.session.get() returns it. It is only populated when a model was picked explicitly (switchModel or session.create({ model })), so a session running on its agent's model or the default model has no stored model. That is why the field reads undefined in our setup.
That also explains the failure mode we hit. A pin plugin written as:
constsession=awaitctx.session.get({ sessionID })if(!pin||!session?.model)return// never true-safeawaitctx.session.switchModel({ sessionID,model: pin})
becomes a silent no-op on every prompt, with no error and no warning, because the guard trips forever. The fix is to drop the session?.model check and switch unconditionally — which is safe, since switchModel is a no-op when already on that model.
So the gap is exactly as you framed it: a plugin cannot read the resolved model. It has to reconstruct it itself — session.model, else the agent's configured model, else ctx.model.default(). Exposing that resolved value on the prompt hook, or through a session method, would remove both the silent-no-op class of bug and that reconstruction.
For reference, the resolved chain you described matches what we observe end to end: agents.*.model is read by the config layer but not applied on the run --agent path (tracked in #42561), which is what pushed us to a switchModel plugin in the first place.
Activity
opencode-agent commentedon Oct 12, 2026
Thanks for the detailed report, @osrt91!
Some context first:
Session.Infoin@opencode/schema/sessiondoes havemodel?: Model.Ref({ providerID, id, variant? }), andctx.session.get()returns it unchanged. It is only stored once a model has been picked explicitly for the session, throughswitchModelorsession.create({ model }). A session running on its agent's model or the default model has no storedmodel, so you getundefined.ctx.session.switchModel()does nothing if the session is already on that model (same provider, id and variant), so no event or extra switch happens. Calling it on every prompt only switches once, so a pin plugin like yours works today without the guard:The part that is really missing: a plugin can't read the model a session will actually use when none was picked explicitly. Today it would have to work that out itself, from
session.model, then the agent's model, thenctx.model.default(). I've marked this as a feature request to expose that resolved model, e.g. on theprompthook input or through a session method. Maintainers will consider it.Related issues:
osrt91 commentedon Oct 12, 2026
Confirmed — my report was imprecise and this correction makes the report accurate.
Session.Infodoes havemodel?: Model.Refandctx.session.get()returns it. It is only populated when a model was picked explicitly (switchModelorsession.create({ model })), so a session running on its agent's model or the default model has no storedmodel. That is why the field readsundefinedin our setup.That also explains the failure mode we hit. A pin plugin written as:
becomes a silent no-op on every prompt, with no error and no warning, because the guard trips forever. The fix is to drop the
session?.modelcheck and switch unconditionally — which is safe, sinceswitchModelis a no-op when already on that model.So the gap is exactly as you framed it: a plugin cannot read the resolved model. It has to reconstruct it itself —
session.model, else the agent's configured model, elsectx.model.default(). Exposing that resolved value on theprompthook, or through a session method, would remove both the silent-no-op class of bug and that reconstruction.For reference, the resolved chain you described matches what we observe end to end:
agents.*.modelis read by the config layer but not applied on therun --agentpath (tracked in #42561), which is what pushed us to aswitchModelplugin in the first place.