session.get() does not expose the session model · Issue #54895 · anomalyco/opencode · GitHub
Skip to content

session.get() does not expose the session model #54895

Open

Description

@osrt91

What happens

ctx.session.get({ sessionID }) does not return a model field, so a plugin cannot read which model a session is currently using.

Observed keys

A plugin logging the returned object:

id, projectID, agent, cost, tokens, time, location
[prompt] event.agent   = undefined
[prompt] session.agent = Astra-Plan
[prompt] session.model = undefined/undefined

Expected

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.

Environment

opencode v2.0.27, @opencode/plugin 2.0.27.

Activity

  1. opencode-agent commented on Oct 12, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report, @osrt91!

    Some context first:

    • 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:
    await ctx.session.switchModel({ sessionID, model: { providerID: "anthropic", id: "claude-sonnet-4-5" } })

    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.

    Related issues:

  2. osrt91 commented on Oct 12, 2026

    @osrt91
    Author

    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:

    const session = await ctx.session.get({ sessionID })
    if (!pin || !session?.model) return          // never true-safe
    await ctx.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.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions