For two years the interesting question in AI coding was which model. In 2026 it has quietly become which harness — the loop that plans, calls tools, edits files, asks permission and knows when to stop. Every serious vendor now ships one: OpenAI has the Codex harness, Google folded its CLI work into Antigravity, Anthropic exposes the Claude Code loop through the Agent SDK.
GitHub's entry is the Copilot SDK, and its pitch is unusually literal. It is not a re-implementation or a thin HTTP wrapper. It is the same runtime that powers the Copilot app and CLI, exposed as a library — and as of general availability it ships in six languages: TypeScript, Python, Go, .NET, Rust and Java, all under an MIT license.
This guide covers what the SDK actually gives you, the API surface that matters, and the architectural trade-off you are accepting when you adopt it.
What "the real runtime" means
Most agent frameworks hand you primitives and let you assemble the loop. The Copilot SDK inverts that: the loop already exists, hardened by production traffic, and you attach to it.
Concretely, the SDK is a client that speaks JSON-RPC to the Copilot CLI running in server mode. The SDK is transport; the CLI is the orchestrator that runs the agentic tool-use cycle and makes the model calls.
That distinction matters for reasoning about cost and behaviour. GitHub's own documentation is refreshingly blunt about the mechanics:
Each iteration of this loop is exactly one LLM API call, visible as one
assistant.turn_start/assistant.turn_endpair in the event log. There are no hidden calls.
A turn is one model call and its consequences. A single user message typically produces several: search the codebase, read the matched files, read more files, then answer. The model sees the full accumulated conversation on every turn and decides each time whether it has enough context to stop. When it returns a response with no tool requests, the loop ends and the session emits session.idle.
If you have ever tried to build this yourself, you know the hard parts are not the happy path — they are cancellation, partial tool failures, permission prompts, and context accounting. That is what you are buying.
Getting started
The one genuine prerequisite is the Copilot CLI: it must be installed and authenticated, since the SDK spawns and talks to it. Verify with copilot --version. TypeScript needs Node.js 20 or newer; Python needs 3.11 or newer.
npm install @github/copilot-sdkThe minimal TypeScript program is four meaningful lines:
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
const session = await client.createSession({ model: "auto" });
const response = await session.sendAndWait({ prompt: "What is 2 + 2?" });
console.log(response?.data.content);
await client.stop();Python follows the same create-client, create-session, send shape, with an explicit permission handler:
import asyncio
from copilot import CopilotClient
from copilot.session import PermissionHandler
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session(
on_permission_request=PermissionHandler.approve_all,
model="auto",
)
response = await session.send_and_wait("What is 2 + 2?")
print(response.data.content)
await client.stop()
asyncio.run(main())sendAndWait blocks until the loop reaches idle. For anything user-facing you want the streaming path instead.
Streaming and the event log
The SDK exposes more than forty event types. The ones you reach for first are token deltas and the idle signal:
const session = await client.createSession({ model: "gpt-4.1", streaming: true });
session.on("assistant.message_delta", (event) => {
process.stdout.write(event.data.deltaContent);
});
session.on("session.idle", () => console.log());Beyond text deltas you get assistant.turn_start and assistant.turn_end for turn boundaries, plus tool.execution_start and tool.execution_complete around every tool call. That event stream is the difference between a progress bar that means something and a spinner that lies. It is also, in practice, your observability layer — pair it with the SDK's OpenTelemetry tracing and you can trace an agent run end-to-end with W3C trace-context propagation into your existing stack.
Hooks: the governance layer
Hooks are where the SDK earns its place in an enterprise codebase. They let you intercept the loop at defined points rather than trusting the model's judgement.
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => { /* inject context */ },
onPreToolUse: async (input, invocation) => { /* approve, deny, rewrite args */ },
onPostToolUse: async (input, invocation) => { /* transform or redact results */ },
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});onPreToolUse fires before a tool runs and can approve or deny execution, modify the tool's arguments, add context, or suppress the output from the conversation entirely. That is a real policy hook, not a logging callback. If your compliance team needs "the agent may never run git push against main, and every file write outside /src gets logged," you express it here — in your own code, in your own process — rather than hoping a system prompt holds.
The hook family covers session lifecycle, pre- and post-tool-use, user-prompt-submitted, and error handling. Python uses the same names in snake_case:
session = await client.create_session(
on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
hooks={
"on_session_start": on_session_start,
"on_pre_tool_use": on_pre_tool_use,
"on_post_tool_use": on_post_tool_use,
},
)Custom tools
Built-in tools cover reading, searching and editing files. Your tools cover your business. In Python the ergonomics are excellent — a decorator derives the JSON schema from the type hints:
@define_tool
def deploy_to_staging(branch: str, region: str) -> str:
"""Deploy a branch to the staging environment and return the deployment URL."""
return run_deployment(branch, region)The docstring is not decoration: it is what the model reads when deciding whether to call the tool. Vague descriptions produce agents that call the wrong thing at the wrong time, and no amount of prompt engineering upstream fixes an ambiguous tool description.
Tool definitions accept a few options worth knowing. skipPermission bypasses the permission prompt for tools you have already decided are safe. overridesBuiltInTool lets you replace a CLI tool with your own implementation — useful when your organisation has a sanctioned wrapper around shell execution. defer controls lazy loading so a large tool catalogue does not consume context on every turn.
.NET takes a more idiomatic route, wrapping ordinary methods with AIFunctionFactory.Create and bridging to Microsoft.Extensions.AI; Java exposes ToolDefinition.create() with typed parameter objects. The concept is identical across all six.
Steering and queueing
Anyone who has watched an agent confidently head down the wrong path knows the frustration of having to kill the run. The SDK models this properly with a mode field on message options.
Steering (mode: "immediate") injects your message into the turn already in flight. The agent sees the correction in real time and adjusts without aborting:
const msgId = await session.send({
prompt: "Refactor the authentication module to use sessions",
});
// The agent is already working — redirect it
await session.send({
prompt: "Actually, use JWT tokens instead of sessions",
mode: "immediate",
});Queueing (mode: "enqueue") buffers the message until the current turn completes — the right choice for "after this, also fix the tests." Two lines of API for a distinction most homegrown harnesses never get around to implementing.
Fleet mode: parallel sub-agents
Fleet mode dispatches multiple sub-agents in parallel from a single parent session, coordinating through a shared todo state. The wire method is session.fleet.start:
const result = await session.rpc.fleet.start({
prompt: "Refactor each SDK package independently, then summarize the changes.",
});
if (result.started) {
console.log("Fleet mode started");
}GitHub is admirably specific about when this is the wrong tool. Fleet mode fits work that decomposes cleanly before execution: multi-file refactors where each worker owns a package, batch reviews across separate diffs, parallel research across independent services. It is the wrong choice for sequential tasks where step two needs step one's concrete output, for tightly coupled edits where workers contend for the same files, and for small tasks a single agent finishes faster than the orchestration overhead.
One caveat straight from the docs: the fleet binding is experimental in the generated RPC surface. If you depend on it, pin both the SDK and the CLI runtime.
Controlling the bill
Autonomous loops consume capacity faster than chat, because a single user message becomes many turns. The SDK gives you a direct lever — a per-session AI Credits budget:
const session = await client.createSession({
onPermissionRequest: approveAll,
sessionLimits: {
maxAiCredits: 30,
},
});Read the semantics carefully: this is a soft cap. Usage is checked after model calls return, so a single response can exceed the configured value before the runtime blocks the next call. Budget for overshoot rather than treating it as a hard ceiling. The same sessionLimits object applies when you resume a session, and the usage-and-billing surface exposes token counts, context-window utilisation and account quota if you want to build your own guardrails on top.
Access-wise, the SDK is included with existing Copilot subscriptions — including Copilot Free, with limited monthly usage — and agent runs draw on standard Copilot capacity. Authentication accepts signed-in user credentials, OAuth GitHub App tokens, environment variables such as COPILOT_GITHUB_TOKEN or GITHUB_TOKEN, or bring-your-own-key against a supported LLM provider. BYOK is the escape hatch that matters for teams outside GitHub's billing relationship: it removes the subscription requirement entirely.
The trade-off, stated plainly
The SDK's greatest strength and its main constraint are the same fact: it is a client for the Copilot CLI, not a stateless HTTP library.
That buys you distribution parity. You are running the exact runtime GitHub ships to millions of developers, with the same tool implementations, the same permission model and the same fixes. You inherit GitHub identity and billing rather than standing up your own.
What it costs you is deployment weight. The CLI binary must be present wherever your code runs — a container layer, a CI image, a Lambda you now have to think harder about. The Rust SDK bundles it by default, which softens the problem for that ecosystem. And the SDK manages the CLI process lifecycle automatically, though you can point it at an external server for advanced deployments.
Choose it when you are already invested in GitHub and want embedded agentic coding — internal developer tools, CI assistants, customer-facing features — without building orchestration from scratch. Be more careful if you need low-dependency stateless calls, run in a constrained serverless environment, or operate largely outside GitHub's ecosystem. In those cases a lighter runtime such as the Claude Agent SDK or an open TypeScript harness may fit better.
Where this leaves the harness debate
The SDK's existence is an argument about where the value sits. GitHub is betting that the loop, the tool implementations, the permission model and the event taxonomy are the hard, differentiated part — and that models are increasingly a configuration choice. The model: "auto" default and full BYOK support say that out loud.
For teams building agentic features rather than agentic products, that is a reasonable bet. The parts you would otherwise spend a quarter rebuilding — cancellation, steering, permission prompts, credit accounting, forty-plus well-named events — are exactly the parts nobody puts on a roadmap and everybody needs.
Start with a single session and a streaming loop. Add hooks the moment the agent touches anything you would not let an intern touch unsupervised. Add custom tools when the agent needs to reach into your systems. Reach for fleet mode only when the work genuinely decomposes — and pin your versions when you do.
Related reading: GitHub Copilot coding agent: autonomous PRs · Harness engineering for production agents · Prompt injection and AI agent security
Building agent-powered features into your product? Noqta helps teams in Tunisia and the Gulf design, secure and ship production AI systems.