Phase 4/13 — Entry Points

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:

SymbolFileLine
Client (struct)go/client.go73
NewClient (constructor)go/client.go119
Session (struct)go/session.go51
ClientOptionsgo/types.go19
SessionConfiggo/types.go349
ResumeSessionConfiggo/types.go443
PermissionHandler (var)go/permissions.go4
Toolgo/types.go412
ToolHandlergo/types.go430
SessionEventimplicitly from event types
Bool, String, Float64 (helpers)go/types.go69-83

1.4 C# / .NET

dotnet/src/Client.cs, dotnet/src/Session.cs, dotnet/src/Types.cs

Namespace: GitHub.Copilot.SDK

SymbolFileLine
CopilotClientdotnet/src/Client.cs55
CopilotSessiondotnet/src/Session.cs53
CopilotClientOptionsdotnet/src/Types.cs38
SessionConfigdotnet/src/Types.cs(exported record)
ResumeSessionConfigdotnet/src/Types.cs(exported record)
PermissionHandlerdotnet/src/Types.cs(static class)
ConnectionStatedotnet/src/Types.cs19
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

OptionTypeDefaultDescription
cliPathstring?bundled CLIPath to CLI executable
cliArgsstring[]?[]Extra CLI arguments
cwdstring?process.cwd()Working directory
portnumber?0 (random)TCP port (TCP mode)
useStdioboolean?trueUse stdio transport
isChildProcessboolean?falseRunning as child of CLI
cliUrlstring?Connect to existing server
logLevelstring?"debug"CLI log level
autoStartboolean?trueAuto-start on first use
autoRestartboolean?trueAuto-restart on crash
envRecord<string,string>?process.envEnvironment variables
githubTokenstring?GitHub auth token
useLoggedInUserboolean?trueUse stored OAuth/gh CLI auth
onListModels() => ModelInfo[]?Custom model listing
Mutual Exclusions

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

OptionTypeDefaultDescription
cli_pathstrbundled binaryPath to CLI executable
cli_argslist[str][]Extra CLI arguments
cwdstros.getcwd()Working directory
portint0TCP port
use_stdioboolTrueUse stdio transport
cli_urlstrConnect to existing server
log_levelLogLevel"info"CLI log level
auto_startboolTrueAuto-start on first use
auto_restartboolTrueAuto-restart on crash
envdict[str,str]inheritedEnvironment variables
github_tokenstrGitHub auth token
use_logged_in_userboolTrueUse stored auth
on_list_modelsCallableCustom model listing

2.3 Go ClientOptions

go/types.go:19-65

OptionTypeDefaultDescription
CLIPathstring"" (auto-detect)Path to CLI executable
CLIArgs[]stringnilExtra CLI arguments
Cwdstring"" (inherit)Working directory
Portint0TCP port
UseStdio*boolnil (true)Use stdio transport
CLIUrlstring""Connect to existing server
LogLevelstring"info"CLI log level
AutoStart*boolnil (true)Auto-start on first use
AutoRestart*boolnil (true)Auto-restart on crash
Env[]stringos.Environ()Environment variables
GitHubTokenstring""GitHub auth token
UseLoggedInUser*boolnil (true)Use stored auth
OnListModelsfunc(ctx) ([]ModelInfo, error)nilCustom model listing

2.4 C# CopilotClientOptions

dotnet/src/Types.cs:38+

PropertyTypeDefaultDescription
CliPathstring?null (bundled)Path to CLI executable
CliArgsIList<string>?nullExtra CLI arguments
Cwdstring?null (inherit)Working directory
Portint?nullTCP port
UseStdiobool?trueUse stdio transport
CliUrlstring?nullConnect to existing server
LogLevelstring?nullCLI log level
AutoStartbooltrueAuto-start on first use
AutoRestartbooltrueAuto-restart on crash
GitHubTokenstring?nullGitHub auth token
UseLoggedInUserbool?null (true)Use stored auth
LoggerILogger?NullLoggerCustom logger
OnListModelsFunc<CancellationToken, Task<List<ModelInfo>>>?nullCustom 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.

FieldRequiredDescription
onPermissionRequestYesHandler for tool permission requests
modelNoModel ID (e.g., "gpt-4")
sessionIdNoCustom session ID (auto-generated UUID)
clientNameNoApplication identifier for User-Agent
toolsNoCustom tool definitions
systemMessageNoSystem prompt config (append/replace)
availableToolsNoAllowlist of tool names
excludedToolsNoDenylist of tool names
providerNoBYOK provider config
onUserInputRequestNoHandler for ask_user tool
hooksNoSession lifecycle hooks
workingDirectoryNoCWD for tool operations
streamingNoEnable delta streaming events
mcpServersNoMCP server configurations
customAgentsNoCustom agent definitions
agentNoAgent to activate on start
skillDirectoriesNoDirectories to load skills from
disabledSkillsNoSkill denylist
infiniteSessionsNoInfinite session / compaction config
reasoningEffortNo"low" / "medium" / "high" / "xhigh"
onEventNoEarly event handler (before RPC)

