diff --git a/docs/guides/streaming-events.md b/docs/guides/streaming-events.md
new file mode 100644
index 0000000000..f33e5fa212
--- /dev/null
+++ b/docs/guides/streaming-events.md
@@ -0,0 +1,724 @@
+# Streaming Session Events
+
+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.
+
+## Overview
+
+When `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`.
+
+```mermaid
+sequenceDiagram
+ participant App as Your App
+ participant SDK as SDK Session
+ participant Agent as Copilot Agent
+
+ App->>SDK: send({ prompt })
+ SDK->>Agent: JSON-RPC
+
+ Agent-->>SDK: assistant.turn_start
+ SDK-->>App: event
+
+ loop Streaming response
+ Agent-->>SDK: assistant.message_delta (ephemeral)
+ SDK-->>App: event
+ end
+
+ Agent-->>SDK: assistant.message
+ SDK-->>App: event
+
+ loop Tool execution
+ Agent-->>SDK: tool.execution_start
+ SDK-->>App: event
+ Agent-->>SDK: tool.execution_complete
+ SDK-->>App: event
+ end
+
+ Agent-->>SDK: assistant.turn_end
+ SDK-->>App: event
+
+ Agent-->>SDK: session.idle (ephemeral)
+ SDK-->>App: event
+```
+
+| Concept | Description |
+|---------|-------------|
+| **Ephemeral event** | Transient; streamed in real time but **not** persisted to the session log. Not replayed on session resume. |
+| **Persisted event** | Saved to the session event log on disk. Replayed when resuming a session. |
+| **Delta event** | An ephemeral streaming chunk (text or reasoning). Accumulate deltas to build the complete content. |
+| **`parentId` chain** | Each event's `parentId` points to the previous event, forming a linked list you can walk. |
+
+## Event Envelope
+
+Every session event, regardless of type, includes these fields:
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | `string` (UUID v4) | Unique event identifier |
+| `timestamp` | `string` (ISO 8601) | When the event was created |
+| `parentId` | `string \| null` | ID of the previous event in the chain; `null` for the first event |
+| `ephemeral` | `boolean?` | `true` for transient events; absent or `false` for persisted events |
+| `type` | `string` | Event type discriminator (see tables below) |
+| `data` | `object` | Event-specific payload |
+
+## Subscribing to Events
+
+
+Node.js / TypeScript
+
+```typescript
+// All events
+session.on((event) => {
+ console.log(event.type, event.data);
+});
+
+// Specific event type — data is narrowed automatically
+session.on("assistant.message_delta", (event) => {
+ process.stdout.write(event.data.deltaContent);
+});
+```
+
+
+
+
+Python
+
+```python
+from copilot.generated.session_events import SessionEventType
+
+def handle(event):
+ if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:
+ print(event.data.delta_content, end="", flush=True)
+
+session.on(handle)
+```
+
+
+
+
+Go
+
+
+```go
+session.On(func(event copilot.SessionEvent) {
+ if event.Type == "assistant.message_delta" {
+ fmt.Print(*event.Data.DeltaContent)
+ }
+})
+```
+
+
+
+
+.NET
+
+
+```csharp
+session.On(evt =>
+{
+ if (evt is AssistantMessageDeltaEvent delta)
+ {
+ Console.Write(delta.Data.DeltaContent);
+ }
+});
+```
+
+
+
+> **Tip (Python / Go):** These SDKs use a single `Data` class/struct with all possible fields as optional/nullable. Only the fields listed in the tables below are populated for each event type — the rest will be `None` / `nil`.
+>
+> **Tip (.NET):** The .NET SDK uses separate, strongly-typed data classes per event (e.g., `AssistantMessageDeltaData`), so only the relevant fields exist on each type.
+>
+> **Tip (TypeScript):** The TypeScript SDK uses a discriminated union — when you match on `event.type`, the `data` payload is automatically narrowed to the correct shape.
+
+---
+
+## Assistant Events
+
+These events track the agent's response lifecycle — from turn start through streaming chunks to the final message.
+
+### `assistant.turn_start`
+
+Emitted when the agent begins processing a turn.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `turnId` | `string` | ✅ | Turn identifier (typically a stringified turn number) |
+| `interactionId` | `string` | | CAPI interaction ID for telemetry correlation |
+
+### `assistant.intent`
+
+Ephemeral. Short description of what the agent is currently doing, updated as it works.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `intent` | `string` | ✅ | Human-readable intent (e.g., "Exploring codebase") |
+
+### `assistant.reasoning`
+
+Complete extended thinking block from the model. Emitted after reasoning is finished.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `reasoningId` | `string` | ✅ | Unique identifier for this reasoning block |
+| `content` | `string` | ✅ | The complete extended thinking text |
+
+### `assistant.reasoning_delta`
+
+Ephemeral. Incremental chunk of the model's extended thinking, streamed in real time.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `reasoningId` | `string` | ✅ | Matches the corresponding `assistant.reasoning` event |
+| `deltaContent` | `string` | ✅ | Text chunk to append to reasoning content |
+
+### `assistant.message`
+
+The assistant's complete response for this LLM call. May include tool invocation requests.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `messageId` | `string` | ✅ | Unique identifier for this message |
+| `content` | `string` | ✅ | The assistant's text response |
+| `toolRequests` | `ToolRequest[]` | | Tool calls the assistant wants to make (see below) |
+| `reasoningOpaque` | `string` | | Encrypted extended thinking (Anthropic models); session-bound |
+| `reasoningText` | `string` | | Readable reasoning text from extended thinking |
+| `encryptedContent` | `string` | | Encrypted reasoning content (OpenAI models); session-bound |
+| `phase` | `string` | | Generation phase (e.g., `"thinking"` vs `"response"`) |
+| `outputTokens` | `number` | | Actual output token count from the API response |
+| `interactionId` | `string` | | CAPI interaction ID for telemetry |
+| `parentToolCallId` | `string` | | Set when this message originates from a sub-agent |
+
+**`ToolRequest` fields:**
+
+| Field | Type | Required | Description |
+|-------|------|----------|-------------|
+| `toolCallId` | `string` | ✅ | Unique ID for this tool call |
+| `name` | `string` | ✅ | Tool name (e.g., `"bash"`, `"edit"`, `"grep"`) |
+| `arguments` | `object` | | Parsed arguments for the tool |
+| `type` | `"function" \| "custom"` | | Call type; defaults to `"function"` when absent |
+
+### `assistant.message_delta`
+
+Ephemeral. Incremental chunk of the assistant's text response, streamed in real time.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `messageId` | `string` | ✅ | Matches the corresponding `assistant.message` event |
+| `deltaContent` | `string` | ✅ | Text chunk to append to the message |
+| `parentToolCallId` | `string` | | Set when originating from a sub-agent |
+
+### `assistant.turn_end`
+
+Emitted when the agent finishes a turn (all tool executions complete, final response delivered).
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `turnId` | `string` | ✅ | Matches the corresponding `assistant.turn_start` event |
+
+### `assistant.usage`
+
+Ephemeral. Token usage and cost information for an individual API call.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `model` | `string` | ✅ | Model identifier (e.g., `"gpt-4.1"`) |
+| `inputTokens` | `number` | | Input tokens consumed |
+| `outputTokens` | `number` | | Output tokens produced |
+| `cacheReadTokens` | `number` | | Tokens read from prompt cache |
+| `cacheWriteTokens` | `number` | | Tokens written to prompt cache |
+| `cost` | `number` | | Model multiplier cost for billing |
+| `duration` | `number` | | API call duration in milliseconds |
+| `initiator` | `string` | | What triggered this call (e.g., `"sub-agent"`); absent for user-initiated |
+| `apiCallId` | `string` | | Completion ID from the provider (e.g., `chatcmpl-abc123`) |
+| `providerCallId` | `string` | | GitHub request tracing ID (`x-github-request-id`) |
+| `parentToolCallId` | `string` | | Set when usage originates from a sub-agent |
+| `quotaSnapshots` | `Record` | | Per-quota resource usage, keyed by quota identifier |
+| `copilotUsage` | `CopilotUsage` | | Itemized token cost breakdown from the API |
+
+### `assistant.streaming_delta`
+
+Ephemeral. Low-level network progress indicator — total bytes received from the streaming API response.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `totalResponseSizeBytes` | `number` | ✅ | Cumulative bytes received so far |
+
+---
+
+## Tool Execution Events
+
+These events track the full lifecycle of each tool invocation — from the model requesting a tool call through execution to completion.
+
+### `tool.execution_start`
+
+Emitted when a tool begins executing.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `toolCallId` | `string` | ✅ | Unique identifier for this tool call |
+| `toolName` | `string` | ✅ | Name of the tool (e.g., `"bash"`, `"edit"`, `"grep"`) |
+| `arguments` | `object` | | Parsed arguments passed to the tool |
+| `mcpServerName` | `string` | | MCP server name, when the tool is provided by an MCP server |
+| `mcpToolName` | `string` | | Original tool name on the MCP server |
+| `parentToolCallId` | `string` | | Set when invoked by a sub-agent |
+
+### `tool.execution_partial_result`
+
+Ephemeral. Incremental output from a running tool (e.g., streaming bash output).
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `toolCallId` | `string` | ✅ | Matches the corresponding `tool.execution_start` |
+| `partialOutput` | `string` | ✅ | Incremental output chunk |
+
+### `tool.execution_progress`
+
+Ephemeral. Human-readable progress status from a running tool (e.g., MCP server progress notifications).
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `toolCallId` | `string` | ✅ | Matches the corresponding `tool.execution_start` |
+| `progressMessage` | `string` | ✅ | Progress status message |
+
+### `tool.execution_complete`
+
+Emitted when a tool finishes executing — successfully or with an error.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `toolCallId` | `string` | ✅ | Matches the corresponding `tool.execution_start` |
+| `success` | `boolean` | ✅ | Whether execution succeeded |
+| `model` | `string` | | Model that generated this tool call |
+| `interactionId` | `string` | | CAPI interaction ID |
+| `isUserRequested` | `boolean` | | `true` when the user explicitly requested this tool call |
+| `result` | `Result` | | Present on success (see below) |
+| `error` | `{ message, code? }` | | Present on failure |
+| `toolTelemetry` | `object` | | Tool-specific telemetry (e.g., CodeQL check counts) |
+| `parentToolCallId` | `string` | | Set when invoked by a sub-agent |
+
+**`Result` fields:**
+
+| Field | Type | Required | Description |
+|-------|------|----------|-------------|
+| `content` | `string` | ✅ | Concise result sent to the LLM (may be truncated for token efficiency) |
+| `detailedContent` | `string` | | Full result for display, preserving complete content like diffs |
+| `contents` | `ContentBlock[]` | | Structured content blocks (text, terminal, image, audio, resource) |
+
+### `tool.user_requested`
+
+Emitted when the user explicitly requests a tool invocation (rather than the model choosing to call one).
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `toolCallId` | `string` | ✅ | Unique identifier for this tool call |
+| `toolName` | `string` | ✅ | Name of the tool the user wants to invoke |
+| `arguments` | `object` | | Arguments for the invocation |
+
+---
+
+## Session Lifecycle Events
+
+### `session.idle`
+
+Ephemeral. The agent has finished all processing and is ready for the next message. This is the signal that a turn is fully complete.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `backgroundTasks` | `BackgroundTasks` | | Background agents/shells still running when the agent became idle |
+
+### `session.error`
+
+An error occurred during session processing.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `errorType` | `string` | ✅ | Error category (e.g., `"authentication"`, `"quota"`, `"rate_limit"`) |
+| `message` | `string` | ✅ | Human-readable error message |
+| `stack` | `string` | | Error stack trace |
+| `statusCode` | `number` | | HTTP status code from the upstream request |
+| `providerCallId` | `string` | | GitHub request tracing ID for server-side log correlation |
+
+### `session.compaction_start`
+
+Context window compaction has begun. **Data payload is empty (`{}`)**.
+
+### `session.compaction_complete`
+
+Context window compaction finished.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `success` | `boolean` | ✅ | Whether compaction succeeded |
+| `error` | `string` | | Error message if compaction failed |
+| `preCompactionTokens` | `number` | | Tokens before compaction |
+| `postCompactionTokens` | `number` | | Tokens after compaction |
+| `preCompactionMessagesLength` | `number` | | Message count before compaction |
+| `messagesRemoved` | `number` | | Messages removed |
+| `tokensRemoved` | `number` | | Tokens removed |
+| `summaryContent` | `string` | | LLM-generated summary of compacted history |
+| `checkpointNumber` | `number` | | Checkpoint snapshot number created for recovery |
+| `checkpointPath` | `string` | | File path where the checkpoint was stored |
+| `compactionTokensUsed` | `{ input, output, cachedInput }` | | Token usage for the compaction LLM call |
+| `requestId` | `string` | | GitHub request tracing ID for the compaction call |
+
+### `session.title_changed`
+
+Ephemeral. The session's auto-generated title was updated.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `title` | `string` | ✅ | New session title |
+
+### `session.context_changed`
+
+The session's working directory or repository context changed.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `cwd` | `string` | ✅ | Current working directory |
+| `gitRoot` | `string` | | Git repository root |
+| `repository` | `string` | | Repository in `"owner/name"` format |
+| `branch` | `string` | | Current git branch |
+
+### `session.usage_info`
+
+Ephemeral. Context window utilization snapshot.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `tokenLimit` | `number` | ✅ | Maximum tokens for the model's context window |
+| `currentTokens` | `number` | ✅ | Current tokens in the context window |
+| `messagesLength` | `number` | ✅ | Current message count in the conversation |
+
+### `session.task_complete`
+
+The agent has completed its assigned task.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `summary` | `string` | | Summary of the completed task |
+
+### `session.shutdown`
+
+The session has ended.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `shutdownType` | `"routine" \| "error"` | ✅ | Normal shutdown or crash |
+| `errorReason` | `string` | | Error description when `shutdownType` is `"error"` |
+| `totalPremiumRequests` | `number` | ✅ | Total premium API requests used |
+| `totalApiDurationMs` | `number` | ✅ | Cumulative API call time in milliseconds |
+| `sessionStartTime` | `number` | ✅ | Unix timestamp (ms) when the session started |
+| `codeChanges` | `{ linesAdded, linesRemoved, filesModified }` | ✅ | Aggregate code change metrics |
+| `modelMetrics` | `Record` | ✅ | Per-model usage breakdown |
+| `currentModel` | `string` | | Model selected at shutdown time |
+
+---
+
+## Permission & User Input Events
+
+These events are emitted when the agent needs approval or input from the user before continuing.
+
+### `permission.requested`
+
+Ephemeral. The agent needs permission to perform an action (run a command, write a file, etc.).
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Use this to respond via `session.respondToPermission()` |
+| `permissionRequest` | `PermissionRequest` | ✅ | Details of the permission being requested |
+
+The `permissionRequest` is a discriminated union on `kind`:
+
+| `kind` | Key Fields | Description |
+|--------|------------|-------------|
+| `"shell"` | `fullCommandText`, `intention`, `commands[]`, `possiblePaths[]` | Execute a shell command |
+| `"write"` | `fileName`, `diff`, `intention`, `newFileContents?` | Write/modify a file |
+| `"read"` | `path`, `intention` | Read a file or directory |
+| `"mcp"` | `serverName`, `toolName`, `toolTitle`, `args?`, `readOnly` | Call an MCP tool |
+| `"url"` | `url`, `intention` | Fetch a URL |
+| `"memory"` | `subject`, `fact`, `citations` | Store a memory |
+| `"custom-tool"` | `toolName`, `toolDescription`, `args?` | Call a custom tool |
+
+All `kind` variants also include an optional `toolCallId` linking back to the tool call that triggered the request.
+
+### `permission.completed`
+
+Ephemeral. A permission request was resolved.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Matches the corresponding `permission.requested` |
+| `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"` |
+
+### `user_input.requested`
+
+Ephemeral. The agent is asking the user a question.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Use this to respond via `session.respondToUserInput()` |
+| `question` | `string` | ✅ | The question to present to the user |
+| `choices` | `string[]` | | Predefined choices for the user |
+| `allowFreeform` | `boolean` | | Whether free-form text input is allowed |
+
+### `user_input.completed`
+
+Ephemeral. A user input request was resolved.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Matches the corresponding `user_input.requested` |
+
+### `elicitation.requested`
+
+Ephemeral. The agent needs structured form input from the user (MCP elicitation protocol).
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Use this to respond via `session.respondToElicitation()` |
+| `message` | `string` | ✅ | Description of what information is needed |
+| `mode` | `"form"` | | Elicitation mode (currently only `"form"`) |
+| `requestedSchema` | `{ type: "object", properties, required? }` | ✅ | JSON Schema describing the form fields |
+
+### `elicitation.completed`
+
+Ephemeral. An elicitation request was resolved.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Matches the corresponding `elicitation.requested` |
+
+---
+
+## Sub-Agent & Skill Events
+
+### `subagent.started`
+
+A custom agent was invoked as a sub-agent.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `toolCallId` | `string` | ✅ | Parent tool call that spawned this sub-agent |
+| `agentName` | `string` | ✅ | Internal name of the sub-agent |
+| `agentDisplayName` | `string` | ✅ | Human-readable display name |
+| `agentDescription` | `string` | ✅ | Description of what the sub-agent does |
+
+### `subagent.completed`
+
+A sub-agent finished successfully.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `toolCallId` | `string` | ✅ | Matches the corresponding `subagent.started` |
+| `agentName` | `string` | ✅ | Internal name |
+| `agentDisplayName` | `string` | ✅ | Display name |
+
+### `subagent.failed`
+
+A sub-agent encountered an error.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `toolCallId` | `string` | ✅ | Matches the corresponding `subagent.started` |
+| `agentName` | `string` | ✅ | Internal name |
+| `agentDisplayName` | `string` | ✅ | Display name |
+| `error` | `string` | ✅ | Error message |
+
+### `subagent.selected`
+
+A custom agent was selected (inferred) to handle the current request.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `agentName` | `string` | ✅ | Internal name of the selected agent |
+| `agentDisplayName` | `string` | ✅ | Display name |
+| `tools` | `string[] \| null` | ✅ | Tool names available to this agent; `null` for all tools |
+
+### `subagent.deselected`
+
+A custom agent was deselected, returning to the default agent. **Data payload is empty (`{}`)**.
+
+### `skill.invoked`
+
+A skill was activated for the current conversation.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `name` | `string` | ✅ | Skill name |
+| `path` | `string` | ✅ | File path to the SKILL.md definition |
+| `content` | `string` | ✅ | Full skill content injected into the conversation |
+| `allowedTools` | `string[]` | | Tools auto-approved while this skill is active |
+| `pluginName` | `string` | | Plugin the skill originated from |
+| `pluginVersion` | `string` | | Plugin version |
+
+---
+
+## Other Events
+
+### `abort`
+
+The current turn was aborted.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `reason` | `string` | ✅ | Why the turn was aborted (e.g., `"user initiated"`) |
+
+### `user.message`
+
+The user sent a message. Recorded for the session timeline.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `content` | `string` | ✅ | The user's message text |
+| `transformedContent` | `string` | | Transformed version after preprocessing |
+| `attachments` | `Attachment[]` | | File, directory, selection, or GitHub reference attachments |
+| `source` | `string` | | Message source identifier |
+| `agentMode` | `string` | | Agent mode: `"interactive"`, `"plan"`, `"autopilot"`, or `"shell"` |
+| `interactionId` | `string` | | CAPI interaction ID |
+
+### `system.message`
+
+A system or developer prompt was injected into the conversation.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `content` | `string` | ✅ | The prompt text |
+| `role` | `"system" \| "developer"` | ✅ | Message role |
+| `name` | `string` | | Source identifier |
+| `metadata` | `{ promptVersion?, variables? }` | | Prompt template metadata |
+
+### `external_tool.requested`
+
+Ephemeral. The agent wants to invoke an external tool (one provided by the SDK consumer).
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Use this to respond via `session.respondToExternalTool()` |
+| `sessionId` | `string` | ✅ | Session this request belongs to |
+| `toolCallId` | `string` | ✅ | Tool call ID for this invocation |
+| `toolName` | `string` | ✅ | Name of the external tool |
+| `arguments` | `object` | | Arguments for the tool |
+
+### `external_tool.completed`
+
+Ephemeral. An external tool request was resolved.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Matches the corresponding `external_tool.requested` |
+
+### `exit_plan_mode.requested`
+
+Ephemeral. The agent has created a plan and wants to exit plan mode.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Use this to respond via `session.respondToExitPlanMode()` |
+| `summary` | `string` | ✅ | Summary of the plan |
+| `planContent` | `string` | ✅ | Full plan file content |
+| `actions` | `string[]` | ✅ | Available user actions (e.g., approve, edit, reject) |
+| `recommendedAction` | `string` | ✅ | Suggested action |
+
+### `exit_plan_mode.completed`
+
+Ephemeral. An exit plan mode request was resolved.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Matches the corresponding `exit_plan_mode.requested` |
+
+### `command.queued`
+
+Ephemeral. A slash command was queued for execution.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Use this to respond via `session.respondToQueuedCommand()` |
+| `command` | `string` | ✅ | The slash command text (e.g., `/help`, `/clear`) |
+
+### `command.completed`
+
+Ephemeral. A queued command was resolved.
+
+| Data Field | Type | Required | Description |
+|------------|------|----------|-------------|
+| `requestId` | `string` | ✅ | Matches the corresponding `command.queued` |
+
+---
+
+## Quick Reference: Agentic Turn Flow
+
+A typical agentic turn emits events in this order:
+
+```
+assistant.turn_start → Turn begins
+├── assistant.intent → What the agent plans to do (ephemeral)
+├── assistant.reasoning_delta → Streaming thinking chunks (ephemeral, repeated)
+├── assistant.reasoning → Complete thinking block
+├── assistant.message_delta → Streaming response chunks (ephemeral, repeated)
+├── assistant.message → Complete response (may include toolRequests)
+├── assistant.usage → Token usage for this API call (ephemeral)
+│
+├── [If tools were requested:]
+│ ├── permission.requested → Needs user approval (ephemeral)
+│ ├── permission.completed → Approval result (ephemeral)
+│ ├── tool.execution_start → Tool begins
+│ ├── tool.execution_partial_result → Streaming tool output (ephemeral, repeated)
+│ ├── tool.execution_progress → Progress updates (ephemeral, repeated)
+│ ├── tool.execution_complete → Tool finished
+│ │
+│ └── [Agent loops: more reasoning → message → tool calls...]
+│
+assistant.turn_end → Turn complete
+session.idle → Ready for next message (ephemeral)
+```
+
+## All Event Types at a Glance
+
+| Event Type | Ephemeral | Category | Key Data Fields |
+|------------|-----------|----------|-----------------|
+| `assistant.turn_start` | | Assistant | `turnId`, `interactionId?` |
+| `assistant.intent` | ✅ | Assistant | `intent` |
+| `assistant.reasoning` | | Assistant | `reasoningId`, `content` |
+| `assistant.reasoning_delta` | ✅ | Assistant | `reasoningId`, `deltaContent` |
+| `assistant.streaming_delta` | ✅ | Assistant | `totalResponseSizeBytes` |
+| `assistant.message` | | Assistant | `messageId`, `content`, `toolRequests?`, `outputTokens?`, `phase?` |
+| `assistant.message_delta` | ✅ | Assistant | `messageId`, `deltaContent`, `parentToolCallId?` |
+| `assistant.turn_end` | | Assistant | `turnId` |
+| `assistant.usage` | ✅ | Assistant | `model`, `inputTokens?`, `outputTokens?`, `cost?`, `duration?` |
+| `tool.user_requested` | | Tool | `toolCallId`, `toolName`, `arguments?` |
+| `tool.execution_start` | | Tool | `toolCallId`, `toolName`, `arguments?`, `mcpServerName?` |
+| `tool.execution_partial_result` | ✅ | Tool | `toolCallId`, `partialOutput` |
+| `tool.execution_progress` | ✅ | Tool | `toolCallId`, `progressMessage` |
+| `tool.execution_complete` | | Tool | `toolCallId`, `success`, `result?`, `error?` |
+| `session.idle` | ✅ | Session | `backgroundTasks?` |
+| `session.error` | | Session | `errorType`, `message`, `statusCode?` |
+| `session.compaction_start` | | Session | *(empty)* |
+| `session.compaction_complete` | | Session | `success`, `preCompactionTokens?`, `summaryContent?` |
+| `session.title_changed` | ✅ | Session | `title` |
+| `session.context_changed` | | Session | `cwd`, `gitRoot?`, `repository?`, `branch?` |
+| `session.usage_info` | ✅ | Session | `tokenLimit`, `currentTokens`, `messagesLength` |
+| `session.task_complete` | | Session | `summary?` |
+| `session.shutdown` | | Session | `shutdownType`, `codeChanges`, `modelMetrics` |
+| `permission.requested` | ✅ | Permission | `requestId`, `permissionRequest` |
+| `permission.completed` | ✅ | Permission | `requestId`, `result.kind` |
+| `user_input.requested` | ✅ | User Input | `requestId`, `question`, `choices?` |
+| `user_input.completed` | ✅ | User Input | `requestId` |
+| `elicitation.requested` | ✅ | User Input | `requestId`, `message`, `requestedSchema` |
+| `elicitation.completed` | ✅ | User Input | `requestId` |
+| `subagent.started` | | Sub-Agent | `toolCallId`, `agentName`, `agentDisplayName` |
+| `subagent.completed` | | Sub-Agent | `toolCallId`, `agentName`, `agentDisplayName` |
+| `subagent.failed` | | Sub-Agent | `toolCallId`, `agentName`, `error` |
+| `subagent.selected` | | Sub-Agent | `agentName`, `agentDisplayName`, `tools` |
+| `subagent.deselected` | | Sub-Agent | *(empty)* |
+| `skill.invoked` | | Skill | `name`, `path`, `content`, `allowedTools?` |
+| `abort` | | Control | `reason` |
+| `user.message` | | User | `content`, `attachments?`, `agentMode?` |
+| `system.message` | | System | `content`, `role` |
+| `external_tool.requested` | ✅ | External Tool | `requestId`, `toolName`, `arguments?` |
+| `external_tool.completed` | ✅ | External Tool | `requestId` |
+| `command.queued` | ✅ | Command | `requestId`, `command` |
+| `command.completed` | ✅ | Command | `requestId` |
+| `exit_plan_mode.requested` | ✅ | Plan Mode | `requestId`, `summary`, `planContent`, `actions` |
+| `exit_plan_mode.completed` | ✅ | Plan Mode | `requestId` |