diff --git a/.gitignore b/.gitignore index 4f6a5c7..24d6f58 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .idea/ -cursor-api-proxy* -.env \ No newline at end of file +bin/ +.env diff --git a/DIAGNOSTIC_RESULTS.md b/DIAGNOSTIC_RESULTS.md deleted file mode 100644 index 0200092..0000000 --- a/DIAGNOSTIC_RESULTS.md +++ /dev/null @@ -1,82 +0,0 @@ -# cursor-adapter SSE Diagnostic Results - -> **Status: RESOLVED 2026-04-18.** This file is kept for history. The bugs -> listed below have been fixed. Current behavior is captured by regression -> tests in `internal/server/messages_test.go` and `internal/converter/convert_test.go`. - ---- - -## Originally reported (2026-04-15) - -When used as an OpenAI-compatible endpoint for SDKs like -`@ai-sdk/openai-compatible` (OpenCode), cursor-adapter had five issues: - -1. **Non-streaming is completely broken** — server hung for 30s and never - wrote a response body. -2. **SSE content was double-JSON-encoded** — each `delta.content` field - held the *entire* Cursor CLI JSON line (including `type:"system"`, - `type:"user"`, `type:"result"`) serialized as a string, instead of plain - assistant text. -3. **Missing `role` in first delta.** -4. **Missing `finish_reason` in final chunk.** -5. **Usage not at the top level** — embedded inside a stringified JSON - payload instead of `chunk.usage`. - ---- - -## Root cause - -Two separate bugs plus one latent one, all landed together: - -- **Non-stream hang / `exit status 1`:** the chat-only isolation ported from - `cursor-api-proxy` was overriding `HOME` → temp dir. On macOS with - keychain login, the `agent` CLI resolves its session token via - `~/.cursor/` + the real keychain, so a fake `HOME` made `agent` exit - immediately with "Authentication required. Please run 'agent login'". - The adapter surfaced this as either a hang (when timeouts swallowed the - exit) or as `exit status 1` once the error bubbled up. -- **Content wrapping / leaked system-user chunks:** older pre-parser code - forwarded raw Cursor JSON lines as `delta.content`. The parser had - already been rewritten by the time this diagnostic was taken, but the - report caught an earlier build. -- **Duplicate final delta (discovered during this pass):** the stream - parser's accumulator was *reassigned* (`p.accumulated = content`) even - when the new fragment did not start with the accumulated prefix. With - Cursor CLI's incremental output mode (one fragment per message), that - meant the "you said the full text" final message looked different from - accumulated and was emitted as a second copy of the whole response. - ---- - -## Fix summary - -- `internal/workspace/workspace.go` — only override `CURSOR_CONFIG_DIR` by - default. `HOME`/`XDG_CONFIG_HOME`/`APPDATA` are only isolated when - `CURSOR_API_KEY` is set (which bypasses keychain auth anyway). -- `internal/converter/convert.go` — stream parser now handles both - cumulative and incremental Cursor output modes. In the non-prefix - branch it appends to accumulated instead of replacing it, so the final - duplicate is correctly detected via `content == accumulated` and - skipped. -- `internal/server/handlers.go` + `anthropic_handlers.go` — already emit - `role:"assistant"` in the first delta, `finish_reason:"stop"` in the - final chunk, and `usage` at the top level. Regression tests added to - `messages_test.go` lock this in. - -## Verified manually - -``` -$ curl -sN http://localhost:8765/v1/chat/completions \ - -H 'Content-Type: application/json' \ - -d '{"model":"auto","stream":true,"messages":[{"role":"user","content":"count 1 to 5"}]}' - -data: {"id":"chatcmpl-…","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]} -data: {"id":"chatcmpl-…","choices":[{"index":0,"delta":{"content":"\n1、2、3、4、5。"},"finish_reason":null}]} -data: {"id":"chatcmpl-…","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":6,"completion_tokens":5,"total_tokens":11}} -data: [DONE] -``` - -Non-streaming returns `chat.completion` JSON with `stop_reason:"stop"` and -`usage` populated. Anthropic `/v1/messages` emits `message_start` → -`content_block_delta*` → `message_stop` without duplicating the final -cumulative fragment. diff --git a/bin/cursor-adapter b/bin/cursor-adapter deleted file mode 100755 index 9100a7a..0000000 Binary files a/bin/cursor-adapter and /dev/null differ