3.5 Session Lifecycle

Across all SDKs, the session creation sequence is:

  1. Validate onPermissionRequest is provided (throws if missing)
  2. Ensure client is connected (auto-start if enabled)
  3. Generate session ID if not provided (UUID)
  4. Create CopilotSession object and register it before the RPC call (to avoid missing early events)
  5. Register tools, permission handler, user input handler, hooks, and early event handler on the session
  6. Issue session.create JSON-RPC request with serialized config
  7. Return the session object (with workspace path if infinite sessions enabled)

Cleanup Methods

SDKDisconnectDeprecated AliasDispose Pattern
TypeScriptsession.disconnect()session.destroy()Symbol.asyncDispose
Pythonawait session.disconnect()await session.destroy()async with context manager
Gosession.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:

SDKApprove-all HandlerFile:Line
TypeScriptapproveAll (exported const)nodejs/src/types.ts:251
PythonPermissionHandler.approve_all (static method)python/copilot/types.py:211-215
GoPermissionHandler.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:

HookInputTrigger
onPreToolUsePreToolUseHookInputBefore a tool executes
onPostToolUsePostToolUseHookInputAfter a tool executes
onUserPromptSubmittedUserPromptSubmittedHookInputUser sends a prompt
onSessionStartSessionStartHookInputSession begins
onSessionEndSessionEndHookInputSession terminates
onErrorOccurredErrorOccurredHookInputAn 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:

  1. Create client (optionally with explicit CLI path)
  2. Start client (auto or explicit)
  3. Create session with approveAll permission handler
  4. Register event handler for reasoning and tool events
  5. REPL loop: read input, call sendAndWait, print response
  6. 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:

ModeDefaultDescription
stdioYesSDK spawns CLI process, communicates via stdin/stdout pipes
TCPNoSDK 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:

MethodDirectionPurpose
status.getSDK → CLIGet server version and protocol version
session.createSDK → CLICreate a new session
session.resumeSDK → CLIResume an existing session
session.sendSDK → CLISend a user message
session.getMessagesSDK → CLIGet conversation history
session.abortSDK → CLIAbort current processing
session.destroySDK → CLIDisconnect session
session.deleteSDK → CLIPermanently delete session data
session.listSDK → CLIList sessions with optional filter
session.eventCLI → SDKPush session events (notification)
auth.getStatusSDK → CLICheck authentication status
models.listSDK → CLIList available models
session.model.switchToSDK → CLIChange session model
session.tools.handlePendingToolCallSDK → CLIReturn tool execution result
session.permissions.handlePendingPermissionRequestSDK → CLIReturn permission decision
session.logSDK → CLILog 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())

  1. State transitions to "connecting"
  2. Spawn CLI process: node /path/to/@github/copilot/index.js --stdio --log-level debug --sdk-protocol-version N
  3. Establish JSON-RPC connection: Creates MessageConnection over stdio pipes (using vscode-jsonrpc)
  4. Register notification handler: Listens for session.event notifications from CLI
  5. Register request handlers: Listens for session.toolCall, session.permissionRequest, session.userInputRequest, session.hooksInvoke requests from CLI (protocol v2) or broadcast events (protocol v3)
  6. Verify protocol version: Issues status.get RPC, checks protocolVersion >= MIN_PROTOCOL_VERSION
  7. State transitions to "connected"

3b. Session registration (before RPC)

  1. Generate UUID for sessionId
  2. Create CopilotSession(sessionId, connection) instance
  3. Register tools, permission handler, user input handler, hooks on the session object
  4. Register onEvent handler if provided
  5. Add session to client.sessions map

3c. Session creation RPC

  1. Issue session.create JSON-RPC request with full config payload
  2. CLI server creates the session, returns { sessionId, workspacePath }
  3. SDK stores workspacePath on 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!" });
  1. Register internal event handler (before send, to avoid race conditions)
  2. Issue session.send RPC with { sessionId, prompt }
  3. CLI processes the message, streams events back via session.event notifications
  4. Events are dispatched to all registered handlers
  5. Tool calls (protocol v3): CLI broadcasts external_tool.requested event; SDK executes handler and responds via session.tools.handlePendingToolCall
  6. Permission requests (protocol v3): CLI broadcasts permission.requested event; SDK executes handler and responds via session.permissions.handlePendingPermissionRequest
  7. Wait for session.idle event (or session.error)
  8. Return the last assistant.message event

Step 6: Cleanup

await session.disconnect();
await client.stop();
  • session.disconnect(): Issues session.destroy RPC, clears handlers/tools/permissions
  • client.stop(): Disconnects all sessions, closes JSON-RPC connection, terminates CLI process

Sequence Diagram (SDK ↔ CLI message flow)

SDK / CLI Bootstrap Sequence
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

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