# How Conversations Are Managed as Sessions in the Copilot SDK

> Discover how the Copilot SDK manages conversations as CopilotSession objects, handling state, communication, and events throughout the dialogue lifecycle.

- Repository: [GitHub/copilot-sdk](https://github.com/github/copilot-sdk)
- Tags: internals
- Published: 2026-07-18

---

**The Copilot SDK encapsulates every conversation with the CLI as a `CopilotSession` object, which manages state, RPC communication, event handling, and persistence across the dialogue lifecycle.**

The `github/copilot-sdk` repository treats each interaction with GitHub Copilot as a stateful session rather than a stateless request. A **CopilotSession** serves as the central coordination point for message flow, tool execution, permission management, and UI interactions. Understanding how these sessions are architected is essential for building robust integrations that leverage the SDK's full capabilities.

## Understanding Copilot SDK Session Architecture

### Session Lifecycle and Instantiation

Sessions are instantiated through `CopilotClient.createSession` or resumed via `CopilotClient.resumeSession`. According to the source code in [`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts) (lines 61-73), the `CopilotSession` constructor is internal and receives a unique `sessionId`, a JSON-RPC `MessageConnection`, an optional `workspacePath`, and optional MCP authentication handlers.

When you create a new session, the SDK establishes a scoped communication channel:

```typescript
const client = await CopilotClient.create();
const session = await client.createSession({ model: "gpt-4" });

```

The session immediately becomes the owner of all subsequent interactions, maintaining isolated state from other concurrent sessions.

### State Storage and Memory Management

Sessions maintain **in-memory state** through structures including `eventHandlers`, `toolHandlers`, and `openCanvasInstances` (as implemented in [`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts), lines 93-102). When **infinite sessions** are enabled, the SDK persists conversation state to disk using the provided `workspacePath` to store checkpoints, plan files, and artifact files.

This dual-layer storage allows sessions to survive process restarts. The in-memory structures handle live conversation flow, while the workspace directory enables long-running dialogues that can be resumed days later.

## RPC Communication and Message Flow

### Session-Scoped RPC Methods

Each session exposes a typed RPC façade through the `rpc` getter. As defined in [`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts) (lines 81-86), this lazily creates a session-scoped RPC client via `createSessionRpc` that exposes methods under the session namespace, including `session.send` and `session.getMessages`.

This design ensures that RPC calls are automatically routed to the correct conversation context without manual session ID management.

### Sending Messages and Waiting for Responses

The SDK provides two primary methods for message transmission:

- **`send`**: Forwards a prompt to the server via `session.send` (fire-and-forget)
- **`sendAndWait`**: Builds on `send` and listens for `assistant.message` and `session.idle` events, returning the final assistant message or timing out

According to [`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts) (lines 44-62 and 86-106), `sendAndWait` handles the complexity of streaming responses, aggregating partial messages until the session signals idleness.

```typescript
const result = await session.sendAndWait({ 
  prompt: "Explain the Singleton pattern." 
});
console.log(result?.data.content);

```

## Event Handling and Broadcasting

### Event Subscription Patterns

Sessions expose an `on` method for registering event listeners. As implemented in [`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts) (lines 85-112), you can subscribe to specific event types (`on(eventType, handler)`) or register wildcard listeners (`on(handler)`) that receive all events.

This pattern enables reactive architectures where UI updates, logging, or side effects respond to conversation state changes:

```typescript
const unsubscribe = session.on("assistant.message", (event) => {
  console.log("Assistant:", event.data.content);
});

```

### Broadcast Event Routing

Internally, `_dispatchEvent` routes events to registered handlers. For broadcast requests—including tool calls, permission requests, MCP auth, commands, and elicitation—the session uses `_handleBroadcastEvent` to match incoming requests with registered handlers and manage asynchronous responses.

## Tool Execution and Permission Management

### Registering Custom Tools

Custom tools are registered via `registerTools`, which accepts an array of tool definitions with names, descriptions, and handlers. When the runtime emits `external_tool.requested`, the session's `_handleBroadcastEvent` forwards the call to the matching `ToolHandler`, captures the result, and replies through `rpc.tools.handlePendingToolCall` (as shown in [`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts), lines 84-108).

```typescript
session.registerTools([
  {
    name: "weather.fetch",
    description: "Fetch current weather for a city.",
    handler: async (args) => {
      const { city } = args as { city: string };
      const resp = await fetch(`https://api.weather.com/v3/${city}`);
      const data = await resp.json();
      return `Temperature: ${data.temp}°C`;
    },
  },
]);

