Skip to content

BYOK: built-in agents (explore, task, research) never use their default models and silently run on the session model #5126

Open

Description

@SivaKesava1

Describe the bug

Copilot CLI's built-in agents declare their own models in their definitions (unchanged from 1.0.90
to 1.0.96):

Agent Default models, in order Effort
explore gpt-5.6-luna, gpt-5.4-mini low
task gpt-5.6-luna, gpt-5.4-mini, claude-haiku-4.5 -
research claude-sonnet-5 -

In a normal signed-in session these models are used, so a quick explore search runs on a small
model. In a BYOK session they are never used. Every built-in agent runs on the session's
model, even when the BYOK provider serves the default model and the session lists it.

As far as we can tell, the cause is the check that a definition's model is "available" in the
session. Under BYOK it never passes:

The fallback is silent and misreported. It isn't logged at the default log level, and the
subagent.started and subagent.completed events report
"modelSelectionSource": "agent_definition_default" with model set to the session model. That
reads as if the definition chose that model.

Why it matters. A BYOK session on an expensive model makes every built-in agent expensive too.
GitHub Agentic Workflows (github/gh-aw) runs Copilot through a proxy configured as a BYOK provider
and picks the session model per task. On runs on Claude Opus, explore and research used 49–58%
of the run's AI credits, all on Opus, although their definitions ask for Luna, mini and Sonnet.
explore is also slower on a large model.

The provider does serve the default model. As a diagnostic, setting
subagents.agents.explore.model in ~/.copilot/settings.json makes explore run on Luna through
the same provider (modelSelectionSource: "configured_preference"). We don't want to rely on that
setting as a fix: it skips the cost guard (a Sol preference in a Luna session ran on Sol), so every
integrator would have to re-implement the runtime's model choice and cost rule.

Affected version

GitHub Copilot CLI 1.0.96, and @github/copilot-sdk 1.0.19 against copilot --headless 1.0.96. Also seen on 1.0.90 and 1.0.95.

Steps to reproduce the behavior

CLI mode (BYOK straight to the Copilot API, no proxy):

mkdir demo && cd demo
printf 'def add_numbers(a, b):\n    return a + b\n' > math_utils.py
export COPILOT_OFFLINE=true \
  COPILOT_PROVIDER_BASE_URL=https://api.githubcopilot.com \
  COPILOT_PROVIDER_TYPE=openai \
  COPILOT_PROVIDER_WIRE_API=responses \
  COPILOT_PROVIDER_BEARER_TOKEN="$(gh auth token)" \
  COPILOT_MODEL=gpt-5.6-sol
copilot --allow-all-tools --no-ask-user --log-level all --log-dir ./logs -p \
  'Use the task tool with agent_type "explore" to find which file defines add_numbers. Do not search yourself.'

Observed: the output shows ● Explore (model: gpt-5.6-sol), and the log's subagent.started
event has "model":"gpt-5.6-sol","taskModelSource":"unset","modelSelectionSource":"agent_definition_default".

SDK mode: start copilot --headless --port <port> and create a session with these options:

{
  "model": "copilot/gpt-5.6-sol",
  "providers": [{ "name": "copilot", "type": "openai", "wireApi": "responses",
                  "baseUrl": "https://api.githubcopilot.com", "bearerToken": "<token>",
                  "headers": { "Copilot-Integration-Id": "copilot-developer-cli" } }],
  "models": [{ "id": "gpt-5.6-sol", "provider": "copilot" },
             { "id": "gpt-5.6-luna", "provider": "copilot" },
             { "id": "gpt-5.4-mini", "provider": "copilot" }]
}

Send the same prompt.

Observed: subagent.started has "model":"copilot/gpt-5.6-sol","modelSelectionSource":"agent_definition_default",
and every request goes to gpt-5.6-sol. This happens although copilot/gpt-5.6-luna is one of the
session's models.

Control (shows the provider serves Luna): add
{"subagents":{"agents":{"explore":{"model":"gpt-5.6-luna"}}}} to ~/.copilot/settings.json and
rerun the CLI steps. ● Explore (model: gpt-5.6-luna), with
"modelSelectionSource":"configured_preference".

Expected behavior

  1. Under BYOK, a built-in agent uses its definition's model when the session can serve it, just as
    in a signed-in session:
    • SDK mode: match the definition's short name to the session's provider-qualified model
      (gpt-5.6-luna → copilot/gpt-5.6-luna).
    • CLI mode: don't treat COPILOT_MODEL as the only available model. The session needs a way
      to know the provider's other models; Feature request: opt-in model discovery for BYOK / custom providers #3795 (BYOK model discovery) would provide one.
  2. The existing cost guard keeps applying, so a default that costs more than the session model
    (research → Sonnet under Luna) is still skipped. It should also work under BYOK, where pricing
    may have to come from the model id.
  3. A default the session can't reach is skipped, not tried and failed. For example, in CLI mode a
    Claude default under a GPT session model is on the other wire API (BYOK: sub-agents always use the session's wire API, so a sub-agent on a model from the other family fails with a 400 #5103).
  4. When the agent does fall back to the session model, the fallback is visible: logged at info or
    warning level, and reported in subagent.started with its own modelSelectionSource value or a
    fallback reason, not as agent_definition_default.

Additional context

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

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions