Skip to main content

电梯演讲

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

现状

目前,ACP 协议通过 session/newsession/loadsession/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_updatecurrent_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 通知:
{
  "jsonrpc": "2.0",
  "method": "session/update",
  "params": {
    "sessionId": "sess_abc123def456",
    "update": {
      "sessionUpdate": "session_info_update",
      "title": "实现用户身份验证",
      "_meta": {
        "tags": ["feature", "auth"],
        "priority": "high"
      }
    }
  }
}

SessionInfoUpdate 类型

更新类型镜像 SessionInfo,但所有字段都是可选的:
{
  sessionUpdate: "session_info_update",
  title?: string | null,           // 更新或清除标题
  updatedAt?: string | null,        // ISO 8601 时间戳(通常由代理设置)
  _meta?: object | null             // 自定义元数据(与现有的合并)
}
注意: sessionIdcwd 不包括在内,因为:
  • sessionId 已经在通知的 params
  • cwd 是不可变的,在 session/new 期间设置

示例

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

在用户发送他们的第一个有意义的提示之后,代理可以生成并发送一个标题以及关于工作目录的元数据:
{
  "jsonrpc": "2.0",
  "method": "session/update",
  "params": {
    "sessionId": "sess_abc123def456",
    "update": {
      "sessionUpdate": "session_info_update",
      "title": "调试身份验证超时",
      "_meta": {
        "projectName": "api-server",
        "branch": "main"
      }
    }
  }
}

随着对话发展更新标题

随着对话焦点转变:
{
  "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 保持一致但都是可选的
{
  "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:
{
  "allOf": [
    {
      "$ref": "#/$defs/SessionInfoUpdate"
    }
  ],
  "description": "**不稳定**\n\n此功能尚未成为规范的一部分,可能会在任何时候被移除或更改。\n\n会话元数据的更新",
  "properties": {
    "sessionUpdate": {
      "const": "session_info_update",
      "type": "string"
    }
  },
  "required": ["sessionUpdate"],
  "type": "object"
}

第 2 阶段:协议文档

  1. /docs/protocol/session-metadata.mdx 中创建文档
    • 解释通知机制
    • 提供常见模式的示例
    • 记录 _meta 的合并语义
    • 阐明与 session/list 的关系
  2. 更新现有文档
    • /docs/protocol/session-setup.mdx 中引用
    • 添加到 /docs/protocol/prompt-turn.mdx 会话更新部分

第 3 阶段:SDK 实施

  1. 在 Rust SDK 中实现
    • 添加 SessionInfoUpdate 结构体
    • 添加 SessionUpdate 枚举中的变体
    • 更新代理和客户端特征中的通知处理
    • 为常见模式添加辅助方法
  2. 在 TypeScript SDK 中实现(如果适用):
    • 添加 TypeScript 类型
    • 更新通知处理程序
    • 添加辅助方法

第 4 阶段:示例实施

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

兼容性考虑

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

设计决策

为什么是通知而不是请求/响应?
  • 与现有的 SessionUpdate 模式(available_commands_updatecurrent_mode_update)一致
  • 代理根据会话状态发起更新
  • 比双向请求/响应更简单
  • 支持实时更新而不需要轮询
为什么所有字段都是可选的?
  • 允许部分更新(只更新改变的)
  • 更高效——不重新发送未更改的数据
  • 更灵活的用例
  • 镜像其他协议中的部分更新模式
为什么不在更新中包含 sessionIdcwd
  • 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:初始提案草稿