```

### Handling Permission Requests

Permission requests (`permission.requested`) are routed to user-provided `PermissionHandler` callbacks. The session manages the request lifecycle, sending results back via `rpc.permissions.handlePendingPermissionRequest` (lines 108-123).

```typescript
session.registerPermissionHandler(async (request) => {
  if (request.action === "file.write" && request.path.endsWith(".txt")) {
    return { kind: "allowed" };
  }
  return { kind: "denied", reason: "Only .txt files are allowed" };
});

```

### MCP Authentication Flows

For Model Context Protocol (MCP) integrations, the session handles OAuth requirements (`mcp.oauth_required`) through registered `McpAuthHandler` instances, responding via `rpc.mcp.oauth.handlePendingRequest` (lines 120-138).

## Session Persistence and Resume Capabilities

### Infinite Sessions and Workspace Storage

When infinite sessions are enabled, the `workspacePath` parameter triggers persistent storage of conversation checkpoints and artifacts. This allows the session to maintain context across application restarts, storing plan files and intermediate states on disk while keeping the active conversation state in memory.

### Graceful Disconnection and Cleanup

The `disconnect()` method (lines 124-135) sends `session.destroy` to the server while preserving on-disk data for future resumption. This triggers `_markDisconnected`, which clears all in-memory handlers and event listeners without deleting persisted workspace data.

To fully remove a session, including its persisted state, use `CopilotClient.deleteSession`:

```typescript
await session.disconnect();  // Keep for resume
await client.deleteSession(sessionId);  // Permanent removal

```

Resuming a session restores the workspace and re-populates open canvases:

```typescript
const resumed = await client.resumeSession("abc123");
await resumed.sendAndWait({ prompt: "Continue where we left off." });

```

## Cross-Language Session Consistency

The Copilot SDK maintains architectural parity across language bindings. Each implementation follows the same session management patterns:

- **Rust**: [`rust/src/session.rs`](https://github.com/github/copilot-sdk/blob/main/rust/src/session.rs) implements the session struct with RPC façade and event dispatch
- **Go**: [`go/session.go`](https://github.com/github/copilot-sdk/blob/main/go/session.go) provides equivalent tool and permission callback handling
- **Python**: [`python/copilot/session.py`](https://github.com/github/copilot-sdk/blob/main/python/copilot/session.py) offers the same session class structure, RPC calls, and UI helpers

All bindings support session-scoped RPC, event dispatch, tool/permission handling, and persistence, ensuring consistent behavior regardless of implementation language.

## Summary

- **CopilotSession** objects encapsulate all state for a single conversation, providing scoped RPC methods and event handling.
- Sessions support both **in-memory** operation and **persistent storage** via workspace directories when infinite sessions are enabled.
- The **RPC façade** (`rpc` getter) exposes type-safe methods like `send` and `sendAndWait` that handle message streaming and completion detection.
- **Event handling** uses a subscription model with support for both specific event types and wildcard listeners, routing tool calls and permissions through `_handleBroadcastEvent`.
- **Tool execution** and **permission management** are handled via registered callbacks that communicate results back to the runtime through session-scoped RPC.
- Sessions can be **resumed** using `resumeSession` with their `sessionId`, restoring workspace state and conversation context across process lifecycles.

## Frequently Asked Questions

### What is the difference between `disconnect()` and `deleteSession()` in the Copilot SDK?

`disconnect()` gracefully terminates the active session connection while preserving any persisted workspace data for later resumption. It sends `session.destroy` to the server and clears in-memory handlers via `_markDisconnected`. In contrast, `deleteSession()` (called on `CopilotClient`) permanently removes both the session reference and all associated on-disk artifacts, making the session ID invalid for future `resumeSession` calls.

### How does the Copilot SDK handle long-running conversations that exceed memory limits?

The SDK supports **infinite sessions** that persist conversation state to disk using the `workspacePath` parameter. When enabled, the session stores checkpoints, plan files, and artifacts in the workspace directory, allowing the conversation to be resumed later with `CopilotClient.resumeSession`. This architecture prevents memory bloat by offloading historical context to disk while maintaining active state in memory.

### Can multiple sessions exist simultaneously in the same process?

Yes, the Copilot SDK supports multiple concurrent sessions. Each session maintains isolated state through its own `CopilotSession` instance with unique `sessionId` values and separate RPC connections. Sessions do not share event handlers, tool registrations, or permission handlers, ensuring that conversations remain isolated even when operating within the same `CopilotClient` instance.

### What events indicate that a conversation turn has completed?

The SDK uses the **`session.idle`** event to signal conversation completion, which `sendAndWait` listens for alongside **`assistant.message`** events. When the assistant finishes generating content and the session enters an idle state, `sendAndWait` resolves with the final aggregated message. You can also subscribe to these events directly using `session.on("session.idle", handler)` for custom flow control.