# How to Stream Session Events and Track Sub-Agent Lifecycle in the Copilot SDK

> Learn to stream session events and track sub-agent lifecycle in the Copilot SDK. Register an event handler to monitor sub-agent states like start, progress, and termination.

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

---

**Register an event handler with `session.on()` to capture real-time `SessionEvent` streams and filter by event type to monitor sub-agent start, progress, and termination states.**

The `github/copilot-sdk` exposes a **Session** object that serves as the central entry point for all interactions with the Copilot CLI runtime. To build responsive applications that react to incremental assistant responses or monitor complex tool-driven workflows, you must tap into the session's event streaming API and understand how sub-agent lifecycles are reported through that same channel.

## Setting Up Session Event Streaming

The primary mechanism for streaming session events is the `session.on()` method, defined in [`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts) at lines 668-671. This method registers a handler function that receives a continuous stream of **SessionEvent** objects while the session processes a request.

Unlike the `session.send()` method—which merely returns a message ID and triggers processing—the event stream captures incremental content such as partial assistant responses, tool execution results, and runtime status changes. 

```typescript
import { createSession } from "@github/copilot-sdk";

const session = await createSession();

// Subscribe to all session events
const unsubscribe = session.on((event) => {
  console.log(`[${event.type}]`, event.data);
  
  if (event.type === "assistant.message_delta") {
    processPartialMessage(event.data);
  }
});

// send() returns an ID but does not block for the full response
const msgId = await session.send({ prompt: "Analyze the codebase" });
console.log("Message sent, id =", msgId);
// Event handler continues receiving deltas until unsubscribed

```

## Tracking Sub-Agent Lifecycle Events

When a tool call spawns a **sub-agent**—for example, a custom tool that runs its own model inference—the runtime emits dedicated lifecycle events through the same event stream. These events allow you to correlate sub-agent activity with their parent tool invocations.

### Key Event Types

According to [`nodejs/src/generated/session-events.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/generated/session-events.ts), you can track sub-agents by filtering on these **event type** discriminators:

- **`subagent.start`** – Emitted when a sub-agent begins execution
- **`subagent.stop`** – Emitted upon completion, includes duration and token consumption metrics
- **`assistant.message_delta`** – Streaming fragments originating from sub-agent models

### Parent-Tool Linkage

Sub-agent events carry linkage fields that map them to their originating tool call. In [`nodejs/src/generated/session-events.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/generated/session-events.ts) at lines 3659-3661, events include a `parentToolCallId` field that references the specific tool invocation that spawned the sub-agent:

```typescript
session.on((event) => {
  if (event.type === "subagent.start") {
    console.log(`Sub-agent ${event.data.subAgentId} started from tool ${event.data.parentToolCallId}`);
  }
  
  if (event.type === "subagent.stop") {
    // Lines 5454-5533 in session-events.ts define these fields:
    console.log(`Sub-agent completed in ${event.data.durationMs}ms`);
    console.log(`Total tokens consumed: ${event.data.tokenCount}`);
  }
});

```

## Configuring Sub-Agent Event Visibility

By default, the SDK forwards all sub-agent streaming events to your handler. If you need to reduce event noise or only care about top-level session activity, disable this behavior via the **SessionConfig** interface.

In [`nodejs/src/types.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/types.ts) at lines 2418-2423, the `includeSubAgentStreaming` boolean controls whether delta events from sub-agents are forwarded:

```typescript
const session = await createSession({
  includeSubAgentStreaming: false  // Suppress sub-agent delta events
});

// This handler will receive only top-level session events
session.on(e => console.log(e.type));

```

## Complete Implementation Example

Combine streaming registration with lifecycle tracking to build a robust monitoring layer:

```typescript
import { createSession, SessionEvent } from "@github/copilot-sdk";

async function monitorSessionWithSubAgents() {
  const session = await createSession({
    includeSubAgentStreaming: true  // Ensure we see all sub-agent activity
  });

  const activeSubAgents = new Set<string>();

  const unsubscribe = session.on((event: SessionEvent) => {
    // Handle real-time content streaming
    if (event.type === "assistant.message_delta") {
      process.stdout.write(event.data.content);
    }
    
    // Track sub-agent lifecycle
    if (event.type === "subagent.start") {
      activeSubAgents.add(event.data.subAgentId);
      console.log(`\n[Sub-agent ${event.data.subAgentId} started]`);
    }
    
    if (event.type === "subagent.stop") {
      activeSubAgents.delete(event.data.subAgentId);
      console.log(`\n[Sub-agent ${event.data.subAgentId} finished: ${event.data.summary}]`);
    }
    
    // Error handling
    if (event.type === "error") {
      console.error("Runtime error:", event.data.message);
    }
  });

  // Trigger a request that spawns sub-agents via tool calls
  await session.send({
    prompt: "Read ./config.json and validate it against the schema",
    tools: [{ name: "view", description: "Read file contents" }]
  });

  // Cleanup when done
  // unsubscribe();
}

```

## Summary

- **Register handlers** using `session.on()` in [`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts) to capture real-time events rather than polling `session.send()` results.
- **Filter by event type** to distinguish between assistant messages, tool calls, and sub-agent lifecycle events ("subagent.start", "subagent.stop").
- **Correlate sub-agents** to parent tools via the `parentToolCallId` field defined in [`nodejs/src/generated/session-events.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/generated/session-events.ts).
- **Control verbosity** with the `includeSubAgentStreaming` option in `SessionConfig` (lines 2418-2423 of [`nodejs/src/types.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/types.ts)) when you only need top-level session data.

## Frequently Asked Questions

### What is the difference between the return value of `session.send()` and session events?

The `session.send()` method returns a **message ID** string immediately after dispatching the request to the runtime, as documented in [`nodejs/src/session.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/session.ts) at lines 664-672. However, the actual response content—including incremental assistant messages and tool results—arrives asynchronously through the **SessionEvent** stream subscribed via `session.on()`. The message ID tracks the request scope, while the event stream delivers the actual execution data.

### How do I distinguish between main agent and sub-agent events in the stream?

Sub-agent events include a `parentToolCallId` field (defined in [`nodejs/src/generated/session-events.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/generated/session-events.ts), lines 3659-3661) that references the specific tool invocation that spawned them. Main agent events lack this field. Additionally, sub-agent events emit specific lifecycle types such as **"subagent.start"** and **"subagent.stop"**, whereas main agent events typically use **"assistant.message_delta"** without parent linkage.

### Can I disable sub-agent event streaming to reduce noise?

Yes. Set `includeSubAgentStreaming: false` in your `SessionConfig` when calling `createSession()`. According to [`nodejs/src/types.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/types.ts) (lines 2418-2423), this option prevents forwarding of streaming delta events from sub-agents to your session handler, though lifecycle events like "subagent.start" may still be emitted depending on runtime configuration.

### What metrics are available when a sub-agent terminates?

The **"subagent.stop"** event includes detailed lifecycle metadata defined in [`nodejs/src/generated/session-events.ts`](https://github.com/github/copilot-sdk/blob/main/nodejs/src/generated/session-events.ts) (lines 5454-5533). This payload typically contains the **wall-clock duration**, **total tokens consumed**, **subAgentId**, and a **summary** description of what the sub-agent accomplished during its execution.