This commit is contained in:
leokun
2026-06-30 10:38:52 +08:00
commit c083be5ec2
312 changed files with 146628 additions and 0 deletions
@@ -0,0 +1,177 @@
# backend/store 日志反查
当用户只给一个 id,或明确让你从本地记录里反查一次请求、会话、模型调用、工具调用或 provider 错误时,优先读这份参考。
## 当前固定路径
当前用户机器上的固定助手根目录:
- `~/.cursor-local-assistant-v2`
当前最关键的是三类内容:
- `history/<conversationId>/state.json`
- 会话元数据与当前状态。
- 重点字段:`request_id` `conversation_id`、`root_conversation_id`、`parent_conversation_id`、`parent_tool_call_id`、`mode`、`current_loop_status`、`current_request_id`、`current_turn_seq`、`context_version`、`next_turn_seq`、`next_entry_seq`、`latest_request_prefix`、`last_provider_call`、`current_todos`、`current_plans`、token / compaction 字段。
- `history/<conversationId>/context.json`
- append-only 语义历史。
- 重点字段:`version`、`items[]`。
- `items[]` 中每个 entry 通常有 `seq`、`turn_seq`、`request_id`、`role`、`kind`、`tool_call_id`、`parent_tool_call_id`、`payload`、`created_at`。
- `history/usage.json`
- provider call 与 turn usage 聚合。
- 重点字段:`totals`、`daily`、`recent_events`、`event_index`。
`logs/app.log` 是运行日志;它只用于补充运行时证据,不是会话事实源。
当前实现不再支持:
- DB-backed store / searchable conversation memory
- HTTP/protocol trace debug UI
- `data/data.sqlite` / `protocol_traces`
- `history/<conversationId>/conversation.json`
- `history/<conversationId>/turns/<n>/request.json|sse.jsonl|summary.json`
- 根目录或会话目录下的旧 `latest.json`、`summary.json`、`replay.json`、`runtime.json`、`request.json`、`recovery.json`、`entries.jsonl`、数字 turn 目录
这些旧产物会被 `internal/backend/forwarder/history_maintenance.go` 清理。不要把它们当成当前事实源。
## 用户发来一个 id 时的固定步骤
### 1. 先判断 id 类型
不要先假设它是 `requestId`。按这个顺序缩小范围:
1. 是否是 `conversationId`
- 检查 `history/<id>/state.json` 和 `history/<id>/context.json` 是否存在。
2. 是否是 `requestId`
- 在 `history/*/state.json` 中查 `current_request_id`、`latest_request_prefix.request_id`、`last_provider_call.request_id`。
- 在 `history/*/context.json` 的 `items[].request_id` 中查。
- 在 `logs/app.log` 中查。
3. 是否是 `modelCallId`
- 在 `state.json` 中查 `latest_request_prefix.model_call_id`、`last_provider_call.model_call_id`。
- 在 `context.json.items[].payload` 中查 `model_call_id`。
- 在 `logs/app.log` 中查 `model_call_id=<id>`。
4. 是否是 `toolCallId` / `exec_id`
- 在 `context.json.items[].tool_call_id`、`items[].payload` 中查。
- 在协议/工具相关日志中查。
可以用本地脚本或 `rg` 做只读反查。不要再用 SQLite 查询模板。
### 2. 拿到 `conversationId` 后看两份事实源
```bash
HISTORY_ROOT="$HOME/.cursor-local-assistant-v2/history"
CONV_ID="<conversation-id>"
ls -la "$HISTORY_ROOT/$CONV_ID"
```
重点检查:
- `state.json`
- `current_loop_status`:`idle`、`running`、`waiting_tool`、`completed`、`canceled`、`provider_error`、`failed`
- `current_request_id`、`current_turn_seq`
- `latest_request_prefix`:最近一次 provider 请求的 provider/model/openai_endpoint/model_call_id/prompt token 摘要
- `last_provider_call`:最近 provider 状态与错误文本
- `next_entry_seq`、`next_turn_seq`、`context_version`
- `current_todos`、`current_plans`
- `context.json`
- `version` 是否与 `state.context_version` 对齐
- `items[]` 是否按 `seq` 稳定递增
- 同一 `turn_seq` 下是否有预期的 user/request_context/prompt_context/assistant/tool_result/metadata entries
- 是否有重复、缺失或顺序异常
- `usage.json`
- 通过 `event_index` 或 `recent_events` 查 request/model-call 相关 usage
- `totals.cache_read_tokens / (totals.cache_read_tokens + totals.input_tokens)` 可粗略看 cache hit
### 3. Provider 调用证据现在在哪里
当前 provider artifact recorder 的行为:
- `RecordLLMRequest(...)`
- 只缓存当前 provider call 的请求摘要。
- 如果 payload 可解析 provider/model/openai_endpoint,会更新 `state.latest_request_prefix`。
- 不再写 `request.json`。
- `AppendLLMResponseChunk(...)`
- 当前是 no-op。
- 不再写 `sse.jsonl`。
- `RecordLLMSummary(...)`
- 只补齐当前 provider call summary,并更新 `state.latest_request_prefix.prompt_tokens_total`。
- usage 聚合写入 `history/usage.json`。
- 不再写 `summary.json`。
所以 provider 错误排查应优先看:
- `state.last_provider_call`
- `state.latest_request_prefix`
- `context.json.items` 里的 `metadata/provider_error/turn_completed` 等 payload
- `history/usage.json`
- `logs/app.log`
- `internal/backend/agent/model/openai.go` / `anthropic.go` 的请求构造和错误解析
## 这些文件是怎么生成的
### 根路径
- `internal/appdata/paths.go`
- `RootDir()` 固定为 `~/.cursor-local-assistant-v2`
- `HistoryRootPath()` 为 `~/.cursor-local-assistant-v2/history`
- `UsageFilePath()` 为 `~/.cursor-local-assistant-v2/history/usage.json`
- `LogsRootPath()` 为 `~/.cursor-local-assistant-v2/logs`
### `state.json + context.json`
来源链路:
- `internal/backend/forwarder/file_store.go`
- `CreateConversation`
- `LoadConversation`
- `AppendEntries`
- `SaveConversationWithEntries`
- `UpdateConversationMeta`
- `ReplaceEntries`
- `internal/backend/forwarder/service.go`
- `handleRunIntent` 开始新 loop / turn
- `appendConversationEntries` 追加语义事件
- `internal/backend/forwarder/projector.go`
- `ProjectPromptReplay()` 把 `context.json.items` 投影为 provider messages
稳定结论:
- `state.json` 是当前状态和可变元数据。
- `context.json.items` 是 replayable 语义历史。
- 发给 LLM 的历史由 projector 从 `context.json.items` 投影,不是从 provider artifacts 重放。
- `state.json.entries` 只是内存结构 `ConversationFile` 的字段;落盘时可投影历史在 `context.json.items`。
### `usage.json`
来源链路:
- `internal/backend/forwarder/usage_store.go`
- `UsageFileStore.UpsertEvent`
- `UsageFileStore.LookupEvent`
- `internal/backend/forwarder/token_usage.go`
- `internal/historymetrics/`
稳定结论:
- `usage.json` 是全局 usage 聚合,不属于单个 conversation 的语义历史。
- `recent_events` 只保留最近有限数量事件;长期总量看 `totals` / `daily`。
### legacy 清理
来源链路:
- `internal/backend/forwarder/history_maintenance.go`
稳定结论:
- `turns/`、`conversation.json`、`entries.jsonl`、`request.json`、`summary.json` 等都是 legacy artifact。
- 启动后的 history maintenance 会清理这些旧产物。
## 快速判断规则
- 用户只发一个 id 时,先查 `history/<id>/state.json` 是否存在;不存在再扫 `state.json/context.json/logs`。
- 请求失败时,先看 `state.last_provider_call`、`context.json.items` 的错误 metadata、`logs/app.log`;不要找 `turns/<n>/summary.json`。
- pending / 工具不收口时,先看同一 `turn_seq` 的 tool call 和 tool result entries,再对照协议上行 `exec_client_message` / `exec_client_control_message` / `interaction_response`。
- prefix cache 异常时,先看 `context.json.items` 的稳定追加顺序和 `usage.json` 的 cache token 字段。
- 如果 history 与日志冲突,优先相信当前仍在更新的 `state.json/context.json`,再用日志解释运行时经过了哪条路径。
@@ -0,0 +1,147 @@
# 文件地图
## 已安装客户端 bundle
优先核对这些实际运行中的客户端文件:
- `/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js`
- `/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js`
- `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-always-local/dist/main.js`
- `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-always-local/dist/gitWorker.js`
- `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-agent-exec/dist/main.js`
- `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-agent-exec/dist/*.js`
- `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-agent-worker/dist/main.js`
当前安装包里 `cursor-agent` 已拆成 split bundle;旧路径 `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-agent/dist/main.js` 通常不存在。不要按旧路径下结论;需要先列出 `extensions/`,再确认实际存在的 `cursor-agent-exec`、`cursor-agent-worker`、`cursor-always-local` 及其 `dist/` 文件。
大致归属:
- `out/vs/workbench/workbench.desktop.main.js`:主 UI、agent window / titlebar、feature flag、用户可点击入口和只读/禁用态。
- `cursor-always-local/dist/main.js`:本地模式协议、`BidiAppend`、`RunSSE`、`AgentServerMessage` / `AgentClientMessage` 桥接。
- `cursor-agent-exec/dist/main.js` 与同目录数字 chunk:agent 执行侧、SDK/canvas runtime、工具执行、proto 消息定义与拆分 chunk。当前构建中 `411.js` 可能包含 agent 执行链路关键片段,但 chunk 编号不是稳定接口,先用 `dist/*.js` 搜索。
- `cursor-agent-worker/dist/main.js`:agent worker 侧后台逻辑。
用户机器上可能还存在其它 app 副本,例如:
- `~/Applications/Cursor Hooked.app`
- `/Applications/Cursor Patched.app`
不要假设哪一个在跑,先看进程路径。
## 当前 backend/store 与 history
当前用户机器上的固定助手目录:
- `~/.cursor-local-assistant-v2/`
重点看:
- `~/.cursor-local-assistant-v2/config.yaml`
- `~/.cursor-local-assistant-v2/data/ca.crt`
- `~/.cursor-local-assistant-v2/data/ads/`
- `~/.cursor-local-assistant-v2/history/usage.json`
- `~/.cursor-local-assistant-v2/history/<conversationId>/state.json`
- `~/.cursor-local-assistant-v2/history/<conversationId>/context.json`
- `~/.cursor-local-assistant-v2/history/<conversationId>/conversation.lock`
- `~/.cursor-local-assistant-v2/logs/app.log`
其中:
- `state.json` 是会话元数据、loop 状态、latest provider/request prefix、当前 todos/plans、token/compaction 状态。
- `context.json.items` 是 append-only 语义历史,也是 prompt replay 的事实源。
- `usage.json` 是全局 provider call / turn usage 聚合。
- `conversation.lock` 是会话级文件锁。
- checkpoint 只表示同一 backend 进程内的 live state,不是持久化恢复事实源。
- legacy artifacts:`conversation.json`、`entries.jsonl`、`turns/`、`request.json`、`summary.json`、`sse.jsonl`、`replay.json`、`runtime.json`、`latest.json`、数字 turn 目录,当前会被 history maintenance 清理。
这些内容的生成入口主要在:
- `internal/appdata/paths.go`
- `internal/backend/host.go`
- `internal/backend/README.md`
- `internal/backend/forwarder/file_store.go`
- `internal/backend/forwarder/history_maintenance.go`
- `internal/backend/forwarder/usage_store.go`
- `internal/backend/forwarder/token_usage.go`
- `internal/backend/forwarder/artifacts.go`
## 本仓库协议与本地模式实现
协议定义:
- `proto/agent_v1.proto`
- `proto/aiserver_v1.proto`
- `proto/from_extensions/agent_v1.proto`
- `proto/from_extensions/aiserver_v1.proto`
扩展快照与提取:
- `proto/extensions-cursor-app/cursor-always-local/package.json`
- `proto/extract_extensions_proto.sh`
- `proto/ext_tool/main.go`
本地后端入口:
- `internal/backend/host.go`
- `internal/backend/server/route.go`
- `internal/backend/server/policy.go`
- `internal/backend/server/local.go`
- `internal/backend/server/config/types.go`
- `internal/backend/server/config/manager.go`
- `internal/backend/server/config/resolver.go`
forwarder 主链路:
- `internal/backend/forwarder/module.go`
- `internal/backend/forwarder/service.go`
- `internal/backend/forwarder/actor.go`
- `internal/backend/forwarder/broker.go`
- `internal/backend/forwarder/events.go`
- `internal/backend/forwarder/compiler.go`
- `internal/backend/forwarder/projector.go`
- `internal/backend/forwarder/provider.go`
- `internal/backend/forwarder/checkpoint_memory.go`
- `internal/backend/forwarder/runtime_summary.go`
协议解码:
- `internal/backend/agent/protocol/inbound.go`
执行桥 / 交互桥:
- `internal/backend/agent/bridge/exec/bridge.go`
- `internal/backend/agent/bridge/interaction/bridge.go`
模型适配:
- `internal/backend/agent/model/router.go`
- `internal/backend/agent/model/openai.go`
- `internal/backend/agent/model/anthropic.go`
- `internal/backend/agent/model/artifacts.go`
- `internal/backend/agent/model/http_error.go`
- `internal/backend/agent/model/tool_call_id.go`
- `internal/modelchannel/identity.go`
- `internal/runtime/local_runtime.go`
Prompt / replay:
- `internal/backend/agent/prompt/engine.go`
- `internal/backend/agent/prompt/replay.go`
- `internal/backend/agent/prompt/content_parts.go`
- `internal/backend/forwarder/prompt_context.go`
- `internal/backend/forwarder/request_context.go`
- `internal/backend/forwarder/reminders.go`
- `internal/backend/forwarder/prompt_guard.go`
## 构建相关参考(只读)
仓库内已有 macOS 构建与签名相关文件,可用于理解产物结构或历史处理方式,但不要把它们当成修改已安装 Cursor 客户端的操作指南:
- `Taskfile.yml`
- `build/darwin/Taskfile.yml`
- `build/dmg-extras/提示损坏?点我.command`
重点看:
- `build/darwin/Taskfile.yml` 中的 `codesign:adhoc`
- `build/dmg-extras/提示损坏?点我.command` 中的 `xattr -cr`
@@ -0,0 +1,87 @@
# 已安装客户端只读核对与验证
首要原则:
- 不要修改已安装的 Cursor 客户端代码、bundle、签名或 app 副本。
- 允许且推荐读取、搜索、比对和分析客户端 bundle、日志、端口和 history 状态。
- 目标是定位差异、收集证据、判断问题归属,而不是 patch 客户端。
## 1. 先确认实际运行的 app 副本
优先用非交互命令核对:
```bash
pgrep -fal 'Cursor Hooked|Cursor Patched|/Contents/MacOS/Cursor'
ps -axo pid,ppid,command | rg 'Cursor(.app)?/Contents/MacOS/Cursor|extension-host'
```
不要在没确认实际运行副本前就下结论,也不要修改客户端文件。
## 2. 只读定位目标 bundle 与关键文件
优先定位并读取这些文件,而不是改写它们:
```bash
ls -l "/absolute/path/Target.app/Contents/Resources/app/extensions"
shasum -a 256 "/absolute/path/Target.app/Contents/Resources/app/extensions/cursor-always-local/dist/main.js"
```
重点关注:
- `out/vs/workbench/workbench.desktop.main.js`
- `out/vs/workbench/api/node/extensionHostProcess.js`
- `cursor-always-local/dist/main.js`
- `cursor-always-local/dist/gitWorker.js`
- `cursor-agent-exec/dist/main.js`
- `cursor-agent-exec/dist/*.js`
- `cursor-agent-worker/dist/main.js`
当前安装包里旧路径 `cursor-agent/dist/main.js` 通常不存在;先确认 `extensions/` 里的实际扩展名和 `dist/` 文件,再选择 `workbench` / `cursor-agent-exec` / `cursor-agent-worker` / `cursor-always-local` 对应排查。
## 3. 只读读取 bundle 内容
常用定位关键词:
```bash
rg -n 'BidiTransport|ExecClientMessage|InteractionResponse|conversation_checkpoint_update' "/absolute/path/Target.app/Contents/Resources/app/extensions/cursor-always-local/dist/main.js"
rg -n 'CursorAgentProvider|AnthropicProxy|ANTHROPIC_BASE_URL|InteractionUpdate|checkpoint|agent window' "/absolute/path/Target.app/Contents/Resources/app/extensions/cursor-agent-exec/dist/main.js" "/absolute/path/Target.app/Contents/Resources/app/extensions/cursor-agent-exec/dist"/*.js "/absolute/path/Target.app/Contents/Resources/app/extensions/cursor-agent-worker/dist/main.js"
rg -n 'agent window|open_agent_window|NameAgent|UpdateConversationMetadata|shouldShowAgentWindowTitleHelperText' "/absolute/path/Target.app/Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js" "/absolute/path/Target.app/Contents/Resources/app/extensions/cursor-agent-exec/dist"/*.js
```
读取具体文件内容时,优先用读取工具按需查看相关片段,不要修改 bundle。
如果需要和仓库实现对照,优先同时打开:
- `proto/agent_v1.proto`
- `proto/aiserver_v1.proto`
- `internal/backend/...`
- `internal/runtime/local_runtime.go`
## 4. 验证行为是否命中目标副本
至少做其中两项:
- 进程路径是否是目标 app
- 目标扩展 host 是否起来
- 本地监听端口是否存在
- `~/.cursor-local-assistant-v2/logs/app.log` 是否更新
- `~/.cursor-local-assistant-v2/history/<conversationId>/state.json` / `context.json` 是否更新
- 请求/协议事件是否真的经过你正在分析的 bundle 文件
常用验证:
```bash
pgrep -fal '/absolute/path/Target.app/Contents/MacOS/Cursor'
lsof -nP -iTCP -sTCP:LISTEN | rg 'Cursor|127.0.0.1'
```
## 5. 记录证据并输出归因
如果确认“已安装 app 行为”和“仓库代码理解”存在差异,优先记录:
1. 实际运行的 app 路径
2. 命中的 bundle 文件路径与关键符号
3. 对应日志、端口、`history/state.json`、`history/context.json`、`usage.json` 证据
4. 仓库里对应实现的位置
如果结论指向客户端侧,也停留在分析和归因,不要继续 patch、重签名、替换文件或做写入式验证。
@@ -0,0 +1,153 @@
# Provider replay / debug
当现象是 provider 返回错误、SSE 里只有 `event: error`、需要确认最终出站 provider body 是否能被独立复现时,优先读这份参考。
这套流程只用于还原“后端最终发给 provider 的请求形状”和“provider 对该请求的真实响应”。它不是语义 history,也不是客户端输入事实源。
## 证据边界
- `history/<conversationId>/debug/provider.jsonl`
- 最接近 provider 出站边界。
- `event=llm_request` 的 `payload.body` 是最终 provider request body。
- 用它做 curl replay,判断问题是否已经出现在出站请求形状。
- `history/<conversationId>/state.json`
- 当前状态和最近 provider 摘要。
- 重点看 `latest_request_prefix`、`last_provider_call`。
- `history/<conversationId>/context.json`
- replayable 语义历史。
- 用来解释为什么会形成这次 prompt,不用来直接重放 provider HTTP 请求。
- `history/<conversationId>/debug/bidi.raw.jsonl`
- 客户端原始上行字节证据。
- `history/<conversationId>/debug/bidi.decoded.jsonl`
- 当前 known-schema 解码后的客户端上行证据。
- `history/<conversationId>/debug/runtime.jsonl`
- 后端把哪些字段挂到 active request / stream 上。
- `history/<conversationId>/debug/runsse.jsonl`
- 后端尝试发回客户端的消息。
不要把这些证据混用:provider replay 只能证明最终 provider HTTP 请求与响应,不能单独证明客户端原始上传了什么,也不能替代 `context.json` 的语义历史。
## 前置条件
- 请求发生时 `config.yaml` 中 `log: true`,或已有 `history/<conversationId>/debug/provider.jsonl`。
- 已经通过 id 反查拿到:
- `conversationId`
- `requestId`
- `modelCallId`
- 已经确认要测的是 Anthropic-compatible `/v1/messages` 请求。
如果没有 debug 文件,先回到 `state.json`、`context.json`、`usage.json`、`logs/app.log` 做推断,并明确“没有直接 provider body 证据”。
## 最小流程
1. 定位 provider debug 文件:
```bash
ROOT="$HOME/.cursor-local-assistant-v2"
CONV="<conversationId>"
REQ="<requestId>"
MODEL_CALL="<modelCallId>"
REQUEST_LOG="$ROOT/history/$CONV/debug/provider.jsonl"
```
2. 确认 `llm_request` 存在:
```bash
jq -c --arg req "$REQ" --arg mc "$MODEL_CALL" '
select(.event == "llm_request" and .request_id == $req and .model_call_id == $mc)
| {at, conversation_id, request_id, model_call_id, provider: .payload.provider, model: .payload.body.model}
' "$REQUEST_LOG"
```
3. 执行通用重放脚本:
```bash
REQUEST_LOG="$REQUEST_LOG" \
REQUEST_ID="$REQ" \
MODEL_CALL_ID="$MODEL_CALL" \
CHANNEL_NAME="GLM" \
OUT_DIR="/tmp/cursor-provider-replay-$REQ" \
.agents/skills/cursor-client-e2e-debugging/scripts/provider-replay.sh
```
也可以直接传入 provider 配置,避免读取 `config.yaml`:
```bash
REQUEST_LOG="$REQUEST_LOG" \
REQUEST_ID="$REQ" \
MODEL_CALL_ID="$MODEL_CALL" \
BASE_URL="<provider-base-url>" \
API_KEY="<provider-api-key>" \
OUT_DIR="/tmp/cursor-provider-replay-$REQ" \
.agents/skills/cursor-client-e2e-debugging/scripts/provider-replay.sh
```
4. 查看产物:
- `request.body.json`:抽取出的最终 provider body。
- `response.headers`:HTTP 响应头。
- `response.sse`:SSE 响应体。
- `replay.meta.json`:本次重放引用的 id 和 provider log 路径。
## 结果判断
- `curl_exit_code != 0`
- 网络、TLS、超时、连接或本机 curl 问题。
- 先看 stderr、`response.headers` 是否存在,再判断是否真的到达 provider。
- HTTP 非 2xx
- provider 网关或鉴权层拒绝。
- 优先看 `response.headers` 和 provider 错误体。
- HTTP 2xx 但 SSE 中有 `event: error`
- provider 已接受连接,但认为请求参数不合法或模型侧拒绝。
- 这种情况下重点比对 `request.body.json` 的消息结构、tool schema、thinking/reasoning 参数、model 名称和 endpoint 兼容性。
- SSE 正常流式输出
- 原始 provider 请求形状基本可用。
- 如果客户端仍失败,回到 `runsse.jsonl`、forwarder 状态机或客户端协议层继续查。
## 常见收敛方向
provider 参数错误时,优先检查:
- `model` 是否是目标 endpoint 支持的名称。
- `messages` 是否符合 Anthropic-compatible 形态。
- `system` 是否被目标 provider 支持,或需要改成 message。
- `tools` / `tool_choice` 是否符合目标 provider 方言。
- `thinking` / `reasoning` 字段是否被目标 provider 支持。
- 图片、文件、cache_control、metadata 等扩展字段是否超出 provider 兼容范围。
- `max_tokens`、`temperature`、`top_p`、`stop_sequences` 是否落在 provider 允许范围内。
## 敏感信息规则
- 不把 API key 写进 skill、reference、脚本默认值或提交内容。
- 回复用户时不要粘贴完整 API key;最多说明“已使用用户提供的 key / config 中的 key”。
- 不把完整 `request.body.json` 大段贴给用户;只摘和结论相关的字段形状。
- 不把一次性 `conversationId/requestId/modelCallId` 写进 skill 文档。
- 临时 replay 产物默认放 `/tmp`;如果需要保留,明确说明路径和原因。
## 脚本参数
`scripts/provider-replay.sh` 使用环境变量控制:
- 必填:
- `REQUEST_LOG`
- `REQUEST_ID`
- `MODEL_CALL_ID`
- 可选:
- `BASE_URL`
- `API_KEY`
- `GLM_BASE_URL`
- `GLM_API_KEY`
- `ANTHROPIC_BASE_URL`
- `ANTHROPIC_API_KEY`
- `CONFIG_FILE`,默认 `~/.cursor-local-assistant-v2/config.yaml`
- `CHANNEL_NAME`,默认 `GLM`
- `OUT_DIR`,默认 `/tmp/cursor-provider-replay-<requestId>`
- `MAX_TIME`,默认 `240`
- `ENDPOINT_PATH`,默认 `/v1/messages`
脚本输出四个稳定产物:
- `request.body.json`
- `response.headers`
- `response.sse`
- `replay.meta.json`
@@ -0,0 +1,223 @@
# 搜索词与判断树
## 先判断层级
### `history + logs` / id 反查层
当现象是“用户只给了一个 id”“需要判断它是 `conversationId`、`requestId`、`modelCallId`、`toolCallId`”“要从本地 history 和日志追状态”:
优先搜索:
- `state.json`
- `context.json`
- `usage.json`
- `logs/app.log`
- `conversation_id`
- `current_request_id`
- `request_id`
- `model_call_id`
- `tool_call_id`
- `latest_request_prefix`
- `last_provider_call`
- `current_loop_status`
- `context_version`
- `next_entry_seq`
- `next_turn_seq`
- `LoadConversation`
- `CreateConversation`
- `SaveConversationWithEntries`
- `AppendEntries`
- `UpdateConversationMeta`
- `ReplaceEntries`
- `ProjectPromptReplay`
- `UsageFileStore`
- `UpsertEvent`
- `LookupEvent`
不要再优先搜索或依赖:
- `data.sqlite`
- `protocol_traces`
- `agent_request_runs`
- `conversation.json`
- `entries.jsonl`
- `turns/<n>`
- `request.json`
- `sse.jsonl`
- `summary.json`
这些是旧实现或 legacy artifact 相关线索,只在排查迁移/清理逻辑时作为历史背景。
### `cursor-agent-exec` / `cursor-agent-worker` 层
当现象涉及 agent 主循环、模型桥接、`InteractionUpdate` 映射、工具 started/completed、session/provider 状态:
优先在已安装客户端 split bundle 中搜索:
- `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-agent-exec/dist/main.js`
- `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-agent-exec/dist/*.js`
- `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-agent-worker/dist/main.js`
优先搜索:
- `registerAgentProvider`
- `CursorAgentProvider`
- `CursorAgentProviderHandle`
- `ClaudeSDKClient`
- `streamInteractionUpdates`
- `handlePartialMessage`
- `AnthropicProxy`
- `getAnthropicProxyPort`
- `getAnthropicProxyAuthToken`
- `ANTHROPIC_BASE_URL`
- `ANTHROPIC_API_KEY`
- `InteractionUpdate`
- `checkpoint`
旧路径 `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-agent/dist/main.js` 可能不存在。先确认实际 `extensions/` 结构,再按 `cursor-agent-exec` / `cursor-agent-worker` / `cursor-always-local` 分层排查。
### agent window / conversation metadata UI 层
当现象涉及 agent window 标题、窗口信息、titlebar 按钮、是否可点击修改、会话名/metadata 更新:
优先在已安装客户端主 UI 和 split bundle 中搜索:
- `/Applications/Cursor.app/Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js`
- `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-agent-exec/dist/main.js`
- `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-agent-exec/dist/*.js`
- `/Applications/Cursor.app/Contents/Resources/app/extensions/cursor-always-local/dist/main.js`
优先搜索:
- `shouldShowAgentWindowTitleHelperText`
- `glass_open_agents_titlebar_button`
- `open_agent_window_top`
- `open_agent_window_bottom_convo`
- `glass.enable_open_agent_in_window`
- `NameAgentRequest`
- `NameAgentResponse`
- `UpdateConversationMetadataRequest`
- `UpdateConversationMetadataResponse`
- `CreateTranscriptOverviewRequest`
- `createTranscriptOverview`
- `updateConversationMetadata`
- `conversation_checkpoint_update`
判断规则:
- `ConversationStateStructure` / `conversation_checkpoint_update` 是 UI 同步快照,不应作为持久化修改入口。
- 如果客户端调用 `UpdateConversationMetadata` / `NameAgent`,要继续确认本地后端是否显式注册对应 `/agent.v1.AgentService/*` 路由;不能只看 proto message 存在。
- 本地模式修改会话名/metadata 时,应落到 `history/<conversationId>/state.json` 或等价持久化会话元数据,再 publish checkpoint 同步 UI。
### `cursor-always-local` / 协议层
当现象涉及本地模式、客户端没有回包、pending 不收口、同一 backend 进程内的 live checkpoint 重连错乱:
优先搜索:
- `BidiTransport`
- `startYieldingInputsToTheServer`
- `BidiAppend`
- `RunSSE`
- `AgentServerMessage`
- `AgentClientMessage`
- `ExecServerMessage`
- `ExecClientMessage`
- `ExecClientControlMessage`
- `InteractionQuery`
- `InteractionResponse`
- `conversation_checkpoint_update`
### 本仓库 forwarder 层
当现象涉及本地后端收发、provider 继续/暂停、exec/interaction 桥接、history 投影:
优先搜索:
- `handleRunIntent`
- `driveProvider`
- `startStreamActor`
- `streamCommandEnvelope`
- `handleToolInvocation`
- `handleExecResult`
- `handleExecControl`
- `publishCheckpoint`
- `CheckpointConversation`
- `snapshotCheckpointConversation`
- `appendConversationEntries`
- `OpenExec`
- `OpenQuery`
- `StartStream`
- `deriveConversationLoopState`
- `historyEntryToolCallID`
- `recordProviderUsage`
- `recordTurnUsage`
### provider / 模型适配层
当现象是 provider 400/500、thinking/reasoning、tool_call_id、OpenAI/Anthropic 请求形状、usage/cache 不对:
优先搜索:
- `StartStream`
- `StreamRequest`
- `ResolvedChannelID`
- `ResolvedChannelName`
- `ProviderModelID`
- `ThinkingEnabled`
- `buildAnthropicThinkingConfig`
- `normalizeAnthropicProviderMessages`
- `normalizeOpenAIProviderMessages`
- `normalizeOpenAIResponsesInput`
- `reasoning_content`
- `ReasoningContent`
- `ReasoningSignature`
- `RecordLLMRequest`
- `RecordLLMSummary`
- `http_error`
- `namespaceToolCallID`
## 快速判断规则
- 如果问题是“给你一个 id,让你先判断是什么 ID,再找日志”,先看 `history/<id>/state.json` 是否存在,再扫 `history/*/state.json`、`history/*/context.json` 和 `logs/app.log`。
- 如果问题是“模型输出语义不对”,先看 `context.json.items` 到 `ProjectPromptReplay()` 的投影,再看 provider request normalization。
- 如果问题是“provider 报 400/参数错误”,先看模型适配层请求构造、`state.latest_request_prefix`、`state.last_provider_call`、`logs/app.log`。
- 如果问题是“客户端没回某个工具结果 / pending 不收口”,先看 `cursor-always-local` 与 forwarder,同时核对同一 `turn_seq` 是否有 `tool_result` 或控制面错误 entry。
- 如果问题是“backend 重启后为什么 checkpoint 没法继续恢复 pending”,不要找磁盘 checkpoint;checkpoint 是 live state,重启后的事实源是 `state.json + context.json`。
- 如果问题是“为什么同一个 `modelID` 还能出现多个渠道”,先检查渠道 ID:规范化后 `baseURL + modelID + apiKey + displayName + openAIEndpoint` 的短 SHA-256;resolver 仍兼容 legacy `baseURL + modelID + apiKey + displayName`。
- 如果问题是“只想桥接到其他 LLM”,优先看模型桥接层,不要默认深入整套 local runtime。
- 如果问题是“已安装 app 行为和仓库代码不一致”,先核对实际运行 bundle,再做只读比对;不要 patch 客户端。
## 协议关键词
上行:
- `run_request`
- `exec_client_message`
- `exec_client_control_message`
- `interaction_response`
下行:
- `interaction_update`
- `exec_server_message`
- `exec_server_control_message`
- `interaction_query`
- `conversation_checkpoint_update`
如果只看到下行请求,没有对应上行结果或控制消息,优先排查:
- `exec_id`
- `id`
- `tool_call_id`
- `request_id`
- `model_call_id`
- pending 收口逻辑
如果用户给的是一个裸 id,不要直接把它当成 `request_id`。先同时查:
- `history/<id>/state.json`
- `history/*/state.json` 的 `current_request_id`、`latest_request_prefix`、`last_provider_call`
- `history/*/context.json` 的 `items[].request_id`、`items[].tool_call_id`、`items[].payload`
- `history/usage.json` 的 `event_index` / `recent_events`
- `logs/app.log`