Files
cursor-byok/README.md
T

88 lines
4.8 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-byok
`cursor-byok` 是一个基于真实 Cursor Agent 流量与 protobuf 实现的自托管服务端,用于把 Cursor 客户端接入用户指定的 LLM Provider。
当前 Rust 服务 `cursor-server` 已实现:
- Cursor `RunSSE + BidiAppend` 双向协议与 Connect envelope。
- OpenAI Chat、OpenAI Responses、Anthropic 三种无状态流式端点。
- `LLM → 客户端工具 → 结果提交 → 下一轮 LLM` 的通用 Loop。
- append-only canonical messages、不可变 revision 分支和同对话 Run 抢占。
- Cursor typed tool UI、Exec/Interaction、多阶段编辑、MCP 与子代理。
- Blob CAS、KV GET/SET ACK、两阶段 checkpoint 和 pending ToolRound 恢复。
- 未匹配 Cursor backend 路由原样流式转发到上游。
- React + Vite + TypeScript + Tailwind 管理台,支持 Provider 配置、模型发现和调用明细。
- 每次 Provider 调用的时间、模型快照、状态和 authoritative usage;详细模式保存脱敏请求与原始流响应。
启动方式见 [cursor-server/README.md](./cursor-server/README.md)。协议证据见 [Cursor上下文与状态同步抓包分析.md](./Cursor上下文与状态同步抓包分析.md),当前实现约束见 [一次性重构计划计划.md](./docs/一次性重构计划计划.md)。
## 核心数据流
```text
HTTP / Connect
Cursor adapter
↓ ClientCommand / ClientEvent
RunEngine
canonical messages + selected revision
typed ModelRequest
Provider adapter → HTTP/SSE → ModelEvent
```
Loop 不依赖 Cursor protobuf、Blob、checkpoint、数字 wire id 或具体 Provider JSON。Cursor adapter 和 Provider adapter 只在各自边界做协议投射。
## 状态与 checkpoint
- SQLite 中的 immutable messages 与 revision 父链是对话事实源;回滚只选择旧 revision 并建立新分支。
- ToolRound 保存完整 assistant、原始 call 顺序和真实 result 完成顺序;结果未齐时不会把悬空 tool call 投给下一轮模型。
- BlobID 是 `SHA-256(data)`;Blob 类型来自引用字段,不编码在 ID 中。
- checkpoint 引用的新 Blob 必须先收到对应 KV SET ACK。协议中不存在 checkpoint ACK,也不保存跨流 outbox。
- staged checkpoint 内联完整 pending assistantToolRound 全部结果提交后才折叠进 stable roots。抓包没有单 ToolResult checkpoint,因此实现也不制造该状态。
- settled checkpoint 必须先于下一轮 LLM 调用。最终文本轮严格发送 `turn_ended → staged → settled → settled 重发 → EndStream`
- Cursor.app 只把 `turn_ended` 前的 checkpoint 作为自动恢复候选;恢复 pending assistant 时先继续工具,不重复调用 LLM。
## 目录边界
```text
cursor-server/src/
├── control/ # 客户端无关的 Provider、模型、调用观测 HTTP API
├── client/ # 所有客户端共用的最小 command/event port
├── model/ # canonical message、revision、ModelSpec、typed history
├── run/ # 协议无关 Loop、ModelCycle、ToolRound 和 RunRegistry
├── provider/ # 各 LLM 端点请求与流事件适配
├── cursor/
│ ├── request/ # RunRequest → PreparedRun/CursorRunContext
│ ├── prompting/ # Cursor prompt、工具 catalog 和 mode manifest
│ ├── projection/ # Cursor AI-SDK stable/pending JSON 编解码
│ ├── interaction/ # UI 更新、InteractionQuery、typed ToolCall 渲染
│ ├── tools/ # Cursor 工具 transport、runtime、dispatch 和 result
│ └── checkpoint/ # root/Turn/derived/recovery 与串行 worker
└── store/ # SQLite messages、revision、ToolRound、Run 和 Blob CAS
console/ # React 管理台;只依赖 control API,不依赖 Cursor protobuf
```
详细到文件的目标目录和验收项只在重构计划中维护,README 不复制第二份易漂移的完整文件清单。
## 工程原则
- 不保留旧路径、兼容层或失败后的隐式 fallback。
- 同一状态只有一个所有者;协议层不做 Loop 决策。
- PromptSpec、ModelSpec 和 selected revision 决定可重放的 ModelRequestrequest id、时间和 model call id 不进入模型输入。
- 前缀稳定限定在相同 PromptSpec/ModelSpec/Provider route;新 Run 切换模型或模式时只替换 Cursor system root,其他历史 message roots 继续复用。
- Provider replay state 只回传给产生它的端点;可展示 thinking 不是跨端点 reasoning 字段。
- Provider usage 只采用端点报告的单轮最终值,不自行估算。
- 同一个取消信号覆盖等待 HTTP 响应头和读取 SSE 两段。
## 验证
```bash
cd cursor-server
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test --all-targets
```