{"meta":{"title":"Streaming session events","intro":"Every action the Copilot agent takes—thinking, writing code, running tools—is emitted as a session event you can subscribe to. This guide is a field-level reference for each event type so you know exactly what data to expect without reading the SDK source.","product":"GitHub Copilot","breadcrumbs":[{"href":"/en/copilot","title":"GitHub Copilot"},{"href":"/en/copilot/how-tos","title":"How-tos"},{"href":"/en/copilot/how-tos/copilot-sdk","title":"Copilot SDK"},{"href":"/en/copilot/how-tos/copilot-sdk/features","title":"Features"},{"href":"/en/copilot/how-tos/copilot-sdk/features/streaming-events","title":"Streaming Events"}],"documentType":"article"},"body":"# Streaming session events\n\nEvery action the Copilot agent takes—thinking, writing code, running tools—is emitted as a session event you can subscribe to. This guide is a field-level reference for each event type so you know exactly what data to expect without reading the SDK source.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n## Overview\n\nWhen `streaming: true` is set on a session, the SDK emits **ephemeral** events in real time (deltas, progress updates) alongside **persisted** events (complete messages, tool results). All events share a common envelope and carry a `data` payload whose shape depends on the event `type`.\n\n![Diagram: Sequence diagram showing the described process.](/assets/images/help/copilot/copilot-sdk/features-streaming-events-diagram-0.png)\n\n| Concept              | Description                                                                                                |\n| -------------------- | ---------------------------------------------------------------------------------------------------------- |\n| **Ephemeral event**  | Transient; streamed in real time but **not** persisted to the session log. Not replayed on session resume. |\n| **Persisted event**  | Saved to the session event log on disk. Replayed when resuming a session.                                  |\n| **Delta event**      | An ephemeral streaming chunk (text or reasoning). Accumulate deltas to build the complete content.         |\n| **`parentId` chain** | Each event's `parentId` points to the previous event, forming a linked list you can walk.                  |\n\n## Event envelope\n\nEvery session event, regardless of type, includes these fields:\n\n| Field       | Type                | Description                                                                                                |\n| ----------- | ------------------- | ---------------------------------------------------------------------------------------------------------- |\n| `id`        | `string` (UUID v4)  | Unique event identifier                                                                                    |\n| `timestamp` | `string` (ISO 8601) | When the event was created                                                                                 |\n| `parentId`  | `string \\| null`    | ID of the previous event in the chain; `null` for the first event                                          |\n| `agentId`   | `string?`           | Sub-agent instance ID for sub-agent-originated events; absent for root/main agent and session-level events |\n| `ephemeral` | `boolean?`          | `true` for transient events; absent or `false` for persisted events                                        |\n| `type`      | `string`            | Event type discriminator (see tables below)                                                                |\n| `data`      | `object`            | Event-specific payload                                                                                     |\n\n## Subscribing to events\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\n// All events\nsession.on((event) => {\n    console.log(event.type, event.data);\n});\n\n// Specific event type — data is narrowed automatically\nsession.on(\"assistant.message_delta\", (event) => {\n    process.stdout.write(event.data.deltaContent);\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot.session_events import SessionEventType\n\ndef handle(event):\n    if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:\n        print(event.data.delta_content, end=\"\", flush=True)\n\nsession.on(handle)\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\nsession.On(func(event copilot.SessionEvent) {\n    if d, ok := event.Data.(*copilot.AssistantMessageDeltaData); ok {\n        fmt.Print(d.DeltaContent)\n    }\n})\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nsession.On<SessionEvent>(evt =>\n{\n    if (evt is AssistantMessageDeltaEvent delta)\n    {\n        Console.Write(delta.Data.DeltaContent);\n    }\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n<!-- docs-validate: skip -->\n\n```java\n// All events\nsession.on(event -> System.out.println(event.getType()));\n\n// Specific event type — data is narrowed to the matching class\nsession.on(AssistantMessageDeltaEvent.class, event ->\n    System.out.print(event.getData().deltaContent())\n);\n```\n\n</div>\n\n</div>\n\n> \\[!TIP]\n> **(Python / Go)** These SDKs use separate, per-event data types (for example, `AssistantMessageDeltaData`), so only the relevant fields exist on each type.\n>\n> \\[!TIP]\n> **(.NET)** The .NET SDK uses separate, strongly-typed data classes per event (e.g., `AssistantMessageDeltaData`), so only the relevant fields exist on each type.\n>\n> \\[!TIP]\n> **(TypeScript)** The TypeScript SDK uses a discriminated union—when you match on `event.type`, the `data` payload is automatically narrowed to the correct shape.\n\n## Render only the parent agent response\n\nSub-agent events share the parent session stream and include envelope-level `agentId`. Root/main agent events and session-level events omit `agentId`, so main-chat renderers can ignore assistant events where `agentId` is set and route those events to traces or progress UI instead.\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nimport type { CopilotSession } from \"@github/copilot-sdk\";\n\nexport function subscribeParentResponse(session: CopilotSession): void {\n    session.on(\"assistant.message_delta\", (event) => {\n        if (!event.agentId) {\n            process.stdout.write(event.data.deltaContent);\n        }\n    });\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot import CopilotSession, SessionEvent, SessionEventType\nfrom copilot.session_events import AssistantMessageDeltaData\n\ndef subscribe_parent_response(session: CopilotSession) -> None:\n    def handle(event: SessionEvent) -> None:\n        if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA and event.agent_id is None:\n            data = event.data\n            if isinstance(data, AssistantMessageDeltaData):\n                print(data.delta_content, end=\"\", flush=True)\n\n    session.on(handle)\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\npackage example\n\nimport (\n\t\"fmt\"\n\n\tcopilot \"github.com/github/copilot-sdk/go\"\n)\n\nfunc subscribeParentResponse(session *copilot.Session) {\n\tsession.On(func(event copilot.SessionEvent) {\n\t\tif event.AgentID != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif d, ok := event.Data.(*copilot.AssistantMessageDeltaData); ok {\n\t\t\tfmt.Print(d.DeltaContent)\n\t\t}\n\t})\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nusing System;\nusing GitHub.Copilot;\n\nstatic class ParentAgentResponseExample\n{\n    public static void SubscribeParentResponse(CopilotSession session)\n    {\n        session.On<AssistantMessageDeltaEvent>(evt =>\n        {\n            if (evt.AgentId is null)\n            {\n                Console.Write(evt.Data.DeltaContent);\n            }\n        });\n    }\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\nimport com.github.copilot.CopilotSession;\nimport com.github.copilot.generated.AssistantMessageDeltaEvent;\n\nfinal class ParentAgentResponseExample {\n    static void subscribeParentResponse(CopilotSession session) {\n        session.on(AssistantMessageDeltaEvent.class, event -> {\n            if (event.getAgentId() == null) {\n                System.out.print(event.getData().deltaContent());\n            }\n        });\n    }\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"rust\" data-label=\"Rust\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Rust</div>\n\n```rust\nuse github_copilot_sdk::session::Session;\n\nasync fn subscribe_parent_response(session: &Session) {\n    let mut events = session.subscribe();\n\n    while let Ok(event) = events.recv().await {\n        if event.event_type == \"assistant.message_delta\" && event.agent_id.is_none() {\n            if let Some(delta) = event.data.get(\"deltaContent\").and_then(|v| v.as_str()) {\n                print!(\"{delta}\");\n            }\n        }\n    }\n}\n```\n\n</div>\n\n</div>\n\n## Assistant events\n\nThese events track the agent's response lifecycle—from turn start through streaming chunks to the final message.\n\n### `assistant.turn_start`\n\nEmitted when the agent begins processing a turn.\n\n| Data Field      | Type     | Required | Description                                           |\n| --------------- | -------- | -------- | ----------------------------------------------------- |\n| `turnId`        | `string` | ✅        | Turn identifier (typically a stringified turn number) |\n| `interactionId` | `string` |          | CAPI interaction ID for telemetry correlation         |\n\n### `assistant.intent`\n\nEphemeral. Short description of what the agent is currently doing, updated as it works.\n\n| Data Field | Type     | Required | Description                                        |\n| ---------- | -------- | -------- | -------------------------------------------------- |\n| `intent`   | `string` | ✅        | Human-readable intent (e.g., \"Exploring codebase\") |\n\n### `assistant.reasoning`\n\nComplete extended thinking block from the model. Emitted after reasoning is finished.\n\n| Data Field    | Type     | Required | Description                                |\n| ------------- | -------- | -------- | ------------------------------------------ |\n| `reasoningId` | `string` | ✅        | Unique identifier for this reasoning block |\n| `content`     | `string` | ✅        | The complete extended thinking text        |\n\n### `assistant.reasoning_delta`\n\nEphemeral. Incremental chunk of the model's extended thinking, streamed in real time.\n\n| Data Field     | Type     | Required | Description                                           |\n| -------------- | -------- | -------- | ----------------------------------------------------- |\n| `reasoningId`  | `string` | ✅        | Matches the corresponding `assistant.reasoning` event |\n| `deltaContent` | `string` | ✅        | Text chunk to append to reasoning content             |\n\n### `assistant.message`\n\nThe assistant's complete response for this LLM call. May include tool invocation requests.\n\n| Data Field         | Type            | Required | Description                                                        |\n| ------------------ | --------------- | -------- | ------------------------------------------------------------------ |\n| `messageId`        | `string`        | ✅        | Unique identifier for this message                                 |\n| `content`          | `string`        | ✅        | The assistant's text response                                      |\n| `toolRequests`     | `ToolRequest[]` |          | Tool calls the assistant wants to make (see below)                 |\n| `reasoningOpaque`  | `string`        |          | Encrypted extended thinking (Anthropic models); session-bound      |\n| `reasoningText`    | `string`        |          | Readable reasoning text from extended thinking                     |\n| `encryptedContent` | `string`        |          | Encrypted reasoning content (OpenAI models); session-bound         |\n| `phase`            | `string`        |          | Generation phase (e.g., `\"thinking\"` vs `\"response\"`)              |\n| `outputTokens`     | `number`        |          | Actual output token count from the API response                    |\n| `interactionId`    | `string`        |          | CAPI interaction ID for telemetry                                  |\n| `parentToolCallId` | `string`        |          | Deprecated. Use envelope-level `agentId` for sub-agent attribution |\n\n**`ToolRequest` fields:**\n\n| Field        | Type                     | Required | Description                                     |\n| ------------ | ------------------------ | -------- | ----------------------------------------------- |\n| `toolCallId` | `string`                 | ✅        | Unique ID for this tool call                    |\n| `name`       | `string`                 | ✅        | Tool name (e.g., `\"bash\"`, `\"edit\"`, `\"grep\"`)  |\n| `arguments`  | `object`                 |          | Parsed arguments for the tool                   |\n| `type`       | `\"function\" \\| \"custom\"` |          | Call type; defaults to `\"function\"` when absent |\n\n### `assistant.message_delta`\n\nEphemeral. Incremental chunk of the assistant's text response, streamed in real time.\n\n| Data Field         | Type     | Required | Description                                                        |\n| ------------------ | -------- | -------- | ------------------------------------------------------------------ |\n| `messageId`        | `string` | ✅        | Matches the corresponding `assistant.message` event                |\n| `deltaContent`     | `string` | ✅        | Text chunk to append to the message                                |\n| `parentToolCallId` | `string` |          | Deprecated. Use envelope-level `agentId` for sub-agent attribution |\n\n### `assistant.turn_end`\n\nEmitted when the agent finishes a turn (all tool executions complete, final response delivered).\n\n| Data Field | Type     | Required | Description                                            |\n| ---------- | -------- | -------- | ------------------------------------------------------ |\n| `turnId`   | `string` | ✅        | Matches the corresponding `assistant.turn_start` event |\n\n### `assistant.usage`\n\nEphemeral. Token usage and cost information for an individual API call.\n\n| Data Field               | Type                                                                       | Required | Description                                                                                                                                        |\n| ------------------------ | -------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `model`                  | `string`                                                                   | ✅        | Model identifier (e.g., `\"gpt-5.4\"`)                                                                                                               |\n| `inputTokens`            | `number`                                                                   |          | Input tokens consumed                                                                                                                              |\n| `outputTokens`           | `number`                                                                   |          | Output tokens produced                                                                                                                             |\n| `reasoningTokens`        | `number`                                                                   |          | Output tokens used for reasoning/chain-of-thought (subset of `outputTokens`)                                                                       |\n| `cacheReadTokens`        | `number`                                                                   |          | Tokens read from prompt cache                                                                                                                      |\n| `cacheWriteTokens`       | `number`                                                                   |          | Tokens written to prompt cache                                                                                                                     |\n| `cacheExpiresAt`         | `string`                                                                   |          | ISO 8601 timestamp when the prompt cache for this model call expires                                                                               |\n| `contentFilterTriggered` | `boolean`                                                                  |          | Whether the response was blocked or truncated by content filtering (`finish_reason === 'content_filter'`)                                          |\n| `finishReason`           | `string`                                                                   |          | Model finish reason (e.g., `\"stop\"`, `\"length\"`, `\"tool_calls\"`, `\"content_filter\"`)                                                               |\n| `cost`                   | `number`                                                                   |          | Model multiplier cost for billing                                                                                                                  |\n| `duration`               | `number`                                                                   |          | API call duration in milliseconds                                                                                                                  |\n| `timeToFirstTokenMs`     | `number`                                                                   |          | Time from request dispatch to first token received (streaming latency)                                                                             |\n| `interTokenLatencyMs`    | `number`                                                                   |          | Average latency between consecutive tokens (streaming throughput)                                                                                  |\n| `reasoningEffort`        | `string`                                                                   |          | Reasoning effort level used for this call (e.g., `\"low\"`, `\"medium\"`, `\"high\"`)                                                                    |\n| `initiator`              | `string`                                                                   |          | What triggered this call (e.g., `\"sub-agent\"`); absent for user-initiated                                                                          |\n| `apiCallId`              | `string`                                                                   |          | Completion ID from the provider (e.g., `chatcmpl-abc123`)                                                                                          |\n| `serviceRequestId`       | `string`                                                                   |          | Copilot service request ID (`x-copilot-service-request-id`) for CAPI log correlation                                                               |\n| `apiEndpoint`            | `\"/chat/completions\" \\| \"/v1/messages\" \\| \"/responses\" \\| \"ws:/responses\"` |          | API endpoint used for the model call; useful for observability and cost attribution. `ws:/responses` is the websocket variant of the responses API |\n| `providerCallId`         | `string`                                                                   |          | GitHub request tracing ID (`x-github-request-id`)                                                                                                  |\n| `parentToolCallId`       | `string`                                                                   |          | Deprecated. Use envelope-level `agentId` for sub-agent attribution                                                                                 |\n| `quotaSnapshots`         | `Record<string, QuotaSnapshot>`                                            |          | Per-quota resource usage, keyed by quota identifier                                                                                                |\n| `copilotUsage`           | `CopilotUsage`                                                             |          | Itemized token cost breakdown from the API                                                                                                         |\n\n### `assistant.streaming_delta`\n\nEphemeral. Low-level network progress indicator—total bytes received from the streaming API response.\n\n| Data Field               | Type     | Required | Description                      |\n| ------------------------ | -------- | -------- | -------------------------------- |\n| `totalResponseSizeBytes` | `number` | ✅        | Cumulative bytes received so far |\n\n## Tool execution events\n\nThese events track the full lifecycle of each tool invocation—from the model requesting a tool call through execution to completion.\n\n### `tool.execution_start`\n\nEmitted when a tool begins executing.\n\n| Data Field         | Type     | Required | Description                                                        |\n| ------------------ | -------- | -------- | ------------------------------------------------------------------ |\n| `toolCallId`       | `string` | ✅        | Unique identifier for this tool call                               |\n| `toolName`         | `string` | ✅        | Name of the tool (e.g., `\"bash\"`, `\"edit\"`, `\"grep\"`)              |\n| `arguments`        | `object` |          | Parsed arguments passed to the tool                                |\n| `mcpServerName`    | `string` |          | MCP server name, when the tool is provided by an MCP server        |\n| `mcpToolName`      | `string` |          | Original tool name on the MCP server                               |\n| `parentToolCallId` | `string` |          | Deprecated. Use envelope-level `agentId` for sub-agent attribution |\n\n### `tool.execution_partial_result`\n\nEphemeral. Incremental output from a running tool (e.g., streaming bash output).\n\n| Data Field      | Type     | Required | Description                                      |\n| --------------- | -------- | -------- | ------------------------------------------------ |\n| `toolCallId`    | `string` | ✅        | Matches the corresponding `tool.execution_start` |\n| `partialOutput` | `string` | ✅        | Incremental output chunk                         |\n\n### `tool.execution_progress`\n\nEphemeral. Human-readable progress status from a running tool (e.g., MCP server progress notifications).\n\n| Data Field        | Type     | Required | Description                                      |\n| ----------------- | -------- | -------- | ------------------------------------------------ |\n| `toolCallId`      | `string` | ✅        | Matches the corresponding `tool.execution_start` |\n| `progressMessage` | `string` | ✅        | Progress status message                          |\n\n### `tool.execution_complete`\n\nEmitted when a tool finishes executing—successfully or with an error.\n\n| Data Field         | Type                 | Required | Description                                                        |\n| ------------------ | -------------------- | -------- | ------------------------------------------------------------------ |\n| `toolCallId`       | `string`             | ✅        | Matches the corresponding `tool.execution_start`                   |\n| `success`          | `boolean`            | ✅        | Whether execution succeeded                                        |\n| `model`            | `string`             |          | Model that generated this tool call                                |\n| `interactionId`    | `string`             |          | CAPI interaction ID                                                |\n| `isUserRequested`  | `boolean`            |          | `true` when the user explicitly requested this tool call           |\n| `result`           | `Result`             |          | Present on success (see below)                                     |\n| `error`            | `{ message, code? }` |          | Present on failure                                                 |\n| `toolTelemetry`    | `object`             |          | Tool-specific telemetry (e.g., CodeQL check counts)                |\n| `parentToolCallId` | `string`             |          | Deprecated. Use envelope-level `agentId` for sub-agent attribution |\n\n**`Result` fields:**\n\n| Field             | Type             | Required | Description                                                            |\n| ----------------- | ---------------- | -------- | ---------------------------------------------------------------------- |\n| `content`         | `string`         | ✅        | Concise result sent to the LLM (may be truncated for token efficiency) |\n| `detailedContent` | `string`         |          | Full result for display, preserving complete content like diffs        |\n| `contents`        | `ContentBlock[]` |          | Structured content blocks (text, terminal, image, audio, resource)     |\n\n### `tool.user_requested`\n\nEmitted when the user explicitly requests a tool invocation (rather than the model choosing to call one).\n\n| Data Field   | Type     | Required | Description                               |\n| ------------ | -------- | -------- | ----------------------------------------- |\n| `toolCallId` | `string` | ✅        | Unique identifier for this tool call      |\n| `toolName`   | `string` | ✅        | Name of the tool the user wants to invoke |\n| `arguments`  | `object` |          | Arguments for the invocation              |\n\n## Session lifecycle events\n\n### `session.idle`\n\nEphemeral. The agent has finished all processing and is ready for the next message. This is the signal that a turn is fully complete.\n\n| Data Field | Type      | Required | Description                                                 |\n| ---------- | --------- | -------- | ----------------------------------------------------------- |\n| `aborted`  | `boolean` |          | True when the preceding turn was cancelled via abort signal |\n\n### `session.error`\n\nAn error occurred during session processing.\n\n| Data Field       | Type     | Required | Description                                                          |\n| ---------------- | -------- | -------- | -------------------------------------------------------------------- |\n| `errorType`      | `string` | ✅        | Error category (e.g., `\"authentication\"`, `\"quota\"`, `\"rate_limit\"`) |\n| `message`        | `string` | ✅        | Human-readable error message                                         |\n| `stack`          | `string` |          | Error stack trace                                                    |\n| `statusCode`     | `number` |          | HTTP status code from the upstream request                           |\n| `providerCallId` | `string` |          | GitHub request tracing ID for server-side log correlation            |\n\n### `session.compaction_start`\n\nContext window compaction has begun. **Data payload is empty (`{}`)**.\n\n### `session.compaction_complete`\n\nContext window compaction finished.\n\n| Data Field                    | Type                             | Required | Description                                       |\n| ----------------------------- | -------------------------------- | -------- | ------------------------------------------------- |\n| `success`                     | `boolean`                        | ✅        | Whether compaction succeeded                      |\n| `error`                       | `string`                         |          | Error message if compaction failed                |\n| `preCompactionTokens`         | `number`                         |          | Tokens before compaction                          |\n| `postCompactionTokens`        | `number`                         |          | Tokens after compaction                           |\n| `preCompactionMessagesLength` | `number`                         |          | Message count before compaction                   |\n| `messagesRemoved`             | `number`                         |          | Messages removed                                  |\n| `tokensRemoved`               | `number`                         |          | Tokens removed                                    |\n| `summaryContent`              | `string`                         |          | LLM-generated summary of compacted history        |\n| `checkpointNumber`            | `number`                         |          | Checkpoint snapshot number created for recovery   |\n| `checkpointPath`              | `string`                         |          | File path where the checkpoint was stored         |\n| `compactionTokensUsed`        | `{ input, output, cachedInput }` |          | Token usage for the compaction LLM call           |\n| `requestId`                   | `string`                         |          | GitHub request tracing ID for the compaction call |\n\n### `session.title_changed`\n\nEphemeral. The session's auto-generated title was updated.\n\n| Data Field | Type     | Required | Description       |\n| ---------- | -------- | -------- | ----------------- |\n| `title`    | `string` | ✅        | New session title |\n\n### `session.context_changed`\n\nThe session's working directory or repository context changed.\n\n| Data Field   | Type     | Required | Description                         |\n| ------------ | -------- | -------- | ----------------------------------- |\n| `cwd`        | `string` | ✅        | Current working directory           |\n| `gitRoot`    | `string` |          | Git repository root                 |\n| `repository` | `string` |          | Repository in `\"owner/name\"` format |\n| `branch`     | `string` |          | Current git branch                  |\n\n### `session.usage_info`\n\nEphemeral. Context window utilization snapshot.\n\n| Data Field       | Type     | Required | Description                                   |\n| ---------------- | -------- | -------- | --------------------------------------------- |\n| `tokenLimit`     | `number` | ✅        | Maximum tokens for the model's context window |\n| `currentTokens`  | `number` | ✅        | Current tokens in the context window          |\n| `messagesLength` | `number` | ✅        | Current message count in the conversation     |\n\n### `session.session_limits_changed`\n\nSession limits changed for the current accounting window. A `null` `sessionLimits` value means no limits are active.\n\n| Data Field                   | Type                          | Required | Description                                                               |\n| ---------------------------- | ----------------------------- | -------- | ------------------------------------------------------------------------- |\n| `sessionLimits`              | `SessionLimitsConfig \\| null` | ✅        | Current session limits, or `null` when no limits are active               |\n| `sessionLimits.maxAiCredits` | `number`                      |          | Maximum AI Credits allowed across the session's current accounting window |\n\n### `session.usage_checkpoint`\n\nDurable aggregate usage checkpoint used to reconstruct accounting when a session is resumed.\n\n| Data Field             | Type     | Required | Description                                                    |\n| ---------------------- | -------- | -------- | -------------------------------------------------------------- |\n| `totalNanoAiu`         | `number` | ✅        | Session-wide accumulated nano-AI units cost at checkpoint time |\n| `totalPremiumRequests` | `number` |          | Total number of premium API requests used at checkpoint time   |\n\n### `session.task_complete`\n\nThe agent has completed its assigned task.\n\n| Data Field | Type     | Required | Description                   |\n| ---------- | -------- | -------- | ----------------------------- |\n| `summary`  | `string` |          | Summary of the completed task |\n\n### `session.shutdown`\n\nThe session has ended.\n\n| Data Field             | Type                                          | Required | Description                                        |\n| ---------------------- | --------------------------------------------- | -------- | -------------------------------------------------- |\n| `shutdownType`         | `\"routine\" \\| \"error\"`                        | ✅        | Normal shutdown or crash                           |\n| `errorReason`          | `string`                                      |          | Error description when `shutdownType` is `\"error\"` |\n| `totalPremiumRequests` | `number`                                      | ✅        | Total premium API requests used                    |\n| `totalApiDurationMs`   | `number`                                      | ✅        | Cumulative API call time in milliseconds           |\n| `sessionStartTime`     | `number`                                      | ✅        | Unix timestamp (ms) when the session started       |\n| `codeChanges`          | `{ linesAdded, linesRemoved, filesModified }` | ✅        | Aggregate code change metrics                      |\n| `modelMetrics`         | `Record<string, ModelMetric>`                 | ✅        | Per-model usage breakdown                          |\n| `currentModel`         | `string`                                      |          | Model selected at shutdown time                    |\n\n## Permission and user input events\n\nThese events are emitted when the agent needs approval or input from the user before continuing.\n\n### `permission.requested`\n\nThe agent needs permission to perform an action (run a command, write a file, etc.).\n\n| Data Field          | Type                | Required | Description                                             |\n| ------------------- | ------------------- | -------- | ------------------------------------------------------- |\n| `requestId`         | `string`            | ✅        | Use this to respond via `session.respondToPermission()` |\n| `permissionRequest` | `PermissionRequest` | ✅        | Details of the permission being requested               |\n\nThe `permissionRequest` is a discriminated union on `kind`:\n\n| `kind`          | Key Fields                                                      | Description              |\n| --------------- | --------------------------------------------------------------- | ------------------------ |\n| `\"shell\"`       | `fullCommandText`, `intention`, `commands[]`, `possiblePaths[]` | Execute a shell command  |\n| `\"write\"`       | `fileName`, `diff`, `intention`, `newFileContents?`             | Write/modify a file      |\n| `\"read\"`        | `path`, `intention`                                             | Read a file or directory |\n| `\"mcp\"`         | `serverName`, `toolName`, `toolTitle`, `args?`, `readOnly`      | Call an MCP tool         |\n| `\"url\"`         | `url`, `intention`                                              | Fetch a URL              |\n| `\"memory\"`      | `subject`, `fact`, `citations`                                  | Store a memory           |\n| `\"custom-tool\"` | `toolName`, `toolDescription`, `args?`                          | Call a custom tool       |\n\nAll `kind` variants also include an optional `toolCallId` linking back to the tool call that triggered the request.\n\n### `permission.completed`\n\nA permission request was resolved.\n\n| Data Field    | Type     | Required | Description                                                                                                                                                                      |\n| ------------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `requestId`   | `string` | ✅        | Matches the corresponding `permission.requested`                                                                                                                                 |\n| `result.kind` | `string` | ✅        | One of: `\"approved\"`, `\"denied-by-rules\"`, `\"denied-interactively-by-user\"`, `\"denied-no-approval-rule-and-could-not-request-from-user\"`, `\"denied-by-content-exclusion-policy\"` |\n\n### `user_input.requested`\n\nEphemeral. The agent is asking the user a question.\n\n| Data Field      | Type       | Required | Description                                            |\n| --------------- | ---------- | -------- | ------------------------------------------------------ |\n| `requestId`     | `string`   | ✅        | Use this to respond via `session.respondToUserInput()` |\n| `question`      | `string`   | ✅        | The question to present to the user                    |\n| `choices`       | `string[]` |          | Predefined choices for the user                        |\n| `allowFreeform` | `boolean`  |          | Whether free-form text input is allowed                |\n\n### `user_input.completed`\n\nEphemeral. A user input request was resolved.\n\n| Data Field  | Type     | Required | Description                                      |\n| ----------- | -------- | -------- | ------------------------------------------------ |\n| `requestId` | `string` | ✅        | Matches the corresponding `user_input.requested` |\n\n### `elicitation.requested`\n\nEphemeral. The agent needs structured form input from the user (MCP elicitation protocol).\n\n| Data Field        | Type                                        | Required | Description                                              |\n| ----------------- | ------------------------------------------- | -------- | -------------------------------------------------------- |\n| `requestId`       | `string`                                    | ✅        | Use this to respond via `session.respondToElicitation()` |\n| `message`         | `string`                                    | ✅        | Description of what information is needed                |\n| `mode`            | `\"form\"`                                    |          | Elicitation mode (currently only `\"form\"`)               |\n| `requestedSchema` | `{ type: \"object\", properties, required? }` | ✅        | JSON Schema describing the form fields                   |\n\n### `elicitation.completed`\n\nEphemeral. An elicitation request was resolved.\n\n| Data Field  | Type     | Required | Description                                       |\n| ----------- | -------- | -------- | ------------------------------------------------- |\n| `requestId` | `string` | ✅        | Matches the corresponding `elicitation.requested` |\n\n## Sub-agent and skill events\n\n### `subagent.started`\n\nA custom agent was invoked as a sub-agent.\n\n| Data Field         | Type     | Required | Description                                            |\n| ------------------ | -------- | -------- | ------------------------------------------------------ |\n| `toolCallId`       | `string` | ✅        | Parent tool call that spawned this sub-agent           |\n| `agentName`        | `string` | ✅        | Internal name of the sub-agent                         |\n| `agentDisplayName` | `string` | ✅        | Human-readable display name                            |\n| `agentDescription` | `string` | ✅        | Description of what the sub-agent does                 |\n| `model`            | `string` |          | Model the sub-agent will run with, when known at start |\n\n### `subagent.completed`\n\nA sub-agent finished successfully.\n\n| Data Field         | Type     | Required | Description                                   |\n| ------------------ | -------- | -------- | --------------------------------------------- |\n| `toolCallId`       | `string` | ✅        | Matches the corresponding `subagent.started`  |\n| `agentName`        | `string` | ✅        | Internal name                                 |\n| `agentDisplayName` | `string` | ✅        | Display name                                  |\n| `model`            | `string` |          | Model used by the sub-agent                   |\n| `durationMs`       | `number` |          | Wall-clock execution duration in milliseconds |\n| `totalTokens`      | `number` |          | Total input and output tokens consumed        |\n| `totalToolCalls`   | `number` |          | Total tool calls made                         |\n\n### `subagent.failed`\n\nA sub-agent encountered an error.\n\n| Data Field         | Type     | Required | Description                                           |\n| ------------------ | -------- | -------- | ----------------------------------------------------- |\n| `toolCallId`       | `string` | ✅        | Matches the corresponding `subagent.started`          |\n| `agentName`        | `string` | ✅        | Internal name                                         |\n| `agentDisplayName` | `string` | ✅        | Display name                                          |\n| `error`            | `string` | ✅        | Error message                                         |\n| `model`            | `string` |          | Model selected for the sub-agent, when known          |\n| `durationMs`       | `number` |          | Wall-clock execution duration in milliseconds         |\n| `totalTokens`      | `number` |          | Total input and output tokens consumed before failure |\n| `totalToolCalls`   | `number` |          | Total tool calls made before failure                  |\n\n### `subagent.selected`\n\nA custom agent was selected (inferred) to handle the current request.\n\n| Data Field         | Type               | Required | Description                                              |\n| ------------------ | ------------------ | -------- | -------------------------------------------------------- |\n| `agentName`        | `string`           | ✅        | Internal name of the selected agent                      |\n| `agentDisplayName` | `string`           | ✅        | Display name                                             |\n| `tools`            | `string[] \\| null` | ✅        | Tool names available to this agent; `null` for all tools |\n\n### `subagent.deselected`\n\nA custom agent was deselected, returning to the default agent. **Data payload is empty (`{}`)**.\n\n### `skill.invoked`\n\nA skill was activated for the current conversation.\n\n| Data Field      | Type       | Required | Description                                       |\n| --------------- | ---------- | -------- | ------------------------------------------------- |\n| `name`          | `string`   | ✅        | Skill name                                        |\n| `path`          | `string`   | ✅        | File path to the SKILL.md definition              |\n| `content`       | `string`   | ✅        | Full skill content injected into the conversation |\n| `allowedTools`  | `string[]` |          | Tools auto-approved while this skill is active    |\n| `pluginName`    | `string`   |          | Plugin the skill originated from                  |\n| `pluginVersion` | `string`   |          | Plugin version                                    |\n\n## Other events\n\n### `abort`\n\nThe current turn was aborted.\n\n| Data Field | Type     | Required | Description                                         |\n| ---------- | -------- | -------- | --------------------------------------------------- |\n| `reason`   | `string` | ✅        | Why the turn was aborted (e.g., `\"user initiated\"`) |\n\n### `user.message`\n\nThe user sent a message. Recorded for the session timeline.\n\n| Data Field           | Type           | Required | Description                                                        |\n| -------------------- | -------------- | -------- | ------------------------------------------------------------------ |\n| `content`            | `string`       | ✅        | The user's message text                                            |\n| `transformedContent` | `string`       |          | Transformed version after preprocessing                            |\n| `attachments`        | `Attachment[]` |          | File, directory, selection, blob, or GitHub reference attachments  |\n| `source`             | `string`       |          | Message source identifier                                          |\n| `agentMode`          | `string`       |          | Agent mode: `\"interactive\"`, `\"plan\"`, `\"autopilot\"`, or `\"shell\"` |\n| `interactionId`      | `string`       |          | CAPI interaction ID                                                |\n\n### `system.message`\n\nA system or developer prompt was injected into the conversation.\n\n| Data Field | Type                             | Required | Description              |\n| ---------- | -------------------------------- | -------- | ------------------------ |\n| `content`  | `string`                         | ✅        | The prompt text          |\n| `role`     | `\"system\" \\| \"developer\"`        | ✅        | Message role             |\n| `name`     | `string`                         |          | Source identifier        |\n| `metadata` | `{ promptVersion?, variables? }` |          | Prompt template metadata |\n\n### `external_tool.requested`\n\nThe agent wants to invoke an external tool (one provided by the SDK consumer).\n\n| Data Field   | Type     | Required | Description                                               |\n| ------------ | -------- | -------- | --------------------------------------------------------- |\n| `requestId`  | `string` | ✅        | Use this to respond via `session.respondToExternalTool()` |\n| `sessionId`  | `string` | ✅        | Session this request belongs to                           |\n| `toolCallId` | `string` | ✅        | Tool call ID for this invocation                          |\n| `toolName`   | `string` | ✅        | Name of the external tool                                 |\n| `arguments`  | `object` |          | Arguments for the tool                                    |\n\n### `external_tool.completed`\n\nAn external tool request was resolved.\n\n| Data Field  | Type     | Required | Description                                         |\n| ----------- | -------- | -------- | --------------------------------------------------- |\n| `requestId` | `string` | ✅        | Matches the corresponding `external_tool.requested` |\n\n### `exit_plan_mode.requested`\n\nEphemeral. The agent has created a plan and wants to exit plan mode.\n\n| Data Field          | Type       | Required | Description                                               |\n| ------------------- | ---------- | -------- | --------------------------------------------------------- |\n| `requestId`         | `string`   | ✅        | Use this to respond via `session.respondToExitPlanMode()` |\n| `summary`           | `string`   | ✅        | Summary of the plan                                       |\n| `planContent`       | `string`   | ✅        | Full plan file content                                    |\n| `actions`           | `string[]` | ✅        | Available user actions (e.g., approve, edit, reject)      |\n| `recommendedAction` | `string`   | ✅        | Suggested action                                          |\n\n### `exit_plan_mode.completed`\n\nEphemeral. An exit plan mode request was resolved.\n\n| Data Field  | Type     | Required | Description                                          |\n| ----------- | -------- | -------- | ---------------------------------------------------- |\n| `requestId` | `string` | ✅        | Matches the corresponding `exit_plan_mode.requested` |\n\n### `command.queued`\n\nEphemeral. A slash command was queued for execution.\n\n| Data Field  | Type     | Required | Description                                                |\n| ----------- | -------- | -------- | ---------------------------------------------------------- |\n| `requestId` | `string` | ✅        | Use this to respond via `session.respondToQueuedCommand()` |\n| `command`   | `string` | ✅        | The slash command text (e.g., `/help`, `/clear`)           |\n\n### `command.completed`\n\nEphemeral. A queued command was resolved.\n\n| Data Field  | Type     | Required | Description                                |\n| ----------- | -------- | -------- | ------------------------------------------ |\n| `requestId` | `string` | ✅        | Matches the corresponding `command.queued` |\n\n### `session_limits_exhausted.requested`\n\nEphemeral. The current session budget was exhausted and the runtime needs a user decision before continuing.\n\n| Data Field      | Type     | Required | Description                                                        |\n| --------------- | -------- | -------- | ------------------------------------------------------------------ |\n| `requestId`     | `string` | ✅        | Use this ID when responding to the pending exhausted-limit request |\n| `maxAiCredits`  | `number` | ✅        | Configured max AI Credits for the current accounting window        |\n| `usedAiCredits` | `number` | ✅        | AI Credits already consumed in the current accounting window       |\n\n### `session_limits_exhausted.completed`\n\nEphemeral. A pending exhausted-limit request was resolved.\n\n| Data Field                     | Type                                    | Required | Description                                                            |\n| ------------------------------ | --------------------------------------- | -------- | ---------------------------------------------------------------------- |\n| `requestId`                    | `string`                                | ✅        | Matches the corresponding `session_limits_exhausted.requested` event   |\n| `response.action`              | `\"add\" \\| \"set\" \\| \"unset\" \\| \"cancel\"` | ✅        | Action selected for the exhausted-limit request                        |\n| `response.additionalAiCredits` | `number`                                |          | AI Credits to add to the current max when `response.action` is `\"add\"` |\n| `response.maxAiCredits`        | `number`                                |          | New absolute max AI Credits when `response.action` is `\"set\"`          |\n\n## Quick reference: agentic turn flow\n\nA typical agentic turn emits events in this order:\n\n```text\nassistant.turn_start          → Turn begins\n├── assistant.intent          → What the agent plans to do (ephemeral)\n├── assistant.reasoning_delta → Streaming thinking chunks (ephemeral, repeated)\n├── assistant.reasoning       → Complete thinking block\n├── assistant.message_delta   → Streaming response chunks (ephemeral, repeated)\n├── assistant.message         → Complete response (may include toolRequests)\n├── assistant.usage           → Token usage for this API call (ephemeral)\n│\n├── [If tools were requested:]\n│   ├── permission.requested  → Needs user approval\n│   ├── permission.completed  → Approval result\n│   ├── tool.execution_start  → Tool begins\n│   ├── tool.execution_partial_result  → Streaming tool output (ephemeral, repeated)\n│   ├── tool.execution_progress        → Progress updates (ephemeral, repeated)\n│   ├── tool.execution_complete        → Tool finished\n│   │\n│   └── [Agent loops: more reasoning → message → tool calls...]\n│\nassistant.turn_end            → Turn complete\nsession.idle                  → Ready for next message (ephemeral)\n```\n\n## All event types at a glance\n\nThis table lists key `data` payload fields. Common envelope fields are documented above.\n\n| Event Type                           | Ephemeral | Category      | Key Data Fields                                                                                           |\n| ------------------------------------ | --------- | ------------- | --------------------------------------------------------------------------------------------------------- |\n| `assistant.turn_start`               |           | Assistant     | `turnId`, `interactionId?`                                                                                |\n| `assistant.intent`                   | ✅         | Assistant     | `intent`                                                                                                  |\n| `assistant.reasoning`                |           | Assistant     | `reasoningId`, `content`                                                                                  |\n| `assistant.reasoning_delta`          | ✅         | Assistant     | `reasoningId`, `deltaContent`                                                                             |\n| `assistant.streaming_delta`          | ✅         | Assistant     | `totalResponseSizeBytes`                                                                                  |\n| `assistant.message`                  |           | Assistant     | `messageId`, `content`, `toolRequests?`, `outputTokens?`, `phase?`                                        |\n| `assistant.message_delta`            | ✅         | Assistant     | `messageId`, `deltaContent`                                                                               |\n| `assistant.turn_end`                 |           | Assistant     | `turnId`                                                                                                  |\n| `assistant.usage`                    | ✅         | Assistant     | `model`, `apiEndpoint?`, `inputTokens?`, `outputTokens?`, `cost?`, `duration?`                            |\n| `tool.user_requested`                |           | Tool          | `toolCallId`, `toolName`, `arguments?`                                                                    |\n| `tool.execution_start`               |           | Tool          | `toolCallId`, `toolName`, `arguments?`, `mcpServerName?`                                                  |\n| `tool.execution_partial_result`      | ✅         | Tool          | `toolCallId`, `partialOutput`                                                                             |\n| `tool.execution_progress`            | ✅         | Tool          | `toolCallId`, `progressMessage`                                                                           |\n| `tool.execution_complete`            |           | Tool          | `toolCallId`, `success`, `result?`, `error?`                                                              |\n| `session.idle`                       | ✅         | Session       | `aborted?`                                                                                                |\n| `session.error`                      |           | Session       | `errorType`, `message`, `statusCode?`                                                                     |\n| `session.compaction_start`           |           | Session       | *(empty)*                                                                                                 |\n| `session.compaction_complete`        |           | Session       | `success`, `preCompactionTokens?`, `summaryContent?`                                                      |\n| `session.title_changed`              | ✅         | Session       | `title`                                                                                                   |\n| `session.context_changed`            |           | Session       | `cwd`, `gitRoot?`, `repository?`, `branch?`                                                               |\n| `session.usage_info`                 | ✅         | Session       | `tokenLimit`, `currentTokens`, `messagesLength`                                                           |\n| `session.session_limits_changed`     |           | Session       | `sessionLimits`                                                                                           |\n| `session.usage_checkpoint`           |           | Session       | `totalNanoAiu`, `totalPremiumRequests?`                                                                   |\n| `session.task_complete`              |           | Session       | `summary?`                                                                                                |\n| `session.shutdown`                   |           | Session       | `shutdownType`, `codeChanges`, `modelMetrics`                                                             |\n| `permission.requested`               |           | Permission    | `requestId`, `permissionRequest`                                                                          |\n| `permission.completed`               |           | Permission    | `requestId`, `result.kind`                                                                                |\n| `user_input.requested`               | ✅         | User Input    | `requestId`, `question`, `choices?`                                                                       |\n| `user_input.completed`               | ✅         | User Input    | `requestId`                                                                                               |\n| `elicitation.requested`              | ✅         | User Input    | `requestId`, `message`, `requestedSchema`                                                                 |\n| `elicitation.completed`              | ✅         | User Input    | `requestId`                                                                                               |\n| `subagent.started`                   |           | Sub-Agent     | `toolCallId`, `agentName`, `agentDisplayName`, `model?`                                                   |\n| `subagent.completed`                 |           | Sub-Agent     | `toolCallId`, `agentName`, `agentDisplayName`, `model?`, `durationMs?`, `totalTokens?`, `totalToolCalls?` |\n| `subagent.failed`                    |           | Sub-Agent     | `toolCallId`, `agentName`, `error`, `model?`, `durationMs?`, `totalTokens?`, `totalToolCalls?`            |\n| `subagent.selected`                  |           | Sub-Agent     | `agentName`, `agentDisplayName`, `tools`                                                                  |\n| `subagent.deselected`                |           | Sub-Agent     | *(empty)*                                                                                                 |\n| `skill.invoked`                      |           | Skill         | `name`, `path`, `content`, `allowedTools?`                                                                |\n| `abort`                              |           | Control       | `reason`                                                                                                  |\n| `user.message`                       |           | User          | `content`, `attachments?`, `agentMode?`                                                                   |\n| `system.message`                     |           | System        | `content`, `role`                                                                                         |\n| `external_tool.requested`            |           | External Tool | `requestId`, `toolName`, `arguments?`                                                                     |\n| `external_tool.completed`            |           | External Tool | `requestId`                                                                                               |\n| `command.queued`                     | ✅         | Command       | `requestId`, `command`                                                                                    |\n| `command.completed`                  | ✅         | Command       | `requestId`                                                                                               |\n| `session_limits_exhausted.requested` | ✅         | Session       | `requestId`, `maxAiCredits`, `usedAiCredits`                                                              |\n| `session_limits_exhausted.completed` | ✅         | Session       | `requestId`, `response.action`                                                                            |\n| `exit_plan_mode.requested`           | ✅         | Plan Mode     | `requestId`, `summary`, `planContent`, `actions`                                                          |\n| `exit_plan_mode.completed`           | ✅         | Plan Mode     | `requestId`                                                                                               |"}