Conversation
Signed-off-by: lishun <2976909322@qq.com>
Signed-off-by: lishun <2976909322@qq.com>
|
Thank you so much for your attention and contribution! We will arrange an internal review for this PR shortly, and all feedback will be shared right here in the discussion. |
yangjj-iso
left a comment
There was a problem hiding this comment.
整体实现和测试都很扎实,改 3 处即可合并:
memory_status 直接 fetch() 没有超时——Gateway 挂起时工具调用会永久阻塞,请复用 gatewayRequest 的 abort 超时。
多个 Composer 窗口并发写 state.json 存在竞态(后写覆盖先写会丢 pending/captured 标记),建议按会话分片或加锁。
/session/end 没传 session_id,请确认 Gateway 契约,或补传 conversation_id。
小问题:未知方法应返回 -32601;README 建议注明 user_email 会上送远程 Gateway(PII)。修完 1–3 即可合并。
Signed-off-by: lishun <2976909322@qq.com>
|
Thanks for the review. I have addressed all three required items and the two minor issues:
I also added regression tests for concurrent state updates and hanging Gateway requests. All 13 tests pass. |
|
Hi @li6667 , thank you so much for this contribution! 🙏 The Hooks + MCP sidecar approach you've implemented is genuinely impressive — the fail-open design, idempotent capture with generation_id, cross-process directory locking, and the Windows GBK mojibake recovery all demonstrate deep, hands-on experience with Cursor's runtime. The architectural analysis (especially the Free vs. Pro distinction and the header capability questions in #1139) is also extremely valuable context for the team. Our preferred direction: Proxy-based routing After careful discussion, we'd like to align on the proxy-based adaptation path for Cursor, consistent with how we've already integrated Claude Code, CodeBuddy, Codex, WorkBuddy, and DeepSeek Harness. Why proxy over sidecar:
Network constraint we accept: Since Cursor's requests route through Cursor's backend before reaching the custom endpoint, the proxy must be deployed on a publicly accessible IP. This is a known trade-off specific to Cursor. What the proxy-based Cursor adapter needs We have an internal integration SOP that has been battle-tested across 5 clients. Below is the full checklist — we'd appreciate it if you could follow these steps to ensure nothing is missed: Phase 0: Protocol Reconnaissance (capture 3–5 real requests) Before writing any code, capture Cursor's actual request traffic (mitmproxy or similar) and document 5 key differences: ┌──────────────────┬──────────────────────────────────────────────────────────┐ Store the raw fixtures under MemoryProxy/docs/cursor-recon/. Phase 1: Proxy code changes (20-step checklist) Skeleton (4 steps):
Identity & whitelist (3 steps): Session-init form (4 steps): Metadata filtering & wire compatibility (3 steps): Handler short-circuits (2 steps): Tests (4 steps): Phase 2: Completion criteria All of the following must pass:
Reference implementation The DeepSeek Harness (dsh) adapter is our most complete and well-documented integration (10 pitfalls encountered and resolved, 39 unit tests). We strongly recommend using it as a template — the diff touches ~14 files and the patterns translate directly to Cursor since both use the OpenAI Chat Completions protocol. What we need from you
We realize this is a significantly larger scope than the current sidecar approach, and we're happy to provide guidance at any step — architecture questions, adapter patterns, test fixtures, or anything else. Your understanding of Cursor's behavior puts you in a great position to drive this forward. Thank you again for championing the Cursor integration — looking forward to seeing it on the proxy path! 🚀 |
Cursor Proxy E2E recordingsCompressed recordings from the real Cursor + MemoryProxy acceptance run:
All recordings are H.264 MP4 and each is below GitHub's 10 MB upload limit. 01-session-init.mp402-assets-memory-recall.mp403-mem-commands.mp4 |
|
Boundary follow-up is now pushed in
Verification: The real Cursor E2E evidence and recordings remain valid. |
…rsor-adapter-server # Conflicts: # MemoryProxy/src/agent-adapters/index.ts # MemoryProxy/src/agent-adapters/types.ts # MemoryProxy/src/handler.ts # MemoryProxy/src/server.ts # MemoryProxy/src/session/codebuddy/cleaner.ts # MemoryProxy/src/session/codebuddy/init.ts # MemoryProxy/src/session/index.ts
L2ncE
left a comment
There was a problem hiding this comment.
cursor headless users running mem:session-reset get the dsh-specific copy ("dsh 客户端…ask_user_question preset…请在带 ask_user_question preset 的 dsh 环境下使用"). The gate now covers cursor too, so the message needs parameterization per agentSource.
There was a problem hiding this comment.
cursor headless users running mem:session-reset get the dsh-specific copy ("dsh 客户端…ask_user_question preset…请在带 ask_user_question preset 的 dsh 环境下使用"). The gate now covers cursor too, so the message needs parameterization per agentSource.
There was a problem hiding this comment.
Thanks for catching this. Fixed in 2536fb8.
The headless mem:session-reset guidance is now parameterized by agentSource in MemoryProxy/src/session/openai-headless.ts:
- Cursor uses its native AskQuestion tool name.
- DSH continues to use ask_user_question.
- Header-only agents retain their existing guidance.
Regression coverage was added in src/session/tests/openai-headless.test.ts
| * 见 MemoryProxy/docs/dsh-recon/2026-08-14-dsh-capture-analysis.md §2.3。 | ||
| */ | ||
| function isFreshCBConversation(messages: MessageArr): boolean { | ||
| function isFreshCBConversation(messages: MessageArr, agentSource: string): boolean { |
There was a problem hiding this comment.
Good catch — yes, this method was no longer referenced by the current session-init flow. I removed isFreshCBConversation() and its now-unused import in 2536fb8.
| // cannot execute that native UI tool. | ||
| const _dshHeadless = (agentSource === "dsh" || agentSource === "cursor") && (() => { | ||
| const tools = (body as { tools?: unknown }).tools; | ||
| if (!Array.isArray(tools) || tools.length === 0) return false; |
There was a problem hiding this comment.
看起来这个是给 dsh 用的,tools 为空不会弹表单,cursor 确认也能这么处理吗
There was a problem hiding this comment.
Thanks for raising this boundary case. Confirmed and fixed in 2536fb8.
Cursor requests are now treated as headless when tools is missing, empty, or does not contain the native AskQuestion tool. These requests bypass session initialization and pass through directly.
DSH’s existing empty-tools behavior is preserved to avoid changing its established flow.
Verification:
- Added unit coverage for both Cursor and DSH boundaries.
- Real Cursor Agent smoke test with AskQuestion: full asset → team → agent → task flow passed.
- Public-route smoke request with tools: []: openAIHeadless=true, session-init bypassed, and the upstream response returned successfully.
- Full suite: 8 test files / 27 tests passed.
|
Thanks @L2ncE for the review. All three comments have been addressed in 2536fb8.
|
|
Thanks for the review! |
Thanks a lot for the contribution! The proxy-side adaptation looks solid and covers most of the integration surface pretty well. 🙏 That said, while reviewing this, we realized there is still a fundamental challenge in the Cursor scenario: when Cursor forwards requests to the proxy via Override Base URL, there doesn't seem to be any stable This is important for our side because skill extraction, memory archival, state management, asset injection, telemetry, etc. all rely on We noticed the PR currently uses One possible direction we were thinking about is using the This is just an initial idea from our side, and we haven't fully validated whether it works well enough in practice or whether the engineering complexity is reasonable. There may also be better or simpler approaches that we're missing. So we'd suggest treating this as a direction worth prototyping rather than a concrete solution. We'd be very interested in your thoughts on whether this approach makes sense, or if you have other ideas for reliably identifying sessions in the Cursor scenario. Thanks again for the work! Cursor integration is really valuable for the whole ecosystem. 🚀 |
|
I am planning to use a conversation-matching approach inspired by the design of Agent Memory. The key observation is that, even after Cursor performs context compaction, it normally retains several of the most recent conversation turns. Older messages may be removed, but the latest user/assistant messages generally remain available in subsequent requests. Based on this behavior, the proxy can identify a Cursor conversation from a small sliding window of recent stable messages instead of relying on a single first message or an unavailable Proposed designFor each Cursor request without an explicit session header:
After every request, the matcher updates the stored recent fingerprints with the newest window. This allows the matching anchor to move forward as the conversation progresses. Why this can survive compactionThe matcher does not depend on the beginning of the conversation. It continuously tracks recent turns. Therefore, when Cursor removes older context, the remaining recent messages can still match the previous session. For example: The recent messages If Cursor removes all previously observed anchors, or if the proxy process restarts and loses its in-memory index, the matcher will conservatively create a new session and allow session initialization again. This may lose continuity in rare cases, but it avoids incorrectly mixing two unrelated conversations. Scope and trade-offsThis design is intentionally limited to Cursor requests that do not provide an explicit conversation header. Existing session headers remain higher priority, and the existing session-init, injection, model routing, and other client adapters remain unchanged. The implementation uses a bounded in-memory index with TTL and a maximum number of tracked sessions. It does not require Redis or a new persistent database. The trade-off is a small amount of additional memory and O(n) matching work per request, which should be acceptable because the number of active conversations for one user is normally limited and SHA-256 calculation is inexpensive. The goal is not to guarantee perfect recovery in every pathological case. The goal is to make normal Cursor conversations— including multi-turn conversations, tool-heavy turns, and most compaction scenarios—continue without the user seeing another session-initialization form, while failing safely when the conversation cannot be identified with sufficient confidence. |

Cursor Proxy Adapter
Summary
This revision changes #1138 from the original Cursor Hooks + MCP sidecar experiment to the maintainer-requested proxy-based integration. Cursor now shares MemoryProxy's session initialization, system-prompt injection, Skill / Memory / Knowledge assets, conversation archiving, mem commands, cost-guard routing, and Langfuse observability path with the existing clients.
The superseded sidecar implementation has been removed from the proposed upstream diff. Its design remains available in the PR discussion history.
Protocol reconnaissance
Five sanitized real Cursor requests are included under
MemoryProxy/docs/cursor-recon/proxy-inbound/.Confirmed findings:
messages[],tools[], streaming, andstream_options.include_usage.userplus the first content-block user message.AskQuestionwithquestions[].{id,prompt,options,allow_multiple}andoptions[].{id,label}.Other...; pagination is not required for the current four-stage form.reasoning_content; the adapter repairs the missing field without overwriting real reasoning content.The Free IDE-side Connect/Protobuf capture is documented as non-authoritative for Proxy ingress.
Implementation
asset_confirm -> team -> agent -> task, with Cursor branches gated byagentSource === "cursor".AskQuestiontool calls.reasoning_contenton Cursor assistant tool-call replay.Real E2E result
Tested with a real Cursor client through a publicly reachable HTTPS Cloudflare Quick Tunnel and the full MemoryProxy / MemoryCore stack:
mem:help,mem:sync, andmem:create-skillwere intercepted.tdai-recorder:write-l0.[skill-conversation-add] archived; the worker completed successfully.agent_source:cursor.The real run did not emit title-generation or compaction traffic to the custom endpoint. Unknown non-chat shapes fail open without form, injection, or archiving; no live auxiliary fixture is claimed.
Tests
npm test:6 test files passed, 15 tests passedBoundary regressions verify that non-Cursor clients retain their historical mem parsing and injected tool guidance.
npm run typecheckstill reports six pre-existing errors in Anthropic/Codex/config/storage files; no remaining error points to a Cursor-added file or Cursor-specific changed line. Details are inMemoryProxy/docs/cursor-recon/notes/e2e-acceptance.md.Evidence
agent_source:cursor.MemoryProxy/docs/cursor-recon/notes/e2e-acceptance.md.Deployment note
Cloudflare Quick Tunnel was used only for temporary acceptance. Production use requires a stable publicly reachable HTTPS endpoint.
Related Issue | 关联 Issue
Closes #926
Change Type | 修改类型
Self-test Checklist | 自测清单