Skip to content

gap: no in-session model switch — expose Session.switchModel in-session, or a switch-request path to the parent/launcher #1886

Closed

Description

@dezren39

Finding

A session cannot change its own model/route. The mechanism exists one level up — Session.switchModel in core, used by the command/API path (POST /api/session/:id/model → 204, next turn runs under the new route) — but no in-session tool exposes it, and nothing tells the session that.

Workshop evidence

Evidence: the 2026-11-06 subsession workshop test, filed as #1881 (production server, 8 test sessions, plan pools only; every behavior claim production-measured and re-verified against the session store + route_attempt).

Options to decide between

  1. In-session switch tool wrapping Session.switchModel, with the prompt-cache warning in its description (a switch invalidates the prompt cache for the new model — sessions should switch rarely and deliberately).
  2. Switch-request path: the session emits a request its parent/launcher performs via the API (keeps the authority outside, makes the intent visible in the tree).
  3. Docs-only floor (if both are rejected by design): base prompt + tool descriptions state plainly that sessions cannot change their own model and must ask their launcher — silence today produces improvisation against the wrong API.

Acceptance sketch

  • One of the options is implemented and documented; a session asked to switch itself either succeeds via the sanctioned path or answers with the documented procedure — never an improvised wrong-API call.
  • Cache-invalidation consequence is stated wherever the mechanism is described.

Acceptance criteria

  • A session can either invoke an in-session model/route switch backed by the existing Session.switchModel, or has a documented switch-request path to its parent/launcher — and the tool/prompt surface says which.
  • A session asked to switch no longer improvises with unrelated tools (the workshop's session_move directory attempt).
  • Switch semantics (next-turn effect, cache loss) are stated where the capability is exposed.

Activity

  1. added
    kind/featureNew feature or capability request
    priority/normalOrdinary priority (the default)
    effort/mMedium: expected under 1 day of work
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Nov 6, 2026
  2. dezren39 commented on Nov 6, 2026

    @dezren39
    Author

    Intake review

    Part of the workshop cluster from opencode issue #1881 (Q5: API switch works and is instant; no in-session exposure). Related: issue #1891 (serving-leaf observability — a switch you cannot observe is half a feature). Risks: mid-session switches destroy prompt cache (per fleet detached rules) — the exposure should say so. Suggested approach: thin tool wrapper over Session.switchModel with the cache warning, or an explicit parent-request message type.

    — fleet monitor intake (Duty 2), 2026-11-06 run. Acceptance criteria appended to the issue body; labels normalized.

  3. dezren39 commented on Nov 7, 2026

    @dezren39
    Author

    Triage evidence — CONFIRMED by reading current main (issue-sweep-rotating)

    Checked 2026-11-07T05:54Z against main cfc5e3d82cd2e9c0d6e304cdc6644218f26d8905 (fresh gh api raw reads, verified twice):

    • The mechanism exists in core. packages/core/src/session/session.ts:137 — Session.switchModel is a real operation (early-returns when the model is unchanged; for child sessions it enforces subtree governance via ModelGovernance.assertAllowed, lines 148-160), exported in the session operations (line 420) and bound into the session handle (line 445). The API/command path uses it, per the issue (POST /api/session/:id/model → 204, next turn under the new route).
    • No in-session tool exposes it. packages/core/src/tool/plugin/opencode.ts (the session's own opencode tool surface): grep for switch returns zero matches (verified twice). The only tool-layer call to switchModel anywhere is packages/core/src/tool/plugin/subagent.ts:201 — a parent switching an existing child at continuation time when the parent passes a model override. That is the launcher-side authority the issue describes, not a session switching itself.
    • Nothing tells the session any of this. The base prompt (packages/core/src/session/runner/prompt/system.txt, 756 bytes): grep for switch returns zero matches — no statement that self-switching is impossible, no documented request path. Silence is what produced the workshop behavior on the issue (Workshop test: subsession machinery from a workhorse session — results and system-prompt suggestions #1881 Q5: the test session improvised session_move — a directory move — got "Unable to move session", and reported SWITCH_METHOD: NONE_AVAILABLE).

    What "done" looks like (the issue's acceptance criteria — note the design choice is inside the issue, no Drew ruling needed): one of the three options is implemented and documented — (1) an in-session switch tool wrapping Session.switchModel, (2) a switch-request path to the parent/launcher, or (3) the docs-only floor stating plainly that sessions cannot switch themselves. Whichever is chosen: a session asked to switch either succeeds via the sanctioned path or answers with the documented procedure (never an improvised wrong-API call), and the prompt-cache consequence (a switch invalidates the prompt cache — switch rarely and deliberately) is stated wherever the mechanism is described. Related: #1891 (serving-leaf observability, fed as task 11040 this run) — a switch you cannot observe is half a feature.

    Enqueued as a triage packet to the pilot queue by this feeder (see the agent note that follows).

  4. dezren39 commented on Nov 7, 2026

    @dezren39
    Author

    Enqueued by issue-sweep-rotating (triage feeder) as queue task 11041 (repo-health-opencode-pilot, kind triage, priority 50, dedupe repo-health:v1:triage:developing-today/opencode:issue:1886). Triage evidence is in the comment above (6703021748): Session.switchModel exists in core (session.ts:137, governance-checked, exported/bound at 420/445) but no in-session tool exposes it (tool/plugin/opencode.ts grep switch = 0; the only tool-layer call is a parent switching a child, subagent.ts:201), and the base prompt is silent (system.txt grep switch = 0). The queue/queued label is applied in the same pass.

  5. added
    work/assignedAn agent holds this item via the queue (claimed task)
    queue/claimedA queue worker has claimed the task for this item
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    queue/claimedA queue worker has claimed the task for this item
    on Nov 8, 2026
  6. added a commit that references this issue on Nov 8, 2026
  7. removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    work/assignedAn agent holds this item via the queue (claimed task)
    on Nov 8, 2026
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

    effort/mMedium: expected under 1 day of workkind/featureNew feature or capability requestmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions