添加qiming-rcoder模块
This commit is contained in:
596
qiming-rcoder/specs/agent-resume-acp/agent-resume-acp-plan.md
Normal file
596
qiming-rcoder/specs/agent-resume-acp/agent-resume-acp-plan.md
Normal file
@@ -0,0 +1,596 @@
|
||||
# Agent Resume via ACP 实现计划
|
||||
|
||||
## 方案概述
|
||||
|
||||
采用 **方式一:通过 NewSessionRequest 的 meta 参数** 实现 resume 功能。
|
||||
|
||||
核心思路:统一使用 `NewSessionRequest` + `_meta.claudeCode.options.resume` 传递 session_id,移除冗余的 `load_session` 尝试逻辑。
|
||||
|
||||
**重要**:Resume 是"尝试性"的,如果 session 不存在导致 Agent 启动失败,会自动降级为不传 resume 参数,创建新会话。
|
||||
|
||||
---
|
||||
|
||||
## Resume 失败降级方案(核心机制)
|
||||
|
||||
### 问题场景
|
||||
|
||||
当用户传入的 `session_id` 对应的会话不存在时(已过期、被清理、或从未存在),Agent 会启动失败:
|
||||
|
||||
```
|
||||
No conversation found for session id: abc-123
|
||||
```
|
||||
|
||||
### 降级策略
|
||||
|
||||
**优先尝试 resume,失败则降级为新会话**:
|
||||
|
||||
```
|
||||
第一次尝试:带 resume 参数
|
||||
↓
|
||||
├─ 成功 → 恢复上下文,继续对话
|
||||
│
|
||||
└─ 失败(任何原因)
|
||||
↓
|
||||
第二次尝试:不带 resume 参数
|
||||
↓
|
||||
└─ 成功 → 创建新会话
|
||||
```
|
||||
|
||||
**关键点**:不需要判断具体错误原因,只要 `has_resume && 启动失败`,就直接降级重试。
|
||||
|
||||
### 当前实现位置(需改动)
|
||||
|
||||
**文件**: `session_manager.rs:217-261`
|
||||
|
||||
**当前代码**(需简化):
|
||||
```rust
|
||||
// 检查是否因为 resume 导致的失败
|
||||
if has_resume
|
||||
&& (error_msg.contains("No conversation found")
|
||||
|| error_msg.contains("session")
|
||||
|| error_msg.contains("exited with code 1")) // ← 删除这些判断
|
||||
{
|
||||
// 降级...
|
||||
}
|
||||
```
|
||||
|
||||
**改动后代码**:
|
||||
```rust
|
||||
// 只要带 resume 且启动失败,就降级重试
|
||||
if has_resume {
|
||||
tracing::warn!(
|
||||
"⚠️ Agent 启动失败(带 resume),降级为不使用 resume 重试: error={}",
|
||||
error_msg
|
||||
);
|
||||
|
||||
// 创建新的 config,不包含 resume_session_id
|
||||
let retry_config = AgentStartConfig {
|
||||
system_prompt: start_config.system_prompt,
|
||||
mcp_servers: start_config.mcp_servers,
|
||||
extra_meta: start_config.extra_meta,
|
||||
service_type: start_config.service_type,
|
||||
resume_session_id: None, // ← 关键:去掉 resume
|
||||
};
|
||||
|
||||
tracing::info!("🔄 重试启动 Agent(不使用 resume)");
|
||||
|
||||
// 第二次尝试:不带 resume
|
||||
launcher.launch(..., retry_config, ...).await?
|
||||
} else {
|
||||
// 不带 resume 的失败,直接返回错误
|
||||
return Err(e);
|
||||
}
|
||||
```
|
||||
|
||||
### 降级流程图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ChatRequest { session_id: "abc-123" } │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ acp_worker.rs │
|
||||
│ └─ AgentStartConfig { resume_session_id: Some("abc-123") } │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ session_manager.rs │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ 第一次尝试 │ │
|
||||
│ │ launcher.launch(start_config) │ │
|
||||
│ │ → meta: { claudeCode.options.resume: "abc-123" } │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────┴───────────────┐ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌────────────────┐ ┌────────────────────┐ │
|
||||
│ │ ✅ 成功 │ │ ❌ 失败 │ │
|
||||
│ │ session 存在 │ │ "No conversation │ │
|
||||
│ │ 恢复上下文 │ │ found" │ │
|
||||
│ └────────────────┘ └────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ 第二次尝试(降级) │ │
|
||||
│ │ retry_config.resume_session_id = None │ │
|
||||
│ │ launcher.launch(retry_config) │ │
|
||||
│ │ → meta: { } ← 不包含 resume │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌────────────────┐ │
|
||||
│ │ ✅ 成功 │ │
|
||||
│ │ 创建新会话 │ │
|
||||
│ │ 返回新 session │ │
|
||||
│ └────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 本次改动对降级机制的影响
|
||||
|
||||
**需要简化**。移除错误关键字判断,改为:只要 `has_resume && 启动失败` 就降级。
|
||||
|
||||
| 层级 | 改动 | 降级机制 |
|
||||
|------|------|----------|
|
||||
| session_manager.rs | 简化降级判断逻辑 | **需改动** |
|
||||
| claude_code_launcher.rs | 简化会话创建逻辑 | 不涉及 |
|
||||
|
||||
---
|
||||
|
||||
## 当前代码状态分析
|
||||
|
||||
### ✅ 已正确实现的部分
|
||||
|
||||
| 文件 | 功能 | 状态 |
|
||||
|------|------|------|
|
||||
| `agent.rs:95-137` | `AgentStartConfig.build_meta()` 构建 `claudeCode.options.resume` | ✅ 完成 |
|
||||
| `acp_worker.rs:158-180` | 判断是否需要 resume 并设置 `resume_session_id` | ✅ 完成 |
|
||||
| `session_manager.rs:217-261` | Resume 失败降级重试机制(框架已完成,判断逻辑需简化) | ⚠️ 需简化 |
|
||||
|
||||
### ❌ 需要改动的部分
|
||||
|
||||
| 文件 | 问题 | 改动 |
|
||||
|------|------|------|
|
||||
| `claude_code_launcher.rs:438-480` | 冗余的 `load_session` 尝试逻辑 | 简化为统一 `new_session` |
|
||||
| `claude_code_launcher.rs:291-299` | `session_id` 参数冗余 | 移除此参数 |
|
||||
| `session_manager.rs:207-214, 247-254` | 调用 launch 时传递 `session_id_hint` | 移除此参数 |
|
||||
| `session_manager.rs:224-227` | 降级判断过于复杂(检测错误关键字) | 简化为 `if has_resume` |
|
||||
|
||||
---
|
||||
|
||||
## 改动任务清单
|
||||
|
||||
### Task 0: 简化 session_manager.rs 降级判断逻辑(新增)
|
||||
|
||||
**文件**: `crates/agent_abstraction/src/session/session_manager.rs`
|
||||
|
||||
**改动位置**: 约 223-228 行
|
||||
|
||||
**当前代码**:
|
||||
```rust
|
||||
// 检查是否因为 resume 导致的失败
|
||||
if has_resume
|
||||
&& (error_msg.contains("No conversation found")
|
||||
|| error_msg.contains("session")
|
||||
|| error_msg.contains("exited with code 1"))
|
||||
{
|
||||
```
|
||||
|
||||
**改动后代码**:
|
||||
```rust
|
||||
// 只要带 resume 且启动失败,就降级重试(不判断具体错误原因)
|
||||
if has_resume {
|
||||
```
|
||||
|
||||
**同时更新日志信息**:
|
||||
```rust
|
||||
tracing::warn!(
|
||||
"⚠️ Agent 启动失败(带 resume),降级为不使用 resume 重试: error={}",
|
||||
error_msg
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 简化 claude_code_launcher.rs 会话创建逻辑
|
||||
|
||||
**文件**: `crates/agent_abstraction/src/compat/claude_code_launcher.rs`
|
||||
|
||||
**改动位置**: 约 438-480 行
|
||||
|
||||
**当前代码**:
|
||||
```rust
|
||||
// 创建会话
|
||||
let session_id = match session_id_for_closure {
|
||||
Some(sid) => {
|
||||
debug!("尝试加载 ACP 会话[load_session]");
|
||||
let given_session_id = SessionId::new(sid);
|
||||
match client_conn
|
||||
.load_session(LoadSessionRequest::new(
|
||||
given_session_id.clone(),
|
||||
project_path_for_closure.clone(),
|
||||
))
|
||||
.await
|
||||
{
|
||||
Ok(resp) => {
|
||||
debug!("ACP 会话加载成功[load_session],{:?}", resp);
|
||||
given_session_id
|
||||
}
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"load_session 失败或未实现,回退创建新会话[new_session]: {:?}",
|
||||
e
|
||||
);
|
||||
// 注意:即使 load_session 失败,仍然会创建 new_session
|
||||
// resume_session_id 会通过 meta.claudeCode.options.resume 传递
|
||||
let new_session_request =
|
||||
NewSessionRequest::new(project_path_for_closure.clone())
|
||||
.mcp_servers(mcp_servers.clone())
|
||||
.meta(system_prompt_meta.clone());
|
||||
let resp = client_conn.new_session(new_session_request).await?;
|
||||
debug!("ACP 会话创建成功[new_session],{:?}", resp);
|
||||
resp.session_id
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
debug!("创建 ACP 会话[new_session]");
|
||||
let new_session_request =
|
||||
NewSessionRequest::new(project_path_for_closure.clone())
|
||||
.mcp_servers(mcp_servers)
|
||||
.meta(system_prompt_meta);
|
||||
let resp = client_conn.new_session(new_session_request).await?;
|
||||
debug!("ACP 会话创建成功[new_session],{:?}", resp);
|
||||
resp.session_id
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**改动后代码**:
|
||||
```rust
|
||||
// 创建会话(统一使用 new_session,resume 通过 meta 传递)
|
||||
// 如果 start_config.resume_session_id 有值,build_meta() 会自动构建
|
||||
// _meta.claudeCode.options.resume 结构
|
||||
debug!("创建 ACP 会话[new_session]");
|
||||
let new_session_request = NewSessionRequest::new(project_path_for_closure.clone())
|
||||
.mcp_servers(mcp_servers)
|
||||
.meta(system_prompt_meta);
|
||||
|
||||
let resp = client_conn
|
||||
.new_session(new_session_request)
|
||||
.await
|
||||
.context("ACP 会话创建失败")?;
|
||||
|
||||
debug!(
|
||||
"ACP 会话创建成功[new_session], session_id={}",
|
||||
resp.session_id.0
|
||||
);
|
||||
let session_id = resp.session_id;
|
||||
```
|
||||
|
||||
**同时需要移除的变量**:
|
||||
```rust
|
||||
// 约 317 行,移除这行
|
||||
let session_id_for_closure = session_id.clone();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: 移除 launch 方法的 session_id 参数
|
||||
|
||||
**文件**: `crates/agent_abstraction/src/compat/claude_code_launcher.rs`
|
||||
|
||||
**改动位置**: 约 279-299 行(方法签名和文档)
|
||||
|
||||
**当前代码**:
|
||||
```rust
|
||||
/// 启动 Claude Code ACP Agent 服务
|
||||
///
|
||||
/// # 参数
|
||||
/// - `project_id`: 项目 ID
|
||||
/// - `project_path`: 项目工作目录
|
||||
/// - `session_id`: 可选的会话 ID(用于恢复会话)
|
||||
/// - `model_provider`: 模型提供商配置
|
||||
/// - `start_config`: Agent 启动配置(包含系统提示词等)
|
||||
/// - `client`: ACP 客户端实现
|
||||
///
|
||||
/// # 返回值
|
||||
/// 返回 LauncherConnectionInfoComplete,包含会话信息和生命周期守卫
|
||||
pub async fn launch(
|
||||
&self,
|
||||
project_id: String,
|
||||
project_path: PathBuf,
|
||||
session_id: Option<String>,
|
||||
model_provider: Option<ModelProviderConfig>,
|
||||
start_config: AgentStartConfig,
|
||||
client: C,
|
||||
) -> Result<LauncherConnectionInfoComplete> {
|
||||
```
|
||||
|
||||
**改动后代码**:
|
||||
```rust
|
||||
/// 启动 Claude Code ACP Agent 服务
|
||||
///
|
||||
/// # 参数
|
||||
/// - `project_id`: 项目 ID
|
||||
/// - `project_path`: 项目工作目录
|
||||
/// - `model_provider`: 模型提供商配置
|
||||
/// - `start_config`: Agent 启动配置(包含系统提示词、resume_session_id 等)
|
||||
/// - `client`: ACP 客户端实现
|
||||
///
|
||||
/// # Resume 机制
|
||||
/// 如果需要恢复会话,通过 `start_config.resume_session_id` 传递 session_id,
|
||||
/// 会自动构建 `_meta.claudeCode.options.resume` 结构传递给 Agent。
|
||||
///
|
||||
/// # 返回值
|
||||
/// 返回 LauncherConnectionInfoComplete,包含会话信息和生命周期守卫
|
||||
pub async fn launch(
|
||||
&self,
|
||||
project_id: String,
|
||||
project_path: PathBuf,
|
||||
model_provider: Option<ModelProviderConfig>,
|
||||
start_config: AgentStartConfig,
|
||||
client: C,
|
||||
) -> Result<LauncherConnectionInfoComplete> {
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: 更新 session_manager.rs 的 launch 调用
|
||||
|
||||
**文件**: `crates/agent_abstraction/src/session/session_manager.rs`
|
||||
|
||||
**改动位置 1**: 约 207-214 行(第一次调用)
|
||||
|
||||
**当前代码**:
|
||||
```rust
|
||||
let result = launcher
|
||||
.launch(
|
||||
project_id.clone(),
|
||||
project_path.clone(),
|
||||
session_id_hint.clone(),
|
||||
model_provider.clone(),
|
||||
start_config.clone(),
|
||||
client,
|
||||
)
|
||||
.await;
|
||||
```
|
||||
|
||||
**改动后代码**:
|
||||
```rust
|
||||
let result = launcher
|
||||
.launch(
|
||||
project_id.clone(),
|
||||
project_path.clone(),
|
||||
model_provider.clone(),
|
||||
start_config.clone(),
|
||||
client,
|
||||
)
|
||||
.await;
|
||||
```
|
||||
|
||||
**改动位置 2**: 约 247-254 行(降级重试调用)
|
||||
|
||||
**当前代码**:
|
||||
```rust
|
||||
launcher
|
||||
.launch(
|
||||
project_id.clone(),
|
||||
project_path,
|
||||
session_id_hint,
|
||||
model_provider.clone(),
|
||||
retry_config,
|
||||
C::default(),
|
||||
)
|
||||
.await?
|
||||
```
|
||||
|
||||
**改动后代码**:
|
||||
```rust
|
||||
launcher
|
||||
.launch(
|
||||
project_id.clone(),
|
||||
project_path,
|
||||
model_provider.clone(),
|
||||
retry_config,
|
||||
C::default(),
|
||||
)
|
||||
.await?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: 清理 session_manager.rs 的方法签名(可选)
|
||||
|
||||
**文件**: `crates/agent_abstraction/src/session/session_manager.rs`
|
||||
|
||||
**改动位置**: `create_new_session` 方法签名(约 185-196 行)
|
||||
|
||||
**当前代码**:
|
||||
```rust
|
||||
pub async fn create_new_session(
|
||||
&self,
|
||||
project_id: String,
|
||||
project_path: PathBuf,
|
||||
session_id_hint: Option<String>,
|
||||
model_provider: Option<ModelProviderConfig>,
|
||||
start_config: AgentStartConfig,
|
||||
client: C,
|
||||
) -> Result<Arc<SessionInfo>> {
|
||||
```
|
||||
|
||||
**评估**:
|
||||
- `session_id_hint` 参数当前未被使用(只是传递给 launcher)
|
||||
- 可以移除,但需要检查 `get_or_create_session` 等上游调用方
|
||||
- **建议**: 暂时保留此参数,避免过大改动范围
|
||||
|
||||
---
|
||||
|
||||
### Task 5: 移除未使用的 import
|
||||
|
||||
**文件**: `crates/agent_abstraction/src/compat/claude_code_launcher.rs`
|
||||
|
||||
**改动位置**: 约 11-14 行
|
||||
|
||||
**当前代码**:
|
||||
```rust
|
||||
use agent_client_protocol::{
|
||||
Agent, Client, ClientSideConnection, Implementation, InitializeRequest, LoadSessionRequest,
|
||||
McpServer, McpServerStdio, NewSessionRequest, PromptRequest, SessionId,
|
||||
};
|
||||
```
|
||||
|
||||
**改动后代码**:
|
||||
```rust
|
||||
use agent_client_protocol::{
|
||||
Agent, Client, ClientSideConnection, Implementation, InitializeRequest,
|
||||
McpServer, McpServerStdio, NewSessionRequest, PromptRequest, SessionId,
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 改动验证清单
|
||||
|
||||
### 编译验证
|
||||
- [ ] `cargo build -p agent_abstraction` 编译通过
|
||||
- [ ] `cargo build --workspace` 全项目编译通过
|
||||
- [ ] `cargo clippy` 无新增警告
|
||||
|
||||
### 单元测试
|
||||
- [ ] `AgentStartConfig.build_meta()` 测试用例通过
|
||||
- [ ] 现有测试用例通过
|
||||
|
||||
### 集成测试
|
||||
- [ ] 新建会话:正常创建
|
||||
- [ ] Resume 会话:传入有效 session_id,恢复上下文
|
||||
- [ ] Resume 失败降级:传入无效 session_id,自动降级为新会话
|
||||
|
||||
---
|
||||
|
||||
## 实现顺序
|
||||
|
||||
```
|
||||
Step 1: Task 0 - 简化 session_manager.rs 降级判断逻辑
|
||||
│
|
||||
└─ 移除错误关键字判断,改为 if has_resume
|
||||
|
||||
Step 2: Task 1 - 简化 claude_code_launcher.rs 会话创建逻辑
|
||||
│
|
||||
├─ 移除 load_session 尝试
|
||||
├─ 统一使用 new_session
|
||||
└─ 移除 session_id_for_closure 变量
|
||||
|
||||
Step 3: Task 2 - 移除 launch 方法的 session_id 参数
|
||||
│
|
||||
└─ 更新方法签名和文档
|
||||
|
||||
Step 4: Task 3 - 更新 session_manager.rs 的 launch 调用
|
||||
│
|
||||
├─ 更新第一次 launch 调用
|
||||
└─ 更新降级重试调用
|
||||
|
||||
Step 5: Task 5 - 清理未使用的 import
|
||||
│
|
||||
└─ 移除 LoadSessionRequest
|
||||
|
||||
Step 6: 验证
|
||||
│
|
||||
├─ 编译验证
|
||||
├─ 运行测试
|
||||
└─ 手动测试 resume 流程
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 风险评估
|
||||
|
||||
| 风险 | 概率 | 影响 | 缓解措施 |
|
||||
|------|------|------|----------|
|
||||
| 移除 session_id 参数导致编译错误 | 低 | 低 | 搜索所有调用方并更新 |
|
||||
| Resume 功能回归 | 低 | 中 | 简化降级机制更健壮 |
|
||||
| 逻辑改动影响其他功能 | 低 | 中 | 充分测试 |
|
||||
|
||||
---
|
||||
|
||||
## 预估工作量
|
||||
|
||||
| Task | 预估代码行数 | 复杂度 |
|
||||
|------|-------------|--------|
|
||||
| Task 0 | -4, +2 | 低 |
|
||||
| Task 1 | -30, +15 | 低 |
|
||||
| Task 2 | -2, +5 | 低 |
|
||||
| Task 3 | -2, +0 | 低 |
|
||||
| Task 5 | -1, +0 | 低 |
|
||||
| **总计** | **约 -39 行,+22 行** | **低** |
|
||||
|
||||
---
|
||||
|
||||
## 附录:完整数据流(改动后)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ HTTP/gRPC Request │
|
||||
│ ChatRequest { session_id: "abc-123", prompt: "..." } │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ acp_worker.rs │
|
||||
│ ├─ 检查内存中是否存在会话 │
|
||||
│ │ └─ 存在且 session_id 匹配 → with_resume_session_id() │
|
||||
│ └─ 构建 AgentStartConfig { resume_session_id: Some("abc-123") }│
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ session_manager.rs │
|
||||
│ └─ launcher.launch(project_id, project_path, │
|
||||
│ model_provider, start_config, client) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ claude_code_launcher.rs │
|
||||
│ ├─ start_config.build_meta() 构建: │
|
||||
│ │ { │
|
||||
│ │ "claudeCode": { │
|
||||
│ │ "options": { "resume": "abc-123" } │
|
||||
│ │ } │
|
||||
│ │ } │
|
||||
│ └─ NewSessionRequest::new(cwd).meta(meta) ← 统一入口 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼ ACP session/new
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ claude-code-acp │
|
||||
│ ├─ newSession() 解析 _meta.claudeCode.options.resume │
|
||||
│ └─ query({ options: { resume: "abc-123" } }) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┴───────────────┐
|
||||
▼ ▼
|
||||
┌────────────────┐ ┌────────────────────────┐
|
||||
│ ✅ 会话存在 │ │ ❌ 会话不存在 │
|
||||
│ → 恢复上下文 │ │ → 抛出错误 │
|
||||
│ → 返回成功 │ │ "No conversation found"│
|
||||
└────────────────┘ └────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ session_manager.rs 降级处理 │
|
||||
│ ├─ 检测错误关键字 │
|
||||
│ ├─ 创建 retry_config { resume_session_id: None }│
|
||||
│ └─ 第二次 launcher.launch(retry_config) │
|
||||
└─────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ ✅ 成功创建新会话 │
|
||||
│ └─ 返回新的 session_id │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
592
qiming-rcoder/specs/agent-resume-acp/agent-resume-acp-spec.md
Normal file
592
qiming-rcoder/specs/agent-resume-acp/agent-resume-acp-spec.md
Normal file
@@ -0,0 +1,592 @@
|
||||
# Agent Resume via ACP 设计方案
|
||||
|
||||
## 1. 背景
|
||||
|
||||
### 1.1 需求描述
|
||||
通过 ACP 协议与 Agent 对话时,如果 Agent 停止后,希望能通过 `resume` 参数继续之前的上下文对话记录,与 Agent 继续对话。
|
||||
|
||||
### 1.2 相关资源
|
||||
- **claude-code-acp**: Zed 公司对 Claude Code 的 ACP 协议封装(参考:`tmp/claude-code-acp`)
|
||||
- **rust-sdk**: ACP 协议的 Rust SDK(参考:`tmp/rust-sdk`)
|
||||
- **当前实现**: `crates/agent_abstraction/src/compat/claude_code_launcher.rs`
|
||||
|
||||
## 2. 技术分析
|
||||
|
||||
### 2.1 ACP 协议中的会话恢复方式
|
||||
|
||||
ACP 协议定义了多种会话管理方法:
|
||||
|
||||
| 方法 | 功能 | 状态 |
|
||||
|------|------|------|
|
||||
| `session/new` (NewSessionRequest) | 创建新会话 | 稳定 |
|
||||
| `session/load` (LoadSessionRequest) | 加载已存在的会话并重放历史 | 稳定,但 claude-code-acp 未实现 |
|
||||
| `session/resume` (ResumeSessionRequest) | 恢复会话(不重放历史)| unstable,需要 `unstable_session_resume` feature |
|
||||
| `session/fork` (ForkSessionRequest) | 分叉会话 | unstable |
|
||||
|
||||
### 2.2 claude-code-acp 的 Resume 实现机制
|
||||
|
||||
通过分析 `tmp/claude-code-acp/src/acp-agent.ts`,发现 claude-code-acp 支持三种 resume 方式:
|
||||
|
||||
#### 方式一:通过 NewSessionRequest 的 meta 参数
|
||||
```typescript
|
||||
// acp-agent.ts:205-216
|
||||
async newSession(params: NewSessionRequest): Promise<NewSessionResponse> {
|
||||
return await this.createSession(params, {
|
||||
resume: (params._meta as NewSessionMeta | undefined)?.claudeCode?.options?.resume,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**传参结构**:
|
||||
```json
|
||||
{
|
||||
"cwd": "/path/to/project",
|
||||
"_meta": {
|
||||
"claudeCode": {
|
||||
"options": {
|
||||
"resume": "previous-session-id"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 方式二:通过 unstable_resumeSession 方法
|
||||
```typescript
|
||||
// acp-agent.ts:233-246
|
||||
async unstable_resumeSession(params: ResumeSessionRequest): Promise<ResumeSessionResponse> {
|
||||
return await this.createSession(
|
||||
{ cwd: params.cwd, mcpServers: params.mcpServers ?? [], _meta: params._meta },
|
||||
{ resume: params.sessionId }
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### 方式三:通过 load_session(claude-code-acp 不支持)
|
||||
```typescript
|
||||
// 返回 Error::method_not_found()
|
||||
```
|
||||
|
||||
### 2.3 createSession 内部 Resume 处理逻辑
|
||||
|
||||
```typescript
|
||||
// acp-agent.ts:593-709
|
||||
private async createSession(
|
||||
params: NewSessionRequest,
|
||||
creationOpts: { resume?: string; forkSession?: boolean } = {},
|
||||
): Promise<NewSessionResponse> {
|
||||
// 1. 确定 sessionId
|
||||
let sessionId;
|
||||
if (creationOpts.forkSession) {
|
||||
sessionId = randomUUID();
|
||||
} else if (creationOpts.resume) {
|
||||
sessionId = creationOpts.resume; // 👈 使用传入的 session_id
|
||||
} else {
|
||||
sessionId = randomUUID();
|
||||
}
|
||||
|
||||
// 2. 构建 extraArgs
|
||||
const extraArgs = { ...userProvidedOptions?.extraArgs };
|
||||
if (creationOpts?.resume === undefined || creationOpts?.forkSession) {
|
||||
extraArgs["session-id"] = sessionId; // 👈 新会话才设置 session-id
|
||||
}
|
||||
|
||||
// 3. 构建 Options 传递给 SDK
|
||||
const options: Options = {
|
||||
// ... 其他配置
|
||||
extraArgs,
|
||||
...creationOpts, // 👈 包含 { resume: sessionId }
|
||||
};
|
||||
|
||||
// 4. 调用 SDK 的 query 函数
|
||||
const q = query({ prompt: input, options });
|
||||
}
|
||||
```
|
||||
|
||||
**关键发现**:
|
||||
1. `resume` 参数最终通过 `Options` 传递给 `@anthropic-ai/claude-agent-sdk` 的 `query()` 函数
|
||||
2. SDK 会根据 `Options.resume` 自动恢复之前的会话上下文
|
||||
3. 当 resume 有值时,不设置新的 `session-id` extraArg
|
||||
|
||||
### 2.4 当前项目的实现分析
|
||||
|
||||
#### 当前流程 (`claude_code_launcher.rs:438-480`)
|
||||
```rust
|
||||
let session_id = match session_id_for_closure {
|
||||
Some(sid) => {
|
||||
// 尝试 load_session
|
||||
match client_conn.load_session(LoadSessionRequest::new(...)).await {
|
||||
Ok(resp) => given_session_id,
|
||||
Err(e) => {
|
||||
// 失败时回退到 new_session
|
||||
// resume_session_id 通过 meta.claudeCode.options.resume 传递
|
||||
let new_session_request = NewSessionRequest::new(...)
|
||||
.mcp_servers(mcp_servers)
|
||||
.meta(system_prompt_meta); // 👈 包含 resume
|
||||
client_conn.new_session(new_session_request).await?
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// 创建新会话
|
||||
client_conn.new_session(NewSessionRequest::new(...)).await?
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### 问题分析
|
||||
1. **冗余逻辑**:先尝试 `load_session`(必定失败),然后回退到 `new_session`
|
||||
2. **混淆概念**:`session_id` 参数和 `resume_session_id` 参数的关系不清晰
|
||||
3. **缺少 ACP 原生 resume**:未使用 `ResumeSessionRequest`
|
||||
|
||||
## 3. 设计方案
|
||||
|
||||
### 3.1 方案选择
|
||||
|
||||
推荐使用 **方式一:通过 NewSessionRequest 的 meta 参数** 实现 resume:
|
||||
|
||||
**理由**:
|
||||
1. **稳定性**:不依赖 unstable feature
|
||||
2. **兼容性**:适用于所有支持 ACP 的 Agent
|
||||
3. **当前实现已支持**:`AgentStartConfig.build_meta()` 已经正确构建了 `claudeCode.options.resume` 结构
|
||||
|
||||
### 3.2 核心改动
|
||||
|
||||
#### 3.2.1 简化 claude_code_launcher.rs 的会话创建逻辑
|
||||
|
||||
**改动位置**:`crates/agent_abstraction/src/compat/claude_code_launcher.rs:438-480`
|
||||
|
||||
**改动前**:
|
||||
```rust
|
||||
let session_id = match session_id_for_closure {
|
||||
Some(sid) => {
|
||||
// 先尝试 load_session,失败后再 new_session
|
||||
// ...
|
||||
}
|
||||
None => {
|
||||
// new_session
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**改动后**:
|
||||
```rust
|
||||
// 统一使用 new_session,resume 通过 meta 传递
|
||||
let new_session_request = NewSessionRequest::new(project_path_for_closure.clone())
|
||||
.mcp_servers(mcp_servers)
|
||||
.meta(system_prompt_meta); // 已包含 claudeCode.options.resume
|
||||
|
||||
let session_id = client_conn
|
||||
.new_session(new_session_request)
|
||||
.await?
|
||||
.session_id;
|
||||
```
|
||||
|
||||
#### 3.2.2 参数语义调整
|
||||
|
||||
| 参数 | 原语义 | 新语义 |
|
||||
|------|--------|--------|
|
||||
| `session_id: Option<String>` (launch 方法) | 传入则尝试 load_session | **移除**,不再使用 |
|
||||
| `AgentStartConfig.resume_session_id` | 通过 meta 传递 | **保持**,作为唯一的 resume 参数来源 |
|
||||
|
||||
#### 3.2.3 数据流示意图
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ RCoder / Agent Runner │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ ChatPrompt │
|
||||
│ ├─ session_id: "abc-123" (用于标识当前对话,可能是新的或历史的) │
|
||||
│ └─ ... │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ acp_worker.rs │
|
||||
│ ├─ 检查会话是否需要 resume │
|
||||
│ │ └─ 如果 session_id 匹配已存在的会话 → 设置 resume_session_id │
|
||||
│ └─ 构建 AgentStartConfig │
|
||||
│ └─ resume_session_id: Some("abc-123") │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ session_manager.rs │
|
||||
│ └─ 调用 ClaudeCodeLauncher::launch() │
|
||||
│ └─ start_config: AgentStartConfig { resume_session_id, ... } │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ claude_code_launcher.rs │
|
||||
│ ├─ start_config.build_meta() 构建: │
|
||||
│ │ { │
|
||||
│ │ "systemPrompt": { "append": "..." }, │
|
||||
│ │ "claudeCode": { │
|
||||
│ │ "options": { │
|
||||
│ │ "resume": "abc-123" ← resume_session_id │
|
||||
│ │ } │
|
||||
│ │ } │
|
||||
│ │ } │
|
||||
│ └─ NewSessionRequest::new(...).meta(meta) │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼ ACP 协议 (session/new)
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ claude-code-acp (Agent) │
|
||||
│ ├─ newSession() 解析 _meta.claudeCode.options.resume │
|
||||
│ ├─ createSession(params, { resume: "abc-123" }) │
|
||||
│ └─ query({ options: { resume: "abc-123", ... } }) │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────────┐
|
||||
│ @anthropic-ai/claude-agent-sdk │
|
||||
│ └─ 根据 Options.resume 恢复之前的会话上下文 │
|
||||
└──────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.3 详细代码改动
|
||||
|
||||
#### 文件 1: `crates/agent_abstraction/src/compat/claude_code_launcher.rs`
|
||||
|
||||
```rust
|
||||
// 改动 launch 方法签名,移除 session_id 参数
|
||||
pub async fn launch(
|
||||
&self,
|
||||
project_id: String,
|
||||
project_path: PathBuf,
|
||||
// session_id: Option<String>, // 👈 移除此参数
|
||||
model_provider: Option<ModelProviderConfig>,
|
||||
start_config: AgentStartConfig,
|
||||
client: C,
|
||||
) -> Result<LauncherConnectionInfoComplete> {
|
||||
```
|
||||
|
||||
```rust
|
||||
// 简化会话创建逻辑(约 438-480 行)
|
||||
// 改动前:
|
||||
// let session_id = match session_id_for_closure { ... }
|
||||
|
||||
// 改动后:
|
||||
debug!("创建 ACP 会话[new_session]");
|
||||
let new_session_request = NewSessionRequest::new(project_path_for_closure.clone())
|
||||
.mcp_servers(mcp_servers)
|
||||
.meta(system_prompt_meta); // 已包含 resume 信息
|
||||
|
||||
let session_id = client_conn
|
||||
.new_session(new_session_request)
|
||||
.await
|
||||
.context("ACP 会话创建失败")?
|
||||
.session_id;
|
||||
|
||||
debug!("ACP 会话创建成功[new_session], session_id={}", session_id.0);
|
||||
```
|
||||
|
||||
#### 文件 2: `crates/agent_abstraction/src/session/session_manager.rs`
|
||||
|
||||
```rust
|
||||
// 更新 launch 调用,移除 session_id 参数
|
||||
let result = launcher
|
||||
.launch(
|
||||
project_id.clone(),
|
||||
project_path.clone(),
|
||||
// None, // 👈 移除 session_id 参数
|
||||
model_provider.clone(),
|
||||
start_config.clone(),
|
||||
acp_client,
|
||||
)
|
||||
.await;
|
||||
```
|
||||
|
||||
### 3.4 Resume 失败降级机制
|
||||
|
||||
#### 3.4.1 问题场景
|
||||
|
||||
当传入的 `session_id` 对应的会话不存在时(例如:会话历史已过期、被清理、或从未存在),Agent 会启动失败并抛出错误。
|
||||
|
||||
**典型错误信息**:
|
||||
```
|
||||
No conversation found for session id: abc-123
|
||||
```
|
||||
或
|
||||
```
|
||||
exited with code 1
|
||||
```
|
||||
|
||||
#### 3.4.2 当前降级实现
|
||||
|
||||
降级逻辑已在 `session_manager.rs:217-261` 实现:
|
||||
|
||||
```rust
|
||||
// session_manager.rs
|
||||
let connection_info = match result {
|
||||
Ok(info) => info,
|
||||
Err(e) => {
|
||||
let error_msg = format!("{:?}", e);
|
||||
|
||||
// 检查是否因为 resume 导致的失败
|
||||
if has_resume
|
||||
&& (error_msg.contains("No conversation found")
|
||||
|| error_msg.contains("session")
|
||||
|| error_msg.contains("exited with code 1"))
|
||||
{
|
||||
tracing::warn!(
|
||||
"⚠️ Agent 启动失败(可能因 --resume),重试不使用 --resume: error={}",
|
||||
error_msg
|
||||
);
|
||||
|
||||
// 创建新的 config,不包含 resume_session_id
|
||||
let retry_config = AgentStartConfig {
|
||||
system_prompt: start_config.system_prompt,
|
||||
mcp_servers: start_config.mcp_servers,
|
||||
extra_meta: start_config.extra_meta,
|
||||
service_type: start_config.service_type,
|
||||
resume_session_id: None, // ✅ 去掉 resume
|
||||
};
|
||||
|
||||
tracing::info!("🔄 重试启动 Agent(不使用 --resume)");
|
||||
|
||||
// 重试启动
|
||||
launcher.launch(..., retry_config, ...).await?
|
||||
} else {
|
||||
// 其他错误,直接返回
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### 3.4.3 降级流程图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ChatRequest { session_id: "abc-123" } │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ session_manager.rs │
|
||||
│ └─ 第一次尝试:start_config.resume_session_id = Some("abc-123")│
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ claude_code_launcher.launch() │
|
||||
│ └─ NewSessionRequest + meta { claudeCode.options.resume } │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼ ACP 协议
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ claude-code-acp │
|
||||
│ └─ SDK query({ options: { resume: "abc-123" } }) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┴───────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌────────────────┐ ┌────────────────────────┐
|
||||
│ 会话存在 │ │ 会话不存在 │
|
||||
│ → 恢复上下文 │ │ → 抛出错误 │
|
||||
│ → 返回成功 │ │ "No conversation found"│
|
||||
└────────────────┘ └────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────┐
|
||||
│ session_manager.rs 降级处理 │
|
||||
│ ├─ 检测错误关键字 │
|
||||
│ ├─ 创建新 config: resume_session_id=None│
|
||||
│ └─ 第二次尝试:不带 resume │
|
||||
└─────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────┐
|
||||
│ claude_code_launcher.launch() │
|
||||
│ └─ NewSessionRequest (无 resume) │
|
||||
└─────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────┐
|
||||
│ 成功创建新会话 │
|
||||
│ └─ 返回新的 session_id │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 3.4.4 错误检测关键字
|
||||
|
||||
当前实现检测以下关键字来判断是否因 resume 失败:
|
||||
|
||||
| 关键字 | 来源 | 说明 |
|
||||
|--------|------|------|
|
||||
| `"No conversation found"` | SDK 错误 | 明确表示会话不存在 |
|
||||
| `"session"` | 通用匹配 | 捕获其他 session 相关错误 |
|
||||
| `"exited with code 1"` | 进程退出 | Agent 进程异常退出 |
|
||||
|
||||
**建议改进**:可以增加更精确的错误码匹配,例如:
|
||||
- `"ENOENT"` - 会话文件不存在
|
||||
- `"invalid session"` - 无效会话
|
||||
|
||||
#### 3.4.5 降级策略配置(可选扩展)
|
||||
|
||||
未来可以考虑增加降级策略配置:
|
||||
|
||||
```rust
|
||||
pub struct ResumePolicy {
|
||||
/// 是否启用降级
|
||||
pub enable_fallback: bool,
|
||||
/// 最大重试次数
|
||||
pub max_retries: u32,
|
||||
/// 降级时是否保留部分配置(如 MCP 服务器)
|
||||
pub preserve_mcp_servers: bool,
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5 备选方案:使用 ResumeSessionRequest (unstable)
|
||||
|
||||
如果需要使用 ACP 原生的 resume 方法,需要:
|
||||
|
||||
1. **启用 unstable feature**:
|
||||
```toml
|
||||
# Cargo.toml
|
||||
agent-client-protocol = { version = "0.6", features = ["unstable_session_resume"] }
|
||||
```
|
||||
|
||||
2. **添加 resume_session 调用**:
|
||||
```rust
|
||||
#[cfg(feature = "unstable_session_resume")]
|
||||
async fn resume_session(&self, session_id: SessionId, cwd: PathBuf) -> Result<SessionId> {
|
||||
let request = ResumeSessionRequest::new(session_id.clone(), cwd);
|
||||
let response = self.client_conn.resume_session(request).await?;
|
||||
Ok(response.session_id)
|
||||
}
|
||||
```
|
||||
|
||||
**不推荐此方案**:
|
||||
- 依赖 unstable API,可能随时变更
|
||||
- claude-code-acp 的 `unstable_resumeSession` 内部也是通过 `createSession` 处理,效果相同
|
||||
|
||||
## 4. API 变更
|
||||
|
||||
### 4.1 内部 API 变更
|
||||
|
||||
| 组件 | 改动 |
|
||||
|------|------|
|
||||
| `ClaudeCodeLauncher::launch()` | 移除 `session_id` 参数 |
|
||||
| `SessionManager` | 调用 launch 时不再传递 session_id |
|
||||
|
||||
### 4.2 外部 API 无变更
|
||||
|
||||
gRPC `ChatRequest` 和 HTTP API 保持不变,`session_id` 字段继续用于:
|
||||
1. 标识会话(用于进度订阅、取消等)
|
||||
2. 决定是否需要 resume(在 `acp_worker.rs` 中判断)
|
||||
|
||||
## 5. 测试计划
|
||||
|
||||
### 5.1 单元测试
|
||||
- [ ] `AgentStartConfig.build_meta()` 正确构建 resume 结构
|
||||
- [ ] 当 `resume_session_id` 为 None 时,meta 不包含 claudeCode 字段
|
||||
|
||||
### 5.2 集成测试
|
||||
- [ ] 新建会话:不传 session_id,正常创建
|
||||
- [ ] Resume 会话:传入历史 session_id,能够继续对话
|
||||
- [ ] Resume 失败降级:传入无效 session_id,能够降级为新会话
|
||||
|
||||
### 5.3 降级场景测试
|
||||
|
||||
#### 场景 1:会话不存在
|
||||
```bash
|
||||
# 传入一个不存在的 session_id
|
||||
curl -X POST /chat -d '{
|
||||
"project_id": "proj1",
|
||||
"session_id": "non-existent-session-id",
|
||||
"prompt": "Hello"
|
||||
}'
|
||||
|
||||
# 预期:
|
||||
# 1. 第一次尝试失败,日志显示 "No conversation found"
|
||||
# 2. 自动降级,去掉 resume 重试
|
||||
# 3. 成功创建新会话,返回新的 session_id
|
||||
```
|
||||
|
||||
#### 场景 2:会话已过期
|
||||
```bash
|
||||
# 使用一个曾经存在但已过期/清理的 session_id
|
||||
curl -X POST /chat -d '{
|
||||
"project_id": "proj1",
|
||||
"session_id": "expired-session-id",
|
||||
"prompt": "继续之前的对话"
|
||||
}'
|
||||
|
||||
# 预期:同场景 1,自动降级为新会话
|
||||
```
|
||||
|
||||
#### 场景 3:会话存在且有效
|
||||
```bash
|
||||
# 第一次对话
|
||||
curl -X POST /chat -d '{"project_id":"proj1", "prompt":"记住数字 42"}'
|
||||
# 返回 session_id: "valid-session-123"
|
||||
|
||||
# 第二次对话(resume)
|
||||
curl -X POST /chat -d '{
|
||||
"project_id": "proj1",
|
||||
"session_id": "valid-session-123",
|
||||
"prompt": "我之前说的数字是多少?"
|
||||
}'
|
||||
|
||||
# 预期:Agent 回答 "42"(证明上下文已恢复)
|
||||
```
|
||||
|
||||
### 5.4 手动测试场景
|
||||
```bash
|
||||
# 1. 首次对话
|
||||
curl -X POST /chat -d '{"project_id":"proj1", "prompt":"Hello"}'
|
||||
# 返回 session_id: "sess-abc-123"
|
||||
|
||||
# 2. 继续对话(resume)
|
||||
curl -X POST /chat -d '{"project_id":"proj1", "session_id":"sess-abc-123", "prompt":"继续刚才的话题"}'
|
||||
# Agent 应该能够记住之前的上下文
|
||||
```
|
||||
|
||||
## 6. 风险评估
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|----------|
|
||||
| SDK 不支持 resume | Agent 无法恢复上下文 | 当前 claude-code-acp 已支持 |
|
||||
| session_id 过期/无效 | resume 失败 | 已有降级逻辑,自动创建新会话 |
|
||||
| 协议变更 | resume 字段位置变化 | 跟踪 claude-code-acp 更新 |
|
||||
|
||||
## 7. 实现步骤
|
||||
|
||||
1. **Phase 1**:简化 `claude_code_launcher.rs`
|
||||
- 移除 `load_session` 尝试逻辑
|
||||
- 统一使用 `new_session` + meta
|
||||
|
||||
2. **Phase 2**:清理接口
|
||||
- 移除 launch 方法的 `session_id` 参数
|
||||
- 更新所有调用方
|
||||
|
||||
3. **Phase 3**:测试验证
|
||||
- 添加单元测试
|
||||
- 执行集成测试
|
||||
|
||||
## 8. 附录
|
||||
|
||||
### 8.1 相关代码位置
|
||||
|
||||
| 文件 | 行号 | 描述 |
|
||||
|------|------|------|
|
||||
| `crates/agent_abstraction/src/compat/claude_code_launcher.rs` | 291-573 | launch 方法 |
|
||||
| `crates/agent_abstraction/src/traits/agent.rs` | 95-142 | `AgentStartConfig.build_meta()` |
|
||||
| `crates/agent_abstraction/src/session/acp_worker.rs` | 164-167 | resume 判断逻辑 |
|
||||
| `crates/agent_abstraction/src/session/session_manager.rs` | 200-260 | launcher 调用 |
|
||||
| `crates/agent_abstraction/src/session/session_manager.rs` | 217-261 | **Resume 失败降级逻辑** |
|
||||
| `tmp/claude-code-acp/src/acp-agent.ts` | 205-246, 593-709 | claude-code-acp resume 实现 |
|
||||
|
||||
### 8.2 参考资料
|
||||
|
||||
- [ACP Protocol Specification](https://agentclientprotocol.com)
|
||||
- [claude-code-acp GitHub](https://github.com/zed-industries/claude-code-acp)
|
||||
- [Agent Client Protocol Rust SDK](https://github.com/agentclientprotocol/rust-sdk)
|
||||
16
qiming-rcoder/specs/agent-resume-acp/agent-resume-acp.md
Normal file
16
qiming-rcoder/specs/agent-resume-acp/agent-resume-acp.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# Introduction
|
||||
|
||||
## Project Alpha
|
||||
|
||||
我使用的ACP协议,来使用的agent ,目前agent使用的是"claude-code-acp","claude-code-acp"是zed公司
|
||||
对 claude code 工具的ACP协议封装,我去看官方 https://github.com/zed-industries/claude-code-acp 仓库里有"--resume" 的相关逻辑, 使用的anthropic的ts sdk 来对接的api(ts版本的官方文档是: https://platform.claude.com/docs/en/agent-sdk/typescript ).
|
||||
|
||||
rust版本的acp的官方源码(https://github.com/agentclientprotocol/rust-sdk),我下载到本地当前项目下的: tmp/rust-sdk 里了;
|
||||
|
||||
crates/agent_abstraction/src/compat/claude_code_launcher.rs 是创建agent session的逻辑,是我启动 "claude-code-acp"的部分代码.
|
||||
|
||||
我现在通过acp协议,和agent对话,如果agent停止后,下一次,我想通过 resume 参数,来继续之前的上下文对话记录,继续和agent对话,但当前我不知道怎么使用 resume 参数,来继续之前的上下文记录,来继续和agent对话,我需要先了解resume参数的使用方法,然后才能继续和agent对话。
|
||||
|
||||
注: 本地参考的源码,可以看项目下的: tmp 目录下,只能用于查阅参考,禁止依赖引入使用
|
||||
- tmp/claude-code-acp 官方的ts写的acp协议,封装的claude code ,支持acp协议调用agent
|
||||
- tmp/rust-sdk 官方ACP协议的rust版本的sdk
|
||||
Reference in New Issue
Block a user