Skip to content

SDK-provided CustomAgentConfig.model is dropped during session.create #1609

@mihaiLucian

Description

@mihaiLucian

Summary

CustomAgentConfig.model is accepted by the Node SDK and sent in customAgents, but the bundled runtime drops model while converting SDK-provided custom agents during session.create, causing custom subagents to inherit the parent session model.

This contradicts the installed SDK type documentation, which says:

Model identifier for this agent.
When set, the runtime will attempt to use this model for the agent,
falling back to the parent session model if unavailable.

The requested child models are available and enabled from client.listModels().

Environment

  • @github/copilot-sdk: 1.0.0
  • Copilot CLI/runtime: 1.0.59
  • Node: 22.20.0
  • OS: Windows
  • Parent/session models tested: gpt-5.4, gpt-5-mini, gpt-5.4-mini
  • Custom-agent models tested: gpt-5.4-mini, gpt-5.4

Minimal Repro

import { BuiltInTools, CopilotClient, ToolSet } from '@github/copilot-sdk';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

const parentModel = 'gpt-5-mini';
const customAgentModel = 'gpt-5.4-mini';
const baseDirectory = mkdtempSync(join(tmpdir(), 'copilot-sdk-model-repro-'));

const client = new CopilotClient({
  mode: 'empty',
  baseDirectory,
  logLevel: 'warning',
});

await client.start();

try {
  const session = await client.createSession({
    model: parentModel,
    streaming: true,
    includeSubAgentStreamingEvents: true,
    enableConfigDiscovery: false,
    skipCustomInstructions: true,
    enableSkills: false,
    availableTools: new ToolSet().addBuiltIn(BuiltInTools.Isolated).toArray(),
    customAgents: [
      {
        name: 'mini-probe',
        displayName: 'Mini Probe',
        description: 'A minimal custom subagent for verifying per-agent model routing.',
        model: customAgentModel,
        tools: [],
        prompt: 'Reply exactly MINI_PROBE_OK and no extra text.',
      },
    ],
    onPermissionRequest: async () => ({ kind: 'approve-once' }),
  });

  session.on((event) => {
    if (event.type === 'assistant.usage') {
      console.log(JSON.stringify({
        type: event.type,
        agentId: event.agentId ?? null,
        initiator: event.data.initiator ?? null,
        parentToolCallId: event.data.parentToolCallId ?? null,
        model: event.data.model,
      }));
    }

    if (event.type.startsWith('subagent.')) {
      console.log(JSON.stringify({
        type: event.type,
        agentId: event.agentId ?? null,
        agentName: event.data.agentName ?? null,
        model: event.data.model ?? null,
      }));
    }
  });

  await session.sendAndWait({
    prompt: 'Use the task tool exactly once with agent_type "mini-probe". Ask it to reply exactly MINI_PROBE_OK and no extra text. Do not answer directly.',
  }, 180_000);

  await session.disconnect();
} finally {
  await client.stop().catch(() => undefined);
  rmSync(baseDirectory, { recursive: true, force: true });
}

Observed

With parent gpt-5-mini and child custom agent gpt-5.4-mini:

{"type":"assistant.usage","agentId":null,"initiator":"user","model":"gpt-5-mini"}
{"type":"subagent.started","agentId":"...","agentName":"mini-probe","model":null}
{"type":"assistant.usage","agentId":"...","initiator":"sub-agent","parentToolCallId":"...","model":"gpt-5-mini"}
{"type":"subagent.completed","agentId":"...","agentName":"mini-probe","model":null}

The subagent usage reports the parent model.

I also tested the reverse direction:

  • Parent gpt-5.4-mini, child gpt-5.4 -> subagent usage reports gpt-5.4-mini
  • Parent gpt-5.4, child gpt-5.4-mini -> subagent usage reports gpt-5.4

So this appears to be inheritance of the parent session model, not a fallback to a specific default.

Expected

The subagent model call should use CustomAgentConfig.model when set and available.

If the runtime falls back to the parent model, the event stream should expose enough information to diagnose it, e.g. requestedModel, effectiveModel, and fallbackReason on subagent.started / subagent.completed or assistant.usage.

Related

Related but not a direct duplicate:

  • github/copilot-sdk#1131 tracks per-subagent reasoning effort and has a comment mentioning model/frontmatter being dropped at task dispatch.
  • github/copilot-cli#2939 was closed as partial support for custom-agent model overrides, but the runtime behavior still appears to inherit parent model during task dispatch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions