mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-06 21:52:51 +08:00
v0.3.8
This commit is contained in:
@@ -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`
|
||||
+87
@@ -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`
|
||||
Reference in New Issue
Block a user