> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/agentclientprotocol/typescript-sdk/llms.txt
> Use this file to discover all available pages before exploring further.

# Handling Session Updates

> Process real-time updates from the agent during prompt execution

## Overview

During a prompt turn, the agent sends real-time updates via the `sessionUpdate` notification. These updates allow your client to provide live feedback to the user as the agent works.

## Session Update Flow

When you call `connection.prompt()`, the agent processes the request and sends multiple `sessionUpdate` notifications before finally responding:

```typescript theme={null}
Client                                Agent
  |                                     |
  |-- prompt request ------------------>|
  |                                     |
  |<-- sessionUpdate (thinking) --------|
  |<-- sessionUpdate (message chunk) ---| (many)
  |<-- sessionUpdate (tool call) -------|
  |<-- sessionUpdate (tool complete) ---|
  |<-- sessionUpdate (message chunk) ---| (many)
  |                                     |
  |<-- prompt response -----------------|
```

<Note>
  Clients SHOULD continue accepting tool call updates even after sending a `session/cancel` notification, as the agent may send final updates before responding with the cancelled stop reason.
</Note>

## Update Types

The `SessionNotification` contains an `update` field with a `sessionUpdate` discriminator:

```typescript theme={null}
interface SessionNotification {
  sessionId: string;
  update: SessionUpdate;
}

type SessionUpdate =
  | AgentMessageChunk
  | AgentThoughtChunk
  | UserMessageChunk
  | ToolCall
  | ToolCallUpdate
  | Plan
  | CurrentModeUpdate
  | ConfigOptionsUpdate
  | AvailableCommandsUpdate;
```

### AgentMessageChunk

Streaming chunks of the agent's response message.

<ParamField path="sessionUpdate" type="'agent_message_chunk'">
  Discriminator value.
</ParamField>

<ParamField path="index" type="number">
  Message index in the conversation.
</ParamField>

<ParamField path="content" type="MessageContent">
  The content chunk:

  * `{ type: "text", text: string }` - Text chunk
  * `{ type: "image", ... }` - Image content
  * `{ type: "audio", ... }` - Audio content
</ParamField>

#### Example

```typescript theme={null}
if (update.sessionUpdate === "agent_message_chunk") {
  if (update.content.type === "text") {
    // Stream text to UI
    appendToMessageDisplay(update.index, update.content.text);
  } else if (update.content.type === "image") {
    // Display image
    displayImage(update.content.data, update.content.mimeType);
  }
}
```

### AgentThoughtChunk

Streaming chunks of the agent's internal reasoning (thinking process).

<ParamField path="sessionUpdate" type="'agent_thought_chunk'">
  Discriminator value.
</ParamField>

<ParamField path="index" type="number">
  Message index.
</ParamField>

<ParamField path="content" type="MessageContent">
  Thought content (typically text).
</ParamField>

#### Example

```typescript theme={null}
if (update.sessionUpdate === "agent_thought_chunk") {
  // Show in a separate "thinking" panel
  appendToThinkingPanel(update.content.text);
}
```

### UserMessageChunk

Streaming chunks of user messages (used when loading sessions).

<ParamField path="sessionUpdate" type="'user_message_chunk'">
  Discriminator value.
</ParamField>

<ParamField path="index" type="number">
  Message index.
</ParamField>

<ParamField path="content" type="MessageContent">
  User message content.
</ParamField>

### ToolCall

A new tool call has been initiated.

<ParamField path="sessionUpdate" type="'tool_call'">
  Discriminator value.
</ParamField>

<ParamField path="toolCallId" type="string">
  Unique identifier for this tool call.
</ParamField>

<ParamField path="title" type="string">
  Human-readable title (e.g., "Read file: config.ts").
</ParamField>

<ParamField path="status" type="ToolCallStatus">
  Initial status: `"pending"`, `"running"`, `"requires_permission"`, etc.
</ParamField>

<ParamField path="content" type="ToolCallContent[]">
  Tool call content (text, code blocks, patches, terminals, etc.).
</ParamField>

#### Example

```typescript theme={null}
if (update.sessionUpdate === "tool_call") {
  // Create a new tool call UI element
  createToolCallCard({
    id: update.toolCallId,
    title: update.title,
    status: update.status,
    content: update.content,
  });
}
```

### ToolCallUpdate

A tool call's status or content has been updated.

<ParamField path="sessionUpdate" type="'tool_call_update'">
  Discriminator value.
</ParamField>

<ParamField path="toolCallId" type="string">
  The tool call being updated.
</ParamField>

<ParamField path="status" type="ToolCallStatus" optional>
  New status if changed: `"running"`, `"completed"`, `"failed"`, `"cancelled"`, etc.
</ParamField>

<ParamField path="appendContent" type="ToolCallContent[]" optional>
  Content to append to the tool call.
</ParamField>

#### Example

```typescript theme={null}
if (update.sessionUpdate === "tool_call_update") {
  const toolCallCard = findToolCallCard(update.toolCallId);
  
  // Update status
  if (update.status) {
    toolCallCard.setStatus(update.status);
  }
  
  // Append new content
  if (update.appendContent) {
    update.appendContent.forEach(content => {
      toolCallCard.appendContent(content);
    });
  }
}
```

### Plan

The agent has created an execution plan.

<ParamField path="sessionUpdate" type="'plan'">
  Discriminator value.
</ParamField>

<ParamField path="description" type="string">
  Human-readable plan description.
</ParamField>

