Files
cursor-byok/README.md
T
2026-08-16 17:29:29 +08:00

171 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cursor Rust 服务端实施与验收计划
## 项目说明
`cursor-byok` 是一个兼容 Cursor Agent 客户端协议的自托管服务端项目,用于把 Cursor 客户端接入用户指定的 LLM Provider。项目根据真实客户端流量和提取出的 protobuf 协议实现,不依赖 Cursor 原服务保存对话状态。
当前 Rust 服务 `cursor-server` 实现以下完整链路:
- 通过 `RunSSE + BidiAppend` 组成的双向协议与 Cursor 客户端通信。
- 将 OpenAI Chat、OpenAI Responses 和 Anthropic 的流式响应统一为内部 `ResponseEvent`
- 运行无状态 LLM Loop`LLM → 客户端工具执行 → 结果追加 → 下一轮 LLM`,直到 Turn 完成或被新 Run 打断。
- 以 append-only messages 作为上下文唯一事实源,保证相邻 LLM 请求的稳定前缀和可重复投射。
- 支持文本、thinking、tool start、参数增量、tool result、usage、model、rules、commands、skills、MCP 和 subagent 上下文。
- 使用 SQLite 持久化 messages、Run 状态、Blob CAS、引用边和 outbox。
- 使用不可变 Blob 对象图表达 Conversation、Turn、UserMessage 和 StepsBlobID 为原始内容的 `SHA-256`
- 在客户端确认 Blob 已存储后发布 checkpoint,并提供单工具粒度的历史回滚和未确认操作恢复。
运行时职责划分如下:Cursor 客户端负责真正执行本地工具并保存服务端同步的 Blob;`cursor-server` 负责 Loop 决策、上下文投射、Provider 调用、状态持久化和 checkpoint 构造。Todo/Plan 等业务状态不单独维护,而是从 messages 确定性推导。
协议与状态模型的抓包结论见 [Cursor上下文与状态同步抓包分析.md](./Cursor上下文与状态同步抓包分析.md)。Rust 服务的启动方式和运行配置见 [cursor-server/README.md](./cursor-server/README.md)。
## 目录硬约束
下列目录、文件名和职责是实现验收条件,不是建议。首版只允许一个 `cursor-server` crate;代码必须落在对应文件,不得用 `core.rs``service.rs` 等总入口替代,也不得提前创建 MCP/subagent 空模块。新增文件必须说明为何现有职责无法容纳;删除、改名或移动下列文件必须先同步修改本计划。
```text
cursor-byok/
├── cursor-server/ # 新 Rust 服务
│ ├── Cargo.toml
│ ├── build.rs # 从 cursor-proto/proto 生成 prost 类型
│ ├── README.md # 启动方式、架构和核心不变量
│ │
│ ├── migrations/
│ │ └── 0001_initial.sql # Blob、messages、runs、outbox
│ │
│ ├── src/
│ │ ├── main.rs # 进程入口
│ │ ├── lib.rs # 模块出口
│ │ ├── app.rs # 依赖组装、启动和关闭
│ │ ├── config.rs # 地址、数据库、provider 配置
│ │ ├── error.rs # 服务统一错误
│ │ │
│ │ ├── model/ # 纯领域类型,不依赖 Cursor/provider
│ │ │ ├── mod.rs
│ │ │ ├── message.rs # CanonicalMessage、Role、Origin
│ │ │ ├── runtime_tag.rs # RuntimeEvent、exactly-once 约束
│ │ │ ├── conversation.rs # Conversation、Turn、revision
│ │ │ ├── tool.rs # ToolCall、ToolResult
│ │ │ └── usage.rs # provider usage 与 Turn usage
│ │ │
│ │ ├── run/ # Loop 引擎和一次 request 的状态机
│ │ │ ├── mod.rs
│ │ │ ├── registry.rs # request_id → RunHandle
│ │ │ ├── actor.rs # 每个 Run 一个 actor
│ │ │ ├── command.rs # run_request、exec/KV result、abort
│ │ │ ├── inbox.rs # append_seqno 排序、去重
│ │ │ ├── loop_engine.rs # LLM → Tool → LLM 主循环
│ │ │ └── lifecycle.rs # turn_ended/checkpoint/EndStream
│ │ │
│ │ ├── cursor/ # Cursor 协议适配器
│ │ │ ├── mod.rs
│ │ │ ├── proto.rs # include prost 生成代码
│ │ │ ├── connect.rs # 5-byte Connect envelope
│ │ │ ├── handlers.rs # Axum 路由入口
│ │ │ ├── bidi_append.rs # 上行 AgentClientMessage
│ │ │ ├── run_sse.rs # 下行 AgentServerMessage
│ │ │ ├── interaction.rs # 交互事件、Tool args 和 usage 投射
│ │ │ ├── exec.rs # Exec 上行/下行解析
│ │ │ ├── pending.rs # Exec/Interaction 的运行期 ID 关联
│ │ │ ├── tools.rs # 唯一工具路由、本地工具和 Cursor step index
│ │ │ ├── tool_result.rs # typed result、UI completion 和结果通道
│ │ │ ├── blob_sync.rs # KV GET/SET、ACK、重试
│ │ │ └── checkpoint.rs # Blob 图和 checkpoint 构造
│ │ │
│ │ ├── provider/ # LLM 端点适配器
│ │ │ ├── mod.rs # Provider trait
│ │ │ ├── event.rs # Canonical ResponseEvent
│ │ │ ├── openai_chat.rs # 第一条可运行链路
│ │ │ ├── openai_responses.rs
│ │ │ └── anthropic.rs
│ │ │
│ │ ├── prompting/ # 模型请求编译
│ │ │ ├── mod.rs
│ │ │ ├── assets.rs # 校验并嵌入根目录 prompt/
│ │ │ ├── compiler.rs # messages + mode + tools
│ │ │ ├── projector.rs # CanonicalMessage → provider 格式
│ │ │ └── derived_state.rs # 从 messages fold Todo/Plan
│ │ │
│ │ └── store/ # SQLite 持久化
│ │ ├── mod.rs
│ │ ├── sqlite.rs # pool、事务、PRAGMA
│ │ ├── messages.rs # append-only messages
│ │ ├── blobs.rs # CAS 与引用边
│ │ ├── conversations.rs # conversation head/revision
│ │ ├── runs.rs # 活动 Run 和恢复信息
│ │ └── outbox.rs # KV/checkpoint 待确认操作
│ │
│ └── tests/
│ ├── support/
│ │ ├── fake_provider.rs
│ │ ├── fake_cursor.rs
│ │ └── fixtures.rs
│ ├── text_turn.rs # 纯文本完整 Turn
│ ├── tool_loop.rs # LLM → Tool → LLM
│ ├── runtime_tag_once.rs # Runtime tag 不重复追加
│ ├── prefix_stability.rs # M(n) 是 M(n+1) 前缀
│ ├── checkpoint_recovery.rs # 单 Tool 回滚
│ ├── interrupt.rs # 新 Run 打断旧 Run
│ └── connect_wire.rs # Connect 二进制兼容性
├── cursor-proto/ # 现有 protobuf 提取和源文件
├── cursor-backend/ # 现有 Go 抓包调试器
├── prompt/ # 已复制的完整模式资产
└── docs/
```
## 按文件实施顺序与通过条件
1. `Cargo.toml``build.rs``src/{main,lib,app,config,error}.rs`:服务能加载配置、迁移数据库、生成 Cursor protobuf 并启动/优雅关闭。
2. `src/model/*.rs``src/store/*.rs``migrations/0001_initial.sql`:实现纯领域消息、runtime tag、tool/usage,以及 append-only messages、Blob CAS/引用边、revision、run 恢复与 outbox`tests/runtime_tag_once.rs``tests/prefix_stability.rs` 必须通过。
3. `src/cursor/{proto,connect,handlers,bidi_append,run_sse}.rs`:实现抓包一致的 5-byte Connect envelope、二进制 RunSSE、BidiAppend 解码和 `append_seqno` 排序去重;`tests/connect_wire.rs` 必须通过。
4. `src/provider/{event,openai_chat,openai_responses,anthropic}.rs``src/prompting/*.rs`:三个端点统一为 canonical `ResponseEvent`;所有 mode 的 prompt/tool 资产可加载;messages 投射幂等且保持严格前缀。
5. `src/run/*.rs``src/cursor/{tools,interaction,exec,pending,tool_result}.rs`:一个 request 一个 RunActorLoop 只处理统一 `ToolCompletion`,工具名称到 Exec/Interaction/Local 的唯一映射只存在于 `cursor/tools.rs`;完成 toolstart 占位、参数增量、客户端执行、打断与 usage。每个完成的工具必须原子追加 `assistant(tool_call) → tool(result)`,整批完成前不得进入下一次 LLM。
6. `src/cursor/{blob_sync,checkpoint}.rs``src/store/{blobs,outbox}.rs`:构造不可变 Blob 对象图,KV SET 未 ACK 前不得发布引用它的 checkpointcheckpoint 达到单工具粒度,最终状态重复发布后再 EndStream;`tests/checkpoint_recovery.rs` 必须通过。
7. `tests/{text_turn,tool_loop,interrupt}.rs``tests/support/*.rs`:覆盖纯文本 Turn、完整工具循环、新 Run 打断旧 Run和恢复路径。最终验收命令固定为 `cargo fmt --check``cargo clippy --all-targets -- -D warnings``cargo test --all-targets`
模块依赖方向固定为:
HTTP/Connect
cursor adapter
run actor
model + prompting
provider / client tools
store + checkpoint
几个关键决定:
model/ 不引用 Cursor protobuf,也不引用具体 provider。
cursor/ 只负责协议转换,不能包含 Loop 业务决策。
provider/ 只把不同端点转换为统一 ResponseEvent。
prompting/derived_state.rs 只 fold messages,不持久化 Todo/Plan。
store/messages.rs 是上下文唯一事实源。
store/outbox.rs 保存尚未确认的 Blob/checkpoint 操作。
MCP 和 subagent 暂时不建空目录:MCP 先作为动态 Tool 接入;subagent 复用 RunActor,需求落地时再加入 run/subagent.rs。
依赖建议:
```
tokio 异步运行时
axum + hyper Connect HTTP 服务
prost + prost-build protobuf
protoc-bin-vendored 避免系统 protoc 依赖
sqlx/sqlite 持久化和事务
reqwest provider HTTP
eventsource-stream provider SSE
serde/serde_json 模型和工具 JSON
sha2 + base64 BlobID
bytes 二进制载荷
tokio-util CancellationToken
thiserror + tracing 错误和日志
include_dir 编译期嵌入 prompt/ 资产
```
Cursor上下文与状态同步抓包分析.md 来告诉你很多信息,你需要一次性读他
Users/leokun/Library/Application Support/cursor-byok/cursor-proxy-debugger.db 是cursor的原服务抓包信息,内容由/Users/leokun/Documents/cursor-byok/cursor-backend产生