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

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.

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). 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.

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.

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.

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

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:

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 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, 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. This JSON Lines file is read on resume and can be parsed for debugging, audit trails, or UI timelines.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →