agentscope-ai / agentscope-ai/QwenPaw

Bug: Cursor ACP Runner — 扩展方法处理违反 JSON-RPC 协议导致 WritableIterable is closed

オープン
#7,607 コメント 2 件 リアクション 0 件 担当者 1 名 @x1n95c が担当を希望しています GitHub で見る
主要言語
Python
スター
34.9k
フォーク
3.1k
平均マージ
1日 15時間
マージ済み PR(30日)
225

説明

# Bug Report: Cursor ACP Runner — 扩展方法处理违反 JSON-RPC 协议导致流崩溃

## 环境

- QwenPaw 版本:当前最新版(venv 安装)
- OS:macOS (Darwin 23.2.0, arm64)
- Cursor CLI:`/Users/hollay/.local/bin/agent`(`agent acp` 模式)
- ACP Runner 配置:`tool_parse_mode: call_title`, `trusted: true`

## 问题概述

使用 `delegate_external_agent` 工具通过 ACP 协议与 Cursor CLI 交互时,每次任务完成后 Cursor 端稳定复现 `Error: RetriableError: WritableIterable is closed` 错误。任务结果本身完整送达,但会话流在收尾阶段异常关闭。

**影响**:简单任务结果可正常接收;但长任务的流式进度上报可能受影响,且 cursor 的扩展方法(如 `cursor/ask_question`、`cursor/create_plan`)无法正常工作。

## 复现步骤

1. 配置 cursor ACP runner:
```json
{
"enabled": true,
"command": "/Users/hollay/.local/bin/agent",
"args": ["acp"],
"env": {},
"trusted": true,
"tool_parse_mode": "call_title",
"stdio_buffer_limit_bytes": 52428800
}
```
2. 调用 `delegate_external_agent(action="start", runner="cursor", message="执行 echo test")`
3. 任务正常完成并返回结果
4. **每次**任务完成后末尾出现:`Error: RetriableError: WritableIterable is closed`
5. 稳定复现(两次独立测试均出现)

## 根因分析

### 问题 1(P0):`client.py` 对 Cursor 扩展方法抛错误,违反 JSON-RPC 协议

**文件**:`qwenpaw/agents/acp/client.py`,`ACPHostedClient` 类

**当前代码**(约第 280-291 行):
```python
async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
_ = params
self._unsupported_method(method)

async def ext_notification(self, method: str, params: dict[str, Any]) -> None:
_ = params
self._unsupported_method(method)

def _unsupported_method(self, method: str) -> NoReturn:
raise RequestError(
code=-32601,
message=f"Unsupported ACP extension method: {method}",
)
```

**问题**:QwenPaw 对**所有** Cursor 扩展方法(包括通知类)抛 `-32601` 错误。

根据 [Cursor 官方 ACP 文档](https://cursor.com/cn/docs/cli/acp),Cursor 发送两类扩展方法:

| 方法 | 类型 | 文档要求 | QwenPaw 当前行为 | 后果 |
|---|---|---|---|---|
| `cursor/ask_question` | 阻塞 | 必须返回 JSON-RPC 响应(answered/skipped/cancelled) | 抛 `-32601` 错误 | cursor 收到 error 响应,行为未定义 |
| `cursor/create_plan` | 阻塞 | 必须返回响应(accepted/rejected/cancelled) | 抛 `-32601` 错误 | 同上 |
| `cursor/update_todos` | **通知** | 即发即弃,**无需响应** | 抛 `-32601` 错误 | ⚠️ **协议违规**:对无 id 的通知返回 error response |
| `cursor/task` | **通知** | 即发即弃 | 抛错误 | 同上 |
| `cursor/generate_image` | **通知** | 即发即弃 | 抛错误 | 同上 |

**关键**:JSON-RPC 2.0 规定**通知(无 id)不应收到响应**。QwenPaw 对通知也抛 error → Cursor 的 ACP server 收到意外响应 → 流状态错乱 → `WritableIterable is closed`。

### 问题 2(P1):`service.py` 缺少 authenticate 步骤

**文件**:`qwenpaw/agents/acp/service.py`,`_open_conversation` 方法

**当前流程**:`spawn_agent_process` → `conn.initialize()` → `conn.new_session()`

**文档要求流程**:`initialize` → `authenticate(methodId: "cursor_login")` → `session/new` → `session/prompt`

缺少 `authenticate` 步骤。目前靠 `agent login` 预认证绕过,但如果 Cursor token 过期或未登录,会话会静默失败。

## 修复建议

### P0:修复 ext_method / ext_notification

```python
# ext_notification: 静默忽略通知(通知不需要响应)
async def ext_notification(self, method: str, params: dict[str, Any]) -> None:
# 通知类方法 fire-and-forget,无需响应
# 可选:记录 debug 日志
logger.debug("Ignoring ACP extension notification: %s", method)
return None

# ext_method: 返回 cancelled outcome 而非抛错误
async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
logger.debug("Received ACP extension method: %s (not implemented)", method)
# 返回 cancelled outcome,让 cursor 能正常继续
return {"outcome": "cancelled"}
```

### P1:补充 authenticate 步骤

在 `_open_conversation` 的 `conn.initialize()` 之后、`conn.new_session()` 之前,增加:
```python
try:
await conn.authenticate(method_id="cursor_login")
except Exception:
# 若已预认证(agent login / CURSOR_API_KEY),authenticate 可能失败但不影响会话
logger.debug("authenticate step skipped or failed (may be pre-authenticated)")
```

## 验证

修复后预期:
- `WritableIterable is closed` 错误消失
- Cursor 的 `cursor/ask_question` / `cursor/create_plan` 阻塞方法能正常收到 cancelled 响应并继续
- 通知类方法不再产生协议违规错误

## 附注

此问题在 `qoder-cli` runner 上不复现(qoder-cli 不发送 Cursor 特有的扩展方法),仅影响 cursor runner。codex runner 同样正常。

コントリビューションガイド

コントリビューションガイドを開く

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。