# How to Manage Copilot SDK Session Lifecycle Events: Start, End, and Agent Stop

> Master Copilot SDK session lifecycle events like start, end, and agent stop. Learn to manage session events effectively using EventEmitter for robust application development.

- Repository: [GitHub/copilot-sdk](https://github.com/github/copilot-sdk)
- Tags: how-to-guide
- Published: 2026-08-02

---

**The Copilot SDK emits type-safe session lifecycle events—`session.start`, `session.resume`, `session.idle`, `session.shutdown`, and `subagent.*` events—through an EventEmitter interface on `client.session`, with all events persisted to `events.jsonl` for replay.**

The Copilot SDK models a *session* as the lifetime of a single interaction context with the runtime (CLI or VS Code extension). Understanding how to hook into **Copilot SDK session lifecycle events** is essential for building robust integrations that respond to state changes, track sub-agent execution, and recover from interruptions. This guide covers the five core event types, their payloads, and practical implementation patterns using the auto-generated types in [`nodejs/src/generated/session-events.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/generated/session-events.ts).

## Core Session Lifecycle Events

The SDK defines session lifecycle events through a discriminated union type in [[`nodejs/src/generated/session-events.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/generated/session-events.ts)](https://github.com/github/copilot-sdk/blob/main/nodejs/src/generated/session-events.ts). Each event carries a `type` discriminator string that you use when registering listeners.

| Event | Emitted When | Key Payload Fields |
|-------|-----------|------------------|
| **`session.start`** | New session created (first client connection) | `sessionId`, `copilotVersion`, `producer`, `startTime`, optional `context` |
| **`session.resume`** | Client reconnects to existing on-disk session | `sessionId`, `eventCount`, `resumeTime`, `continuePendingWork` |
| **`session.idle`** | Runtime finishes all queued work and becomes quiescent | `aborted?` — true if previous turn was cancelled |
| **`session.shutdown`** | Session terminated (graceful or error) | `shutdownType` (`"routine"` \| `"error"`), optional `error` details |

### Handling Session Start and Resume

A session begins with `session.start` and may later emit `session.resume` if the client reconnects—for example, when VS Code reopens or a new terminal tab attaches to an existing session.

```typescript
import { CopilotClient } from '@github/copilot-sdk';

const client = new CopilotClient();

client.session.on('session.start', (ev) => {
  console.log('🟢 Session started:', ev.data.sessionId);
  console.log('Copilot version:', ev.data.copilotVersion);
  console.log('Working directory:', ev.data.context?.workingDirectory);
});

client.session.on('session.resume', (ev) => {
  console.log('🔁 Resumed session:', ev.data.sessionId);
  console.log('Previous events:', ev.data.eventCount);
  console.log('Continuing pending work:', ev.data.continuePendingWork);
});

```

The `continuePendingWork` boolean is critical: when `true`, in-flight tool calls from the previous connection are preserved rather than cancelled.

### Detecting Idle and Shutdown States

Use `session.idle` to trigger cleanup or UI updates when no work remains. The `session.shutdown` event signals final termination—distinguish between routine exits and errors via `shutdownType`.

```typescript
client.session.on('session.idle', (ev) => {
  if (ev.data.aborted) {
    console.log('⚠️ Previous turn was aborted');
  }
  console.log('💤 Session idle — ready for new input');
});

client.session.on('session.shutdown', (ev) => {
  const { shutdownType, error } = ev.data;
  if (shutdownType === 'error') {
    console.error('💥 Fatal error shutdown:', error?.message);
  } else {
    console.log('🔴 Routine shutdown');
  }
});

```

## Sub-Agent Lifecycle Events

The SDK treats sub-agents (skills or remote-steerable agents) as nested workloads with their own **start**, **completion**, and **failure** events. These are emitted through the same `client.session` emitter.

| Event | Purpose | Payload |
|-------|---------|---------|
| **`subagent.started`** | Agent instance launched | `agentId`, optional `metadata` |
| **`subagent.completed`** | Agent finished successfully | `agentId` |
| **`subagent.failed`** | Agent crashed or aborted | `agentId`, `error` with `message` and optional `stack` |

### Tracking Agent Start and Stop

Register listeners to monitor agent execution, implement timeouts, or update progress indicators.

```typescript
client.session.on('subagent.started', (ev) => {
  console.log(`🚀 Sub-agent "${ev.data.agentId}" started`);
  // Start progress tracking, set timeouts, etc.
});

client.session.on('subagent.completed', (ev) => {
  console.log(`✅ Sub-agent "${ev.data.agentId}" completed`);
  // Cleanup progress state
});

client.session.on('subagent.failed', (ev) => {
  console.error(`❌ Sub-agent "${ev.data.agentId}" failed:`);
  console.error(ev.data.error.message);
  if (ev.data.error.stack) {
    console.error(ev.data.error.stack);
  }
  // Trigger retry logic or alert user
});

```

Sub-agents are created via the `session.mcp.apps.launchAgent` RPC. Their lifecycle events flow through the same emitter, enabling unified observation of nested workloads.

## Event Persistence and Replay

All non-ephemeral events are appended to `events.jsonl` in the session directory. This log enables **state reconstruction** on resume and supports debugging or timeline UIs.

### Replaying Events from Disk

```typescript
import { readFile } from 'fs/promises';

async function replaySessionEvents(sessionDir: string): Promise<void> {
  const filePath = `${sessionDir}/events.jsonl`;
  const raw = await readFile(filePath, 'utf-8');
  
  for (const line of raw.split('\n').filter(Boolean)) {
    const event = JSON.parse(line);
    
    switch (event.type) {
      case 'session.start':
        console.log('▶️ Start:', event.data.sessionId);
        break;
      case 'session.idle':
        console.log('⏸️ Idle');
        break;
      case 'subagent.started':
        console.log('🚀 Agent:', event.data.agentId);
        break;
      case 'subagent.completed':
        console.log('✅ Agent done:', event.data.agentId);
        break;
      case 'session.shutdown':
        console.log('⏹️ Shutdown:', event.data.shutdownType);
        break;
    }
  }
}

```

The **event log** is read on `resume` to reconstruct session state, including `eventCount` and pending work status.

## Key Implementation Files

These source files define and implement **Copilot SDK session lifecycle events**:

- **[`nodejs/src/generated/session-events.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/generated/session-events.ts)** — Auto-generated TypeScript interfaces for all event types (`StartEvent`, `ResumeEvent`, `ShutdownEvent`, `SubagentStartedEvent`, etc.)
- **[`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts)** — Core session management: creates the EventEmitter, writes to `events.jsonl`, implements `start`, `resume`, and `shutdown` logic
- **[`nodejs/src/client.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/client.ts)** — High-level SDK entry point exposing `client.session` (the EventEmitter consumed by applications)
- **[`docs/hooks/session-lifecycle.md`](https://github.com/github/copilot-sdk/blob/main/docs/hooks/session-lifecycle.md)** — Official documentation of event semantics and usage patterns

## Summary

- **Five core events** track session state: `session.start`, `session.resume`, `session.idle`, `session.shutdown`, plus `subagent.*` events for nested workloads
- **Register listeners** on `client.session` using the `type` discriminator strings for type-safe event handling
- **Persisted log** at `events.jsonl` enables session recovery and event replay
- **Sub-agent events** provide granular visibility into skill and remote agent execution
- **Auto-generated types** in [`session-events.ts`](https://github.com/github/copilot-sdk/blob/main/session-events.ts) ensure compile-time validation and IDE autocomplete

## Frequently Asked Questions

### How do I distinguish between a new session and a resumed session?

Listen for `session.start` (new session) versus `session.resume` (reconnected session). The `ResumeEvent` payload includes `eventCount` and `continuePendingWork` to indicate prior state. In [`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts), the session manager reads `events.jsonl` on resume to populate these fields.

### What happens to in-flight work when a client disconnects?

The `continuePendingWork` field in `ResumeEvent` indicates whether pending tool calls are preserved. When `true`, the session resumes where it left off; when `false`, work was cancelled and `session.idle` will have `aborted: true`.

### How can I handle sub-agent failures gracefully?

Register a listener for `subagent.failed`. The event payload includes `agentId` and an `error` object with `message` and optional `stack`. Implement retry logic, user notifications, or fallback behaviors based on `agentId` to route errors appropriately.

### Where are session events actually persisted?

Events are appended to `events.jsonl` in the session directory, as implemented in [`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts). This JSON Lines file is read on resume and can be parsed for debugging, audit trails, or UI timelines.