<ParamField path="steps" type="PlanStep[]">
  Array of planned steps.
</ParamField>

#### Example

```typescript theme={null}
if (update.sessionUpdate === "plan") {
  displayPlan({
    description: update.description,
    steps: update.steps.map(step => ({
      description: step.description,
      status: step.status,
    })),
  });
}
```

### CurrentModeUpdate

The session's mode has changed.

<ParamField path="sessionUpdate" type="'current_mode_update'">
  Discriminator value.
</ParamField>

<ParamField path="currentMode" type="SessionMode">
  The new current mode.
</ParamField>

### ConfigOptionsUpdate

Session configuration options have changed.

<ParamField path="sessionUpdate" type="'config_options_update'">
  Discriminator value.
</ParamField>

<ParamField path="configOptions" type="ConfigOption[]">
  Updated configuration options.
</ParamField>

### AvailableCommandsUpdate

Available slash commands have changed.

<ParamField path="sessionUpdate" type="'available_commands_update'">
  Discriminator value.
</ParamField>

<ParamField path="availableCommands" type="AvailableCommand[]">
  Commands the agent can execute.
</ParamField>

## Implementation Example

Here's a complete implementation of `sessionUpdate`:

```typescript theme={null}
class MyClient implements acp.Client {
  async sessionUpdate(params: acp.SessionNotification): Promise<void> {
    const { sessionId, update } = params;
    
    switch (update.sessionUpdate) {
      case "agent_message_chunk":
        if (update.content.type === "text") {
          // Stream text output
          this.appendMessage(sessionId, update.index, update.content.text);
        }
        break;

      case "agent_thought_chunk":
        if (update.content.type === "text") {
          // Show thinking process
          this.appendThought(sessionId, update.content.text);
        }
        break;

      case "tool_call":
        // Create new tool call display
        this.createToolCall(sessionId, {
          id: update.toolCallId,
          title: update.title,
          status: update.status,
          content: update.content,
        });
        break;

      case "tool_call_update":
        // Update existing tool call
        const toolCall = this.getToolCall(sessionId, update.toolCallId);
        
        if (update.status) {
          toolCall.setStatus(update.status);
        }
        
        if (update.appendContent) {
          toolCall.appendContent(update.appendContent);
        }
        break;

      case "plan":
        // Display execution plan
        this.showPlan(sessionId, {
          description: update.description,
          steps: update.steps,
        });
        break;

      case "current_mode_update":
        // Update mode indicator
        this.setMode(sessionId, update.currentMode);
        break;

      case "config_options_update":
        // Update configuration UI
        this.updateConfigOptions(sessionId, update.configOptions);
        break;

      case "available_commands_update":
        // Update command palette
        this.updateAvailableCommands(sessionId, update.availableCommands);
        break;

      default:
        console.warn("Unknown update type:", update);
    }
  }
}
```

## Rendering Tool Call Content

Tool calls contain various content types:

```typescript theme={null}
function renderToolCallContent(content: ToolCallContent) {
  switch (content.type) {
    case "text":
      return renderText(content.text);
    
    case "code":
      return renderCodeBlock({
        code: content.code,
        language: content.language,
        path: content.path,
      });
    
    case "patch":
      return renderDiffView({
        diff: content.diff,
        path: content.path,
      });
    
    case "terminal":
      return renderTerminal({
        id: content.terminalId,
        output: content.output,
        exitStatus: content.exitStatus,
      });
    
    case "image":
      return renderImage({
        data: content.data,
        mimeType: content.mimeType,
      });
    
    default:
      return renderUnknownContent(content);
  }
}
```

## Handling Cancellation

When the user cancels a prompt:

```typescript theme={null}
class MyClient implements acp.Client {
  private activeCancellations = new Set<string>();
  
  async cancelPrompt(sessionId: string) {
    // Mark as cancelled
    this.activeCancellations.add(sessionId);
    
    // Send cancellation
    await this.connection.cancel({ sessionId });
  }
  
  async sessionUpdate(params: acp.SessionNotification): Promise<void> {
    // Continue processing updates even after cancellation
    const isCancelled = this.activeCancellations.has(params.sessionId);
    
    if (isCancelled) {
      // Show updates in a "cancelled" state
      this.renderUpdateAsCancelled(params.update);
    } else {
      this.renderUpdate(params.update);
    }
  }
  
  async handlePromptResponse(response: acp.PromptResponse) {
    if (response.stopReason === "cancelled") {
      // Clean up cancellation state
      this.activeCancellations.delete(sessionId);
      this.showCancelledMessage();
    }
  }
}
```

## Performance Considerations

### Debouncing UI Updates

For high-frequency updates like text chunks, consider debouncing:

```typescript theme={null}
class MessageBuffer {
  private buffer = "";
  private timeout: NodeJS.Timeout | null = null;
  
  append(text: string) {
    this.buffer += text;
    
    if (this.timeout) {
      clearTimeout(this.timeout);
    }
    
    this.timeout = setTimeout(() => {
      this.flush();
    }, 16); // ~60fps
  }
  
  flush() {
    if (this.buffer) {
      updateUI(this.buffer);
      this.buffer = "";
    }
  }
}
```

### Virtual Scrolling

For long conversations, use virtual scrolling to render only visible messages.

## See Also

* [Implementing the Client Interface](/clients/implementing-client-interface)
* [Permission Requests](/clients/permission-requests)
* [Protocol: Prompt Turn](https://agentclientprotocol.com/protocol/prompt-turn)
