> ## Documentation Index
> Fetch the complete documentation index at: https://acp-docs.cxykevin.top/llms.txt
> Use this file to discover all available pages before exploring further.

# 会话信息更新

* 作者：[@ignatov](https://github.com/ignatov)

## 电梯演讲

向现有的 `SessionUpdate` 通知类型添加一个 `session_info_update` 变体，允许代理更新会话元数据（特别是标题/名称），从而实现客户端 UI 中的动态会话识别，而不需要新的端点。

## 现状

目前，ACP 协议通过 `session/new`、`session/load` 和 `session/list`（不稳定）提供会话管理。`session/list` 端点返回包含可选 `title` 字段的 `SessionInfo` 对象，用于在客户端 UI 中显示会话名称。

然而，有几个问题：

1. **无法传达标题更新** - `SessionInfo` 中的 `title` 字段在列表响应中是静态的。代理无法随着会话的发展动态更新它。

2. **没有实时元数据更新机制** - 与命令（`available_commands_update`）或模式（`current_mode_update`）不同，代理无法：
   * 在第一次有意义的交换后自动生成标题
   * 随着对话上下文的变化更新标题
   * 提供反映会话状态的自定义元数据

3. **与协议模式不一致** - 其他动态会话属性使用 `session/update` 通知（命令、模式、计划），但元数据没有等效机制。

当前的解决方法是让客户端：

* 维护自己的标题映射（不会持久化或同步）
* 只显示 `session/list` 中的静态元数据
* 无法实时接收代理生成的标题

## 我们提议对此做什么

向现有的 `SessionUpdate` 可区分联合添加一个新的 `session_info_update` 变体，允许代理通知客户端元数据更改。此更新将：

1. **遵循现有的 `SessionUpdate` 模式**：
   * 使用与 `available_commands_update`、`current_mode_update` 等相同的通知机制
   * 通过 `session/update` 方法发送
   * 代理发起，不需要请求/响应

2. **与 `SessionInfo` 结构保持一致**：
   * 包含与 `session/list` 中的 `SessionInfo` 相同的字段
   * 所有字段都是可选的（部分更新）
   * 支持增量元数据更新
   * **重要**：`SessionInfoUpdate` 必须与 `SessionInfo` 保持一致——当新字段添加到 `SessionInfo` 时，它们也应该作为可选字段添加到 `SessionInfoUpdate`

3. **支持常见用例**：
   * 代理在第一个提示后自动生成标题
   * 代理随着对话上下文的变化更新标题
   * 代理为客户端功能提供自定义元数据（标签、状态等）
   * 用户明确请求标题更改（代理用更新通知响应）

4. **无缝集成**：
   * 不需要新功能（使用现有的 `session/update` 机制）
   * 与 `session/list` 兼容——元数据应该持久化并在列表响应中反映
   * 在活动会话期间工作

### 通知结构

代理发送带有 `sessionUpdate: "session_info_update"` 的 `session/update` 通知：

```json theme={null}
{
  "jsonrpc": "2.0",
  "method": "session/update",
  "params": {
    "sessionId": "sess_abc123def456",
    "update": {
      "sessionUpdate": "session_info_update",
      "title": "实现用户身份验证",
      "_meta": {
        "tags": ["feature", "auth"],
        "priority": "high"
      }
    }
  }
}
```

### SessionInfoUpdate 类型

更新类型镜像 `SessionInfo`，但所有字段都是可选的：

```typescript theme={null}
{
  sessionUpdate: "session_info_update",
  title?: string | null,           // 更新或清除标题
  updatedAt?: string | null,        // ISO 8601 时间戳（通常由代理设置）
  _meta?: object | null             // 自定义元数据（与现有的合并）
}
```

**注意：** `sessionId` 和 `cwd` 不包括在内，因为：

* `sessionId` 已经在通知的 `params` 中
* `cwd` 是不可变的，在 `session/new` 期间设置

### 示例

#### 更新标题和工作目录元数据

在用户发送他们的第一个有意义的提示之后，代理可以生成并发送一个标题以及关于工作目录的元数据：

```json theme={null}
{
  "jsonrpc": "2.0",
  "method": "session/update",
  "params": {
    "sessionId": "sess_abc123def456",
    "update": {
      "sessionUpdate": "session_info_update",
      "title": "调试身份验证超时",
      "_meta": {
        "projectName": "api-server",
        "branch": "main"
      }
    }
  }
}
```

#### 随着对话发展更新标题

随着对话焦点转变：

```json theme={null}
{
  "jsonrpc": "2.0",
  "method": "session/update",
  "params": {
    "sessionId": "sess_abc123def456",
    "update": {
      "sessionUpdate": "session_info_update",
      "title": "调试身份验证超时 → 添加重试逻辑"
    }
  }
}
```

## 美好的未来

> 一旦这个功能存在，事情将如何发展？

1. **动态会话识别** - 代理可以：
   * 从对话内容中自动生成有意义的标题
   * 随着对话的发展更新标题
   * 提供更好的组织的丰富元数据

2. **改进的客户端 UI** - 客户端可以：
   * 在会话列表中显示实时标题更新
   * 显示会话状态、标签或其他元数据
   * 无需轮询 `session/list` 立即更新 UI

3. **一致的协议模式** - 会话元数据更新与其他动态会话属性（命令、模式）一样工作，创建统一模型

4. **双向工作流** - 与潜在的未来请求方法结合：
   * 用户重命名会话 → 客户端发送请求 → 代理用 `session_info_update` 通知确认
   * 代理自动生成标题 → 发送 `session_info_update` 通知 → 客户端显示它

5. **增强的用例**：
   * 自动设置标题和标签的会话模板
   * 通过 `_meta` 进行进度指示器
   * 通过元数据与外部工具集成
   * 丰富的会话浏览和过滤

## 实施细节和计划

### 第 1 阶段：模式更改

1. **更新 `schema.unstable.json`**：
   * 添加 `SessionInfoUpdate` 类型定义
   * 在 `SessionUpdate` oneOf 数组中添加新的变体
   * 使字段与 `SessionInfo` 保持一致但都是可选的

```json theme={null}
{
  "SessionInfoUpdate": {
    "description": "**不稳定**\n\n此功能尚未成为规范的一部分，可能会在任何时候被移除或更改。\n\n会话元数据的更新。所有字段都是可选的以支持部分更新。",
    "properties": {
      "_meta": {
        "description": "实现的扩展点"
      },
      "title": {
        "description": "会话的人类可读标题",
        "type": ["string", "null"]
      },
      "updatedAt": {
        "description": "上次活动的 ISO 8601 时间戳",
        "type": ["string", "null"]
      }
    },
    "type": "object"
  }
}
```

添加到 `SessionUpdate` oneOf：

```json theme={null}
{
  "allOf": [
    {
      "$ref": "#/$defs/SessionInfoUpdate"
    }
  ],
  "description": "**不稳定**\n\n此功能尚未成为规范的一部分，可能会在任何时候被移除或更改。\n\n会话元数据的更新",
  "properties": {
    "sessionUpdate": {
      "const": "session_info_update",
      "type": "string"
    }
  },
  "required": ["sessionUpdate"],
  "type": "object"
}
```

### 第 2 阶段：协议文档

2. **在 `/docs/protocol/session-metadata.mdx` 中创建文档**：
   * 解释通知机制
   * 提供常见模式的示例
   * 记录 `_meta` 的合并语义
   * 阐明与 `session/list` 的关系

3. **更新现有文档**：
   * 在 `/docs/protocol/session-setup.mdx` 中引用
   * 添加到 `/docs/protocol/prompt-turn.mdx` 会话更新部分

### 第 3 阶段：SDK 实施

4. **在 Rust SDK 中实现**：
   * 添加 `SessionInfoUpdate` 结构体
   * 添加 `SessionUpdate` 枚举中的变体
   * 更新代理和客户端特征中的通知处理
   * 为常见模式添加辅助方法

5. **在 TypeScript SDK 中实现**（如果适用）：
   * 添加 TypeScript 类型
   * 更新通知处理程序
   * 添加辅助方法

### 第 4 阶段：示例实施

6. **更新示例代理**：
   * 演示从第一个提示自动生成标题
   * 显示会话期间的元数据更新
   * 使用 `_meta` 进行自定义字段的示例

### 兼容性考虑

* **完全向后兼容**：这向现有机制添加了一个新的通知变体
* **没有破坏性更改**：现有代理和客户端继续工作
* **优雅降级**：不处理此通知的客户端只是忽略它
* **不需要新功能**：使用现有的 `session/update` 基础设施

### 设计决策

**为什么是通知而不是请求/响应？**

* 与现有的 `SessionUpdate` 模式（`available_commands_update`、`current_mode_update`）一致
* 代理根据会话状态发起更新
* 比双向请求/响应更简单
* 支持实时更新而不需要轮询

**为什么所有字段都是可选的？**

* 允许部分更新（只更新改变的）
* 更高效——不重新发送未更改的数据
* 更灵活的用例
* 镜像其他协议中的部分更新模式

**为什么不在更新中包含 `sessionId` 和 `cwd`？**

* `sessionId` 已经在通知 params 中
* `cwd` 是不可变的（在 `session/new` 中设置，永不改变）
* 保持更新专注于可变的元数据

**`_meta` 更新如何工作？**

* **合并语义**：新的 `_meta` 字段与现有的合并
* 要清除特定字段：`"_meta": { "fieldName": null }`
* 要清除所有自定义元数据：`"_meta": null`

### 安全考虑

* **没有额外的安全问题**：使用现有的会话身份验证
* **输入验证**：
  * 代理应该验证标题长度（建议最多 500 个字符）
  * 清理元数据以防止注入
  * 根据代理要求验证 `_meta` 结构
* **资源限制**：代理应该限制更新频率和元数据大小

## 常见问题

### 为什么不像 `session/update-metadata` 那样创建一个新的端点？

通知模式更合适，因为：

1. **一致性**：其他动态会话属性（命令、模式）使用通知
2. **代理发起**：代理通常从对话上下文生成标题
3. **实时**：没有请求/响应开销，更新自然流动
4. **更简单**：重用现有的 `session/update` 基础设施

### 这与 `session/list` 如何工作？

更新的元数据应该持久化并在后续的 `session/list` 调用中反映。通知为连接的客户端提供实时更新，而 `session/list` 提供发现的当前状态。

### 客户端可以触发标题更新吗？

这个 RFD 涵盖代理发起的更新。客户端发起的更新可以通过以下方式工作：

1. 客户端发送要求重命名会话的提示
2. 代理更新其内部状态
3. 代理发送 `session_info_update` 通知
4. 客户端接收并显示更新

如果需要，可以在未来的 RFD 中添加显式的请求/响应。

### 如果快速连续发送多个更新怎么办？

客户端应该按顺序增量应用更新。每个通知代表一个增量，而不是完全替换（除了明确设置为 `null` 的字段）。

### 代理是否应该自动设置 `updatedAt`？

是的，通常代理应该在发生任何会话活动时更新此时间戳，而不仅仅是元数据更改时。然而，在 `session_info_update` 中包含它允许代理在需要时明确控制它。

### 代理需要新功能吗？

不需要。支持 `session/update` 通知的所有代理都可以发送此变体。不识别它的客户端会忽略它（标准的 JSON-RPC 行为）。

### 这如何与 `session/fork` 交互？

在分叉时，父会话的元数据可以被复制（实现选择）。分叉的会话将有自己的 `sessionId`，并可以接收单独的 `session_info_update` 通知。

### 如果标题太长怎么办？

这是实现选择。代理**可以**：

* 截断长标题
* 拒绝更新（尽管这是通知，所以没有错误响应）
* 设置合理的限制（例如，500 个字符）

客户端**应该**优雅地处理长标题（在 UI 中截断，显示工具提示等）。

### `_meta` 可以有嵌套对象吗？

是的，`_meta` 是一个任意对象。代理定义其结构。合并语义递归应用——嵌套对象被合并，而不是完全替换。

### 您考虑了哪些替代方法，为什么选择这一个？

考虑了几种替代方案：

1. **添加一个新的请求/响应端点（`session/update-metadata`）**——这将与如何处理其他动态会话属性（命令、模式）不一致。通知模式对于代理发起的更新更合适。

2. **在 `session/new` 中添加标题参数**——只允许在创建时设置标题，不支持随着对话发展的动态更新。

3. **仅客户端元数据跟踪**——无法跨设备工作，可能不同步，并重复存储。这是当前的解决方法，有重大限制。

4. **用于所有属性的通用 `session/update` 请求**——可能与不可变属性（sessionId、cwd）冲突，并且不清楚什么可以更新。

提议的基于通知的方法：

* **一致**于现有协议模式
* **灵活**适用于代理发起和用户发起的更新
* **简单**实现和理解
* **可扩展**通过 `_meta` 字段

## 修订历史

* **2025-11-28**：初始提案草稿
