Phase 4: Entry Points
Every public entry point across the four Copilot CLI SDKs (TypeScript/Node.js, Python, Go, C#) — covering package exports, client initialization, session creation, extension points, sample applications, CLI integration, and the bootstrap sequence.
1. Package Entry Points
1.1 TypeScript / Node.js
nodejs/src/index.ts
The npm package @github/copilot-sdk re-exports everything through a single barrel file. There are two module entry points: the main package and the /extension sub-path.
Main export (@github/copilot-sdk)
// nodejs/src/index.ts:11-55
export { CopilotClient } from "./client.js";
export { CopilotSession, type AssistantMessageEvent } from "./session.js";
export { defineTool, approveAll } from "./types.js";
export type {
ConnectionState,
CopilotClientOptions,
CustomAgentConfig,
ForegroundSessionInfo,
GetAuthStatusResponse,
GetStatusResponse,
InfiniteSessionConfig,
MCPLocalServerConfig,
MCPRemoteServerConfig,
MCPServerConfig,
MessageOptions,
ModelBilling,
ModelCapabilities,
ModelInfo,
ModelPolicy,
PermissionHandler,
PermissionRequest,
PermissionRequestResult,
ResumeSessionConfig,
SessionConfig,
SessionEvent,
SessionEventHandler,
SessionEventPayload,
SessionEventType,
SessionLifecycleEvent,
SessionLifecycleEventType,
SessionLifecycleHandler,
SessionContext,
SessionListFilter,
SessionMetadata,
SystemMessageAppendConfig,
SystemMessageConfig,
SystemMessageReplaceConfig,
Tool,
ToolHandler,
ToolInvocation,
ToolResultObject,
TypedSessionEventHandler,
TypedSessionLifecycleHandler,
ZodSchema,
} from "./types.js";
Extension sub-path (@github/copilot-sdk/extension)
// nodejs/src/extension.ts:26-39
export async function joinSession(config: ResumeSessionConfig): Promise<CopilotSession>
1.2 Python
python/copilot/__init__.py
The copilot package exports via __init__.py with an explicit __all__ list:
# python/copilot/__init__.py:1-77
from .client import CopilotClient
from .session import CopilotSession
from .tools import define_tool
from .types import (
AzureProviderOptions, ConnectionState, CustomAgentConfig,
GetAuthStatusResponse, GetStatusResponse, MCPLocalServerConfig,
MCPRemoteServerConfig, MCPServerConfig, MessageOptions,
ModelBilling, ModelCapabilities, ModelInfo, ModelPolicy,
PermissionHandler, PermissionRequest, PermissionRequestResult,
PingResponse, ProviderConfig, ResumeSessionConfig, SessionConfig,
SessionContext, SessionEvent, SessionListFilter, SessionMetadata,
StopError, Tool, ToolHandler, ToolInvocation, ToolResult,
)
__version__ = "0.1.0"
1.3 Go
go/client.go (package copilot)
Go uses the standard Go package model. All exported identifiers in the copilot package are public. Key exports:
| Symbol | File | Line |
|---|---|---|
Client (struct) | go/client.go | 73 |
NewClient (constructor) | go/client.go | 119 |
Session (struct) | go/session.go | 51 |
ClientOptions | go/types.go | 19 |
SessionConfig | go/types.go | 349 |
ResumeSessionConfig | go/types.go | 443 |
PermissionHandler (var) | go/permissions.go | 4 |
Tool | go/types.go | 412 |
ToolHandler | go/types.go | 430 |
SessionEvent | implicitly from event types | |
Bool, String, Float64 (helpers) | go/types.go | 69-83 |
1.4 C# / .NET
dotnet/src/Client.cs, dotnet/src/Session.cs, dotnet/src/Types.cs
Namespace: GitHub.Copilot.SDK
| Symbol | File | Line |
|---|---|---|
CopilotClient | dotnet/src/Client.cs | 55 |
CopilotSession | dotnet/src/Session.cs | 53 |
CopilotClientOptions | dotnet/src/Types.cs | 38 |
SessionConfig | dotnet/src/Types.cs | (exported record) |
ResumeSessionConfig | dotnet/src/Types.cs | (exported record) |
PermissionHandler | dotnet/src/Types.cs | (static class) |
ConnectionState | dotnet/src/Types.cs | 19 |
SessionEvent (hierarchy) | dotnet/src/Types.cs | (class hierarchy with pattern matching) |
2. Client Initialization
All four SDKs follow the same pattern: construct a client with options, then start it (either explicitly or via auto-start).
Constructor Signatures
// nodejs/src/client.ts:203
constructor(options: CopilotClientOptions = {})# python/copilot/client.py:113
def __init__(self, options: CopilotClientOptions | None = None):// go/client.go:119
func NewClient(options *ClientOptions) *Client// dotnet/src/Client.cs:117
public CopilotClient(CopilotClientOptions? options = null)2.1 TypeScript CopilotClientOptions
nodejs/src/types.ts:16-107
| Option | Type | Default | Description |
|---|---|---|---|
cliPath | string? | bundled CLI | Path to CLI executable |
cliArgs | string[]? | [] | Extra CLI arguments |
cwd | string? | process.cwd() | Working directory |
port | number? | 0 (random) | TCP port (TCP mode) |
useStdio | boolean? | true | Use stdio transport |
isChildProcess | boolean? | false | Running as child of CLI |
cliUrl | string? | — | Connect to existing server |
logLevel | string? | "debug" | CLI log level |
autoStart | boolean? | true | Auto-start on first use |
autoRestart | boolean? | true | Auto-restart on crash |
env | Record<string,string>? | process.env | Environment variables |
githubToken | string? | — | GitHub auth token |
useLoggedInUser | boolean? | true | Use stored OAuth/gh CLI auth |
onListModels | () => ModelInfo[]? | — | Custom model listing |
cliUrl is mutually exclusive with useStdio and cliPath. Auth options (githubToken, useLoggedInUser) cannot be used with cliUrl.
2.2 Python CopilotClientOptions
python/copilot/types.py:73-105
| Option | Type | Default | Description |
|---|---|---|---|
cli_path | str | bundled binary | Path to CLI executable |
cli_args | list[str] | [] | Extra CLI arguments |
cwd | str | os.getcwd() | Working directory |
port | int | 0 | TCP port |
use_stdio | bool | True | Use stdio transport |
cli_url | str | — | Connect to existing server |
log_level | LogLevel | "info" | CLI log level |
auto_start | bool | True | Auto-start on first use |
auto_restart | bool | True | Auto-restart on crash |
env | dict[str,str] | inherited | Environment variables |
github_token | str | — | GitHub auth token |
use_logged_in_user | bool | True | Use stored auth |
on_list_models | Callable | — | Custom model listing |
2.3 Go ClientOptions
go/types.go:19-65
| Option | Type | Default | Description |
|---|---|---|---|
CLIPath | string | "" (auto-detect) | Path to CLI executable |
CLIArgs | []string | nil | Extra CLI arguments |
Cwd | string | "" (inherit) | Working directory |
Port | int | 0 | TCP port |
UseStdio | *bool | nil (true) | Use stdio transport |
CLIUrl | string | "" | Connect to existing server |
LogLevel | string | "info" | CLI log level |
AutoStart | *bool | nil (true) | Auto-start on first use |
AutoRestart | *bool | nil (true) | Auto-restart on crash |
Env | []string | os.Environ() | Environment variables |
GitHubToken | string | "" | GitHub auth token |
UseLoggedInUser | *bool | nil (true) | Use stored auth |
OnListModels | func(ctx) ([]ModelInfo, error) | nil | Custom model listing |
2.4 C# CopilotClientOptions
dotnet/src/Types.cs:38+
| Property | Type | Default | Description |
|---|---|---|---|
CliPath | string? | null (bundled) | Path to CLI executable |
CliArgs | IList<string>? | null | Extra CLI arguments |
Cwd | string? | null (inherit) | Working directory |
Port | int? | null | TCP port |
UseStdio | bool? | true | Use stdio transport |
CliUrl | string? | null | Connect to existing server |
LogLevel | string? | null | CLI log level |
AutoStart | bool | true | Auto-start on first use |
AutoRestart | bool | true | Auto-restart on crash |
GitHubToken | string? | null | GitHub auth token |
UseLoggedInUser | bool? | null (true) | Use stored auth |
Logger | ILogger? | NullLogger | Custom logger |
OnListModels | Func<CancellationToken, Task<List<ModelInfo>>>? | null | Custom model listing |
3. Session Creation
Sessions are the primary conversation primitive. Creating a session requires a mandatory onPermissionRequest handler.
Create & Resume Signatures
// nodejs/src/client.ts:539
async createSession(config: SessionConfig): Promise<CopilotSession>
// nodejs/src/client.ts:616+
async resumeSession(sessionId: string, config: ResumeSessionConfig): Promise<CopilotSession># python/copilot/client.py:449
async def create_session(self, config: SessionConfig) -> CopilotSession:
# python/copilot/client.py:640
async def resume_session(self, session_id: str, config: ResumeSessionConfig) -> CopilotSession:// go/client.go:487
func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Session, error)
// go/client.go:588
func (c *Client) ResumeSession(ctx context.Context, sessionID string, config *ResumeSessionConfig) (*Session, error)// dotnet/src/Client.cs:387
public async Task<CopilotSession> CreateSessionAsync(
SessionConfig config, CancellationToken cancellationToken = default)
// dotnet/src/Client.cs:493
public async Task<CopilotSession> ResumeSessionAsync(
string sessionId, ResumeSessionConfig config, CancellationToken cancellationToken = default)SessionConfig Key Fields
nodejs/src/types.ts:631-770 — other SDKs mirror these with idiomatic naming.
| Field | Required | Description |
|---|---|---|
onPermissionRequest | Yes | Handler for tool permission requests |
model | No | Model ID (e.g., "gpt-4") |
sessionId | No | Custom session ID (auto-generated UUID) |
clientName | No | Application identifier for User-Agent |
tools | No | Custom tool definitions |
systemMessage | No | System prompt config (append/replace) |
availableTools | No | Allowlist of tool names |
excludedTools | No | Denylist of tool names |
provider | No | BYOK provider config |
onUserInputRequest | No | Handler for ask_user tool |
hooks | No | Session lifecycle hooks |
workingDirectory | No | CWD for tool operations |
streaming | No | Enable delta streaming events |
mcpServers | No | MCP server configurations |
customAgents | No | Custom agent definitions |
agent | No | Agent to activate on start |
skillDirectories | No | Directories to load skills from |
disabledSkills | No | Skill denylist |
infiniteSessions | No | Infinite session / compaction config |
reasoningEffort | No | "low" / "medium" / "high" / "xhigh" |
onEvent | No | Early event handler (before RPC) |
3.5 Session Lifecycle
Across all SDKs, the session creation sequence is:
- Validate
onPermissionRequestis provided (throws if missing) - Ensure client is connected (auto-start if enabled)
- Generate session ID if not provided (UUID)
- Create
CopilotSessionobject and register it before the RPC call (to avoid missing early events) - Register tools, permission handler, user input handler, hooks, and early event handler on the session
- Issue
session.createJSON-RPC request with serialized config - Return the session object (with workspace path if infinite sessions enabled)
Cleanup Methods
| SDK | Disconnect | Deprecated Alias | Dispose Pattern |
|---|---|---|---|
| TypeScript | session.disconnect() | session.destroy() | Symbol.asyncDispose |
| Python | await session.disconnect() | await session.destroy() | async with context manager |
| Go | session.Disconnect() | session.Destroy() | defer session.Disconnect() |
| C# | await session.DisposeAsync() | — | await using |
4. Extension Points
4.1 Custom Tools
Tools allow the LLM to invoke user-defined functions. Each SDK provides a way to define tools with schemas.
// nodejs/src/types.ts:159-186
// Tool interface
interface Tool<TArgs = unknown> {
name: string;
description?: string;
parameters?: ZodSchema<TArgs> | Record<string, unknown>;
handler: ToolHandler<TArgs>;
overridesBuiltInTool?: boolean;
}
// defineTool helper for Zod-inferred types
const myTool = defineTool("get_weather", {
description: "Get weather for a location",
parameters: z.object({ location: z.string() }),
handler: async (args) => ({ temperature: 72 }),
});# python/copilot/tools.py:43-166
# Decorator pattern with Pydantic schema inference
@define_tool(description="Fetch issue details")
def lookup_issue(params: LookupIssueParams) -> str:
return fetch_issue(params.id).summary
# Function pattern
tool = define_tool(
"lookup_issue",
description="Fetch issue details",
handler=lambda params, inv: fetch_issue(params.id).summary,
params_type=LookupIssueParams,
)// go/types.go:412-430
type Tool struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Parameters map[string]any `json:"parameters,omitempty"`
OverridesBuiltInTool bool `json:"overridesBuiltInTool,omitempty"`
Handler ToolHandler `json:"-"`
}
type ToolHandler func(invocation ToolInvocation) (ToolResult, error)// dotnet/src/Session.cs:293
// C# uses Microsoft.Extensions.AI.AIFunction for tool definitions
internal void RegisterTools(ICollection<AIFunction> tools)4.2 Permission Handlers
Every SDK requires a permission handler. A convenience "approve all" handler is provided:
| SDK | Approve-all Handler | File:Line |
|---|---|---|
| TypeScript | approveAll (exported const) | nodejs/src/types.ts:251 |
| Python | PermissionHandler.approve_all (static method) | python/copilot/types.py:211-215 |
| Go | PermissionHandler.ApproveAll (package-level var) | go/permissions.go:4-11 |
| C# | PermissionHandler.ApproveAll (static member) | dotnet/src/Types.cs |
Permission request kinds: "shell", "write", "mcp", "read", "url", "custom-tool".
Permission result kinds: "approved", "denied-by-rules", "denied-by-content-exclusion-policy", "denied-no-approval-rule-and-could-not-request-from-user", "denied-interactively-by-user".
4.3 Session Hooks
Hooks intercept session lifecycle events. Six hook types are supported:
| Hook | Input | Trigger |
|---|---|---|
onPreToolUse | PreToolUseHookInput | Before a tool executes |
onPostToolUse | PostToolUseHookInput | After a tool executes |
onUserPromptSubmitted | UserPromptSubmittedHookInput | User sends a prompt |
onSessionStart | SessionStartHookInput | Session begins |
onSessionEnd | SessionEndHookInput | Session terminates |
onErrorOccurred | ErrorOccurredHookInput | An error occurs |
// nodejs/src/types.ts:470-500
interface SessionHooks {
onPreToolUse?: PreToolUseHandler;
onPostToolUse?: PostToolUseHandler;
onUserPromptSubmitted?: UserPromptSubmittedHandler;
onSessionStart?: SessionStartHandler;
onSessionEnd?: SessionEndHandler;
onErrorOccurred?: ErrorOccurredHandler;
}// go/types.go:283-290
type SessionHooks struct {
OnPreToolUse PreToolUseHandler
OnPostToolUse PostToolUseHandler
OnUserPromptSubmitted UserPromptSubmittedHandler
OnSessionStart SessionStartHandler
OnSessionEnd SessionEndHandler
OnErrorOccurred ErrorOccurredHandler
}The preToolUse hook can return permission decisions ("allow", "deny", "ask") and modified arguments. The postToolUse hook can return modified results.
4.4 MCP Server Registration
MCP (Model Context Protocol) servers are configured at session creation via mcpServers:
// nodejs/src/types.ts:528-557
interface MCPLocalServerConfig {
type?: "local" | "stdio";
command: string;
args: string[];
tools: string[];
env?: Record<string, string>;
cwd?: string;
timeout?: number;
}
interface MCPRemoteServerConfig {
type: "http" | "sse";
url: string;
tools: string[];
headers?: Record<string, string>;
timeout?: number;
}
Usage Example
const session = await client.createSession({
onPermissionRequest: approveAll,
mcpServers: {
"my-server": {
command: "npx",
args: ["-y", "@my/mcp-server"],
tools: ["*"],
},
},
});
4.5 Custom Agents
Agents provide named prompt configurations with optional tool restrictions and MCP servers:
// nodejs/src/types.ts:566-597
interface CustomAgentConfig {
name: string;
displayName?: string;
description?: string;
tools?: string[] | null;
prompt: string;
mcpServers?: Record<string, MCPServerConfig>;
infer?: boolean;
}
Activated via agent field in SessionConfig or via session.rpc.agent.select({ name }).
4.6 Skills
Skills are loaded from directories specified in skillDirectories and can be disabled via disabledSkills:
// nodejs/src/types.ts:746-751
skillDirectories?: string[];
disabledSkills?: string[];
4.7 User Input Handler
Enables the ask_user tool, allowing the agent to ask questions:
// nodejs/src/types.ts:296-299
type UserInputHandler = (
request: UserInputRequest,
invocation: { sessionId: string }
) => Promise<UserInputResponse> | UserInputResponse;
4.8 System Message Configuration
Two modes for controlling the system prompt:
// nodejs/src/types.ts:202-230
// Append mode (default): SDK foundation + custom content
interface SystemMessageAppendConfig {
mode?: "append";
content?: string;
}
// Replace mode: full control, replaces entire system message
interface SystemMessageReplaceConfig {
mode: "replace";
content: string;
}
4.9 BYOK Provider Configuration
Bring-your-own-key support for custom model providers:
// nodejs/src/types.ts:810-847
interface ProviderConfig {
type?: "openai" | "azure" | "anthropic";
wireApi?: "completions" | "responses";
baseUrl: string;
apiKey?: string;
bearerToken?: string;
azure?: { apiVersion?: string };
}
4.10 Extension Entry Point (TypeScript only)
The joinSession function is for extensions running as child processes of the Copilot CLI:
// nodejs/src/extension.ts:26-39
export async function joinSession(config: ResumeSessionConfig): Promise<CopilotSession> {
const sessionId = process.env.SESSION_ID;
if (!sessionId) {
throw new Error(
"joinSession() is intended for extensions running as child processes of the Copilot CLI."
);
}
const client = new CopilotClient({ isChildProcess: true });
return client.resumeSession(sessionId, {
...config,
disableResume: config.disableResume ?? true,
});
}
This reads SESSION_ID from the environment and connects back to the parent CLI process via stdio.
5. Sample Applications
All four SDKs include a chat sample that demonstrates the minimal integration pattern.
// nodejs/samples/chat.ts:1-35
import { CopilotClient, approveAll, type SessionEvent } from "@github/copilot-sdk";
async function main() {
const client = new CopilotClient();
const session = await client.createSession({
onPermissionRequest: approveAll,
});
session.on((event: SessionEvent) => {
let output: string | null = null;
if (event.type === "assistant.reasoning") {
output = `[reasoning: ${event.data.content}]`;
} else if (event.type === "tool.execution_start") {
output = `[tool: ${event.data.toolName}]`;
}
if (output) console.log(`\x1b[34m${output}\x1b[0m`);
});
// REPL loop
while (true) {
const input = await prompt("You: ");
if (!input.trim()) continue;
const reply = await session.sendAndWait({ prompt: input });
console.log(`\nAssistant: ${reply?.data.content}\n`);
}
}# python/samples/chat.py:1-45
from copilot import CopilotClient, PermissionHandler
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session({
"on_permission_request": PermissionHandler.approve_all,
})
def on_event(event):
if event.type.value == "assistant.reasoning":
print(f"[reasoning: {event.data.content}]")
elif event.type.value == "tool.execution_start":
print(f"[tool: {event.data.tool_name}]")
session.on(on_event)
while True:
user_input = input("You: ").strip()
reply = await session.send_and_wait({"prompt": user_input})
print(f"\nAssistant: {reply.data.content if reply else None}\n")// go/samples/chat.go:17-73
func main() {
ctx := context.Background()
client := copilot.NewClient(&copilot.ClientOptions{CLIPath: cliPath})
if err := client.Start(ctx); err != nil { panic(err) }
defer client.Stop()
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
defer session.Disconnect()
session.On(func(event copilot.SessionEvent) {
switch event.Type {
case copilot.AssistantReasoning:
fmt.Printf("[reasoning: %s]", *event.Data.Content)
case copilot.ToolExecutionStart:
fmt.Printf("[tool: %s]", *event.Data.ToolName)
}
})
// Scanner loop
for scanner.Scan() {
reply, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: input})
fmt.Printf("\nAssistant: %s\n\n", *reply.Data.Content)
}
}// dotnet/samples/Chat.cs:1-35
using GitHub.Copilot.SDK;
await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll
});
using var _ = session.On(evt =>
{
switch (evt)
{
case AssistantReasoningEvent reasoning:
Console.WriteLine($"[reasoning: {reasoning.Data.Content}]");
break;
case ToolExecutionStartEvent tool:
Console.WriteLine($"[tool: {tool.Data.ToolName}]");
break;
}
});
while (true)
{
var input = Console.ReadLine()?.Trim();
var reply = await session.SendAndWaitAsync(new MessageOptions { Prompt = input });
Console.WriteLine($"\nAssistant: {reply?.Data.Content}\n");
}5.5 Pattern Summary
All samples demonstrate the same flow:
- Create client (optionally with explicit CLI path)
- Start client (auto or explicit)
- Create session with
approveAllpermission handler - Register event handler for reasoning and tool events
- REPL loop: read input, call
sendAndWait, print response - Cleanup on exit
6. CLI Integration
6.1 Transport Modes
The SDK communicates with the Copilot CLI server via JSON-RPC 2.0 over one of two transports:
| Mode | Default | Description |
|---|---|---|
| stdio | Yes | SDK spawns CLI process, communicates via stdin/stdout pipes |
| TCP | No | SDK connects to CLI server on a TCP port |
6.2 CLI Server Spawning
When using stdio (default), the SDK spawns the Copilot CLI as a child process:
// nodejs/src/client.ts:92-98
function getBundledCliPath(): string {
const sdkUrl = import.meta.resolve("@github/copilot/sdk");
const sdkPath = fileURLToPath(sdkUrl);
return join(dirname(dirname(sdkPath)), "index.js");
}
// Resolves the bundled CLI from @github/copilot package, runs index.js via Node.# python/copilot/client.py:58-75
def _get_bundled_cli_path() -> str | None:
bin_dir = Path(__file__).parent / "bin"
if sys.platform == "win32":
binary_name = "copilot.exe"
else:
binary_name = "copilot"
binary_path = bin_dir / binary_name
if binary_path.exists():
return str(binary_path)
return None
# Ships a platform-specific native binary in copilot/bin/.// go/client.go + go/internal/embeddedcli/
// Uses an embedded CLI binary from the embeddedcli internal package,
// or looks up COPILOT_CLI_PATH from the environment.// dotnet/src/Client.cs
// Follows a similar pattern: bundles the CLI binary or allows
// CliPath override via CopilotClientOptions.6.3 Connection to External Servers
All SDKs support connecting to a pre-existing CLI server via cliUrl:
const client = new CopilotClient({ cliUrl: "localhost:3000" });
URL formats supported: "port", "host:port", "http://host:port", "https://host:port".
When cliUrl is set, no CLI process is spawned; the SDK connects via TCP directly.
6.4 Protocol Version Negotiation
After connecting, every SDK verifies protocol compatibility:
// nodejs/src/client.ts:56
const MIN_PROTOCOL_VERSION = 2;# python/copilot/client.py:55
MIN_PROTOCOL_VERSION = 2// Verified in client.go Start() via verifyProtocolVersion()// dotnet/src/Client.cs:60
private const int MinProtocolVersion = 2;The client issues a status check RPC and rejects servers with protocol versions below the minimum.
6.5 JSON-RPC Methods
Key RPC methods used by the SDK:
| Method | Direction | Purpose |
|---|---|---|
status.get | SDK → CLI | Get server version and protocol version |
session.create | SDK → CLI | Create a new session |
session.resume | SDK → CLI | Resume an existing session |
session.send | SDK → CLI | Send a user message |
session.getMessages | SDK → CLI | Get conversation history |
session.abort | SDK → CLI | Abort current processing |
session.destroy | SDK → CLI | Disconnect session |
session.delete | SDK → CLI | Permanently delete session data |
session.list | SDK → CLI | List sessions with optional filter |
session.event | CLI → SDK | Push session events (notification) |
auth.getStatus | SDK → CLI | Check authentication status |
models.list | SDK → CLI | List available models |
session.model.switchTo | SDK → CLI | Change session model |
session.tools.handlePendingToolCall | SDK → CLI | Return tool execution result |
session.permissions.handlePendingPermissionRequest | SDK → CLI | Return permission decision |
session.log | SDK → CLI | Log message to session timeline |
7. Bootstrap Sequence
The full flow from import to first assistant response:
Step 1: Import
import { CopilotClient, approveAll } from "@github/copilot-sdk";
This loads the barrel file (index.ts), which re-exports CopilotClient from client.ts, approveAll from types.ts, and type definitions.
Step 2: Client Construction
const client = new CopilotClient();
- Resolves bundled CLI path (e.g.,
@github/copilot/index.js) - Merges user options with defaults
- Sets initial state to
"disconnected" - Does NOT start the CLI process yet
Step 3: Session Creation (triggers auto-start)
const session = await client.createSession({
onPermissionRequest: approveAll,
model: "gpt-4",
});
Internally this triggers the following sub-steps:
3a. Auto-start (client.start())
- State transitions to
"connecting" - Spawn CLI process:
node /path/to/@github/copilot/index.js --stdio --log-level debug --sdk-protocol-version N - Establish JSON-RPC connection: Creates
MessageConnectionover stdio pipes (usingvscode-jsonrpc) - Register notification handler: Listens for
session.eventnotifications from CLI - Register request handlers: Listens for
session.toolCall,session.permissionRequest,session.userInputRequest,session.hooksInvokerequests from CLI (protocol v2) or broadcast events (protocol v3) - Verify protocol version: Issues
status.getRPC, checksprotocolVersion >= MIN_PROTOCOL_VERSION - State transitions to
"connected"
3b. Session registration (before RPC)
- Generate UUID for
sessionId - Create
CopilotSession(sessionId, connection)instance - Register tools, permission handler, user input handler, hooks on the session object
- Register
onEventhandler if provided - Add session to
client.sessionsmap
3c. Session creation RPC
- Issue
session.createJSON-RPC request with full config payload - CLI server creates the session, returns
{ sessionId, workspacePath } - SDK stores
workspacePathon the session object
Step 4: Event Subscription
session.on((event) => {
if (event.type === "assistant.message") {
console.log(event.data.content);
}
});
Registers a callback in the session's eventHandlers set. Supports:
- Wildcard:
session.on(handler)— receives all events - Typed:
session.on("assistant.message", handler)— receives only matching events (TypeScript only)
Returns an unsubscribe function.
Step 5: Send Message
const reply = await session.sendAndWait({ prompt: "Hello!" });
- Register internal event handler (before send, to avoid race conditions)
- Issue
session.sendRPC with{ sessionId, prompt } - CLI processes the message, streams events back via
session.eventnotifications - Events are dispatched to all registered handlers
- Tool calls (protocol v3): CLI broadcasts
external_tool.requestedevent; SDK executes handler and responds viasession.tools.handlePendingToolCall - Permission requests (protocol v3): CLI broadcasts
permission.requestedevent; SDK executes handler and responds viasession.permissions.handlePendingPermissionRequest - Wait for
session.idleevent (orsession.error) - Return the last
assistant.messageevent
Step 6: Cleanup
await session.disconnect();
await client.stop();
session.disconnect(): Issuessession.destroyRPC, clears handlers/tools/permissionsclient.stop(): Disconnects all sessions, closes JSON-RPC connection, terminates CLI process
Sequence Diagram (SDK ↔ CLI message flow)
sequenceDiagram
participant SDK as SDK Client
participant CLI as CLI Server
SDK->>CLI: spawn process (stdio pipes)
SDK->>CLI: status.get
CLI-->>SDK: {version, protocolVersion}
SDK->>CLI: session.create {config}
CLI-->>SDK: {sessionId, workspacePath}
SDK->>CLI: session.send {prompt}
CLI-->>SDK: session.event (streaming tokens)
CLI-->>SDK: session.event: tool.requested
SDK->>CLI: tools.handlePendingToolCall
CLI-->>SDK: session.event: permission.requested
SDK->>CLI: permissions.handlePendingPermission
CLI-->>SDK: session.event: idle
SDK->>CLI: session.destroy
CLI-->>SDK: ok
Appendix: Cross-SDK API Correspondence
flowchart LR
subgraph CONCEPT["<b>Concept</b>"]
direction TB
C1["Client class<br/>Constructor<br/>Start<br/>Stop<br/>Create session<br/>Resume session<br/>Send message<br/>Send and wait<br/>Subscribe<br/>Disconnect<br/>Abort<br/>Set model<br/>Log<br/>Approve all<br/>Define tool<br/>Typed RPC"]
end
subgraph TS["<b>TypeScript</b>"]
direction TB
TS1["CopilotClient<br/>new CopilotClient#40;opts?#41;<br/>await client.start#40;#41;<br/>await client.stop#40;#41;<br/>await client.createSession#40;cfg#41;<br/>await client.resumeSession#40;id, cfg#41;<br/>await session.send#40;opts#41;<br/>await session.sendAndWait#40;opts#41;<br/>session.on#40;handler#41;<br/>await session.disconnect#40;#41;<br/>await session.abort#40;#41;<br/>await session.setModel#40;id#41;<br/>await session.log#40;msg#41;<br/>approveAll<br/>defineTool#40;name, cfg#41;<br/>session.rpc.*"]
end
subgraph PY["<b>Python</b>"]
direction TB
PY1["CopilotClient<br/>CopilotClient#40;opts?#41;<br/>await client.start#40;#41;<br/>await client.stop#40;#41;<br/>await client.create_session#40;cfg#41;<br/>await client.resume_session#40;id, cfg#41;<br/>await session.send#40;opts#41;<br/>await session.send_and_wait#40;opts#41;<br/>session.on#40;handler#41;<br/>await session.disconnect#40;#41;<br/>await session.abort#40;#41;<br/>await session.set_model#40;id#41;<br/>await session.log#40;msg#41;<br/>PermissionHandler.approve_all<br/>@define_tool#40;...#41;<br/>session.rpc.*"]
end
subgraph GO["<b>Go</b>"]
direction TB
GO1["Client<br/>NewClient#40;opts*#41;<br/>client.Start#40;ctx#41;<br/>client.Stop#40;#41;<br/>client.CreateSession#40;ctx, cfg#41;<br/>client.ResumeSession#40;ctx, id, cfg#41;<br/>session.Send#40;ctx, opts#41;<br/>session.SendAndWait#40;ctx, opts#41;<br/>session.On#40;handler#41;<br/>session.Disconnect#40;#41;<br/>session.Abort#40;ctx#41;<br/>session.SetModel#40;ctx, id#41;<br/>session.Log#40;ctx, msg, opts#41;<br/>PermissionHandler.ApproveAll<br/>Tool#123;...#125; struct literal<br/>session.RPC.*"]
end
subgraph CS["<b>C#</b>"]
direction TB
CS1["CopilotClient<br/>new CopilotClient#40;opts?#41;<br/>await client.StartAsync#40;#41;<br/>await client.StopAsync#40;#41;<br/>await client.CreateSessionAsync#40;cfg#41;<br/>await client.ResumeSessionAsync#40;id, cfg#41;<br/>await session.SendAsync#40;opts#41;<br/>await session.SendAndWaitAsync#40;opts#41;<br/>session.On#40;handler#41;<br/>await session.DisposeAsync#40;#41;<br/>await session.AbortAsync#40;#41;<br/>await session.SetModelAsync#40;id#41;<br/>await session.LogAsync#40;msg#41;<br/>PermissionHandler.ApproveAll<br/>AIFunctionFactory.Create#40;...#41;<br/>session.Rpc.*"]
end