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上下文与状态同步抓包分析.md,当前实现约束见 一次性重构计划计划.md

核心数据流

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。

目录边界

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 两段。

验证

cd cursor-server
cargo fmt --check
cargo clippy --all-targets -- -D warnings
cargo test --all-targets
S
Description
No description provided
Readme MIT
22 MiB
Languages
Go 86.4%
Vue 6.3%
JavaScript 4.8%
CSS 1%
NSIS 0.6%
Other 0.8%