Custom Agents vs Sub-Agent Orchestration Patterns in the Copilot SDK
Custom agents are static configuration definitions that you declare at session creation, while sub-agent orchestration is the dynamic runtime behavior that selects, launches, and manages their execution within a parent session.
When building complex AI workflows with the GitHub Copilot SDK, understanding the distinction between agent definitions and their execution model is critical for designing scalable applications. Custom agents serve as reusable building blocks configured in your session setup, whereas sub-agent orchestration represents the runtime intelligence—defined in docs/features/custom-agents.md—that determines which agent runs, when it activates, and how its results integrate back into the conversation.
What Are Custom Agents?
Custom agents are static configurations that you attach to a session via the customAgents array. Each agent carries its own system prompt, an optional list of allowed tools, optional MCP servers, and per-agent settings such as infer, model, or reasoningEffort. According to the Copilot SDK source code, these definitions act as candidate sub-agents that the runtime may later invoke based on user intent or explicit requests.
In docs/features/custom-agents.md, custom agents are declared at session initialization and remain visible in sessionConfig.customAgents throughout the session lifetime. They do not execute on their own; they serve strictly as metadata-rich definitions that describe capabilities, constraints, and behavioral instructions.
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
await client.start();
const session = await client.createSession({
model: "gpt-5.4",
customAgents: [
{
name: "researcher",
displayName: "Research Agent",
description: "Read-only exploration of the codebase",
tools: ["grep", "glob", "view"],
prompt: "You are a research assistant. Analyze code and answer questions. Do not modify any files.",
},
{
name: "editor",
displayName: "Editor Agent",
description: "Makes targeted code changes",
tools: ["view", "edit", "bash"],
prompt: "You are a code editor. Make minimal, surgical changes to files as requested.",
},
],
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
What Is Sub-Agent Orchestration?
Sub-agent orchestration is the runtime behavior that decides when and how a custom agent is actually executed. As implemented in github/copilot-sdk, the runtime performs inference when a user's intent matches an agent's description and the infer property is not explicitly set to false.
The orchestration layer manages the full lifecycle of agent execution, running each sub-agent in an isolated context with its own prompt and restricted tool set. The parent session receives streamed events that UI layers can render, providing visibility into which agent was selected and its current state.
Sub-Agent Event Lifecycle
When the runtime selects and executes a custom agent, it emits a sequence of lifecycle events that the parent session can observe:
session.on((event) => {
switch (event.type) {
case "subagent.selected":
console.log(`🧭 Selected ${event.data.agentDisplayName}`);
break;
case "subagent.started":
console.log(`▶️ Started ${event.data.agentDisplayName}`);
break;
case "subagent.completed":
console.log(`✅ Completed ${event.data.agentDisplayName}`);
break;
case "subagent.failed":
console.error(`❌ Failed ${event.data.agentDisplayName}: ${event.data.error}`);
break;
}
});
await session.sendAndWait({ prompt: "Explain the authentication flow in this repo" });
Parallel Execution with Fleet Mode
For truly parallel sub-agent orchestration, the SDK provides Fleet mode, detailed in docs/features/fleet-mode.md. This pattern dispatches multiple sub-agents concurrently using the task tool and a shared SQL-based todo coordination model, allowing independent units of work to execute simultaneously.
const result = await session.rpc.fleet.start({
prompt: "Refactor each SDK package independently, then summarize the changes.",
});
if (result.started) {
console.log("🚀 Fleet mode launched");
}
Key Differences Between Configuration and Runtime
Drawing from docs/features/custom-agents.md and docs/features/fleet-mode.md, the distinction between these concepts breaks down across several dimensions:
- Purpose: Custom agents provide static definitions (name, prompt, tool set, description) that can be selected later. Sub-agent orchestration dynamically selects, launches, and manages the execution of these agents based on user intent or explicit tool calls.
- Creation: Agents are declared in the
customAgentsarray at session creation time. Orchestration triggers automatically when intent matches description, or when explicitly invoked via thesession.rpc.fleet.startAPI. - Execution Context: Custom agents exist only as metadata with no execution context. Sub-agent orchestration runs agents in isolated contexts with restricted tool sets and emits lifecycle events (
subagent.started,subagent.completed, etc.). - Parallelism: Individual custom agents are not inherently parallel; each may be invoked multiple times sequentially. Fleet mode enables true parallelism by dispatching many sub-agents concurrently with coordination via the SQL-based todo model.
- Control: Developers adjust custom agents via properties like
tools,infer,model, andskills. They control orchestration through runtime events, explicit selections, or by disabling inference (infer: false) to require explicit user requests.
Disabling Automatic Agent Selection
You can prevent the runtime from automatically selecting a specific agent by setting infer: false in the configuration. This forces explicit invocation, ensuring the agent only runs when the user specifically requests it or when called via a tool.
{
name: "dangerous-cleanup",
description: "Deletes unused files and dead code",
tools: ["bash", "edit", "view"],
prompt: "You clean up codebases by removing dead code and unused files.",
infer: false, // Must be called explicitly
}
Even when handling parallel workloads in Fleet mode, the same event stream tracks each sub-agent's progress:
def handle(event):
if event.type == "subagent.started":
print(f"▶ {event.data.agent_display_name} started")
elif event.type == "subagent.completed":
print(f"✅ {event.data.agent_display_name} finished")
session.on(handle)
Summary
- Custom agents are static definitions configured in the
customAgentsarray of your session configuration, specifying prompts, tool sets, and metadata. - Sub-agent orchestration is the dynamic runtime logic—implemented in
docs/features/custom-agents.md—that selects and executes agents based on inference or explicit calls. - The runtime emits lifecycle events (
subagent.selected,subagent.started,subagent.completed,subagent.failed) that parent sessions observe to track execution. - Fleet mode, documented in
docs/features/fleet-mode.md, enables parallel sub-agent orchestration usingsession.rpc.fleet.startand SQL-based coordination. - Setting
infer: falseforces explicit agent invocation, bypassing automatic intent matching while maintaining the agent's availability for direct requests.
Frequently Asked Questions
How do I prevent the Copilot SDK from automatically selecting a custom agent?
Set the infer property to false in your agent configuration within the customAgents array. According to docs/features/custom-agents.md, this prevents the runtime from matching user intent against the agent's description, requiring either explicit user requests or programmatic tool calls to invoke the agent.
What events indicate that a sub-agent has started or completed its work?
The runtime emits subagent.selected when inference chooses an agent, subagent.started when execution begins, and either subagent.completed upon success or subagent.failed if an error occurs. These events are streamed to the parent session and can be handled via the session.on() event listener as shown in the Node.js and Python examples above.
Can custom agents run in parallel, and how is this coordinated?
Yes, through Fleet mode, a specialized sub-agent orchestration pattern detailed in docs/features/fleet-mode.md. You initiate parallel execution by calling session.rpc.fleet.start(), which dispatches multiple sub-agents concurrently using the task tool and coordinates their work through a shared SQL-based todo model, aggregating results back into the parent session.
Where are custom agent definitions stored versus orchestration logic?
Agent definitions reside in your session configuration's customAgents array and are documented in docs/features/custom-agents.md. The orchestration logic—the algorithms for selection, lifecycle management, and parallel coordination—is implemented in the SDK runtime and documented across docs/features/custom-agents.md (for sequential orchestration) and docs/features/fleet-mode.md (for parallel execution), with RPC bindings available in language-specific sources like go/rpc/fleet.proto.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →