gateway: Cursor Private Inference asking with another key of magpie's is shown its own model list (mamba on Discord) — after v0.1.980 its Reasoning control was still missing and models taken out on its row still listed: a key typed in its Open configuration or a model's settings ("magpie") overrides magpie-cursor-local, and its User-Agent (Cursor-CLI/…, cursor-agent-exec's default) made it the cursor agent, which is shown the plain list. cursor-agent talks to Cursor's backend only, so a Cursor User-Agent at the gateway is now taken as Cursor Private Inference's: its /models carries api_types and capabilities and follows its row's model picks, and its use is counted under it. Its notice now says to leave both the base URL and the key empty (or use magpie-cursor-local) and to quit the app, not only its window, as it keeps the model list it was first given. TestCursorLocalModels fails without the change. go vet, go build (darwin, GOOS=linux nogui, GOOS=windows) and go test -tags nogui ./... under a temp HOME pass.

This commit is contained in:
yetone committed 2026-10-05 16:29:51 +08:00
1 parent 602d4ec6cc
commit 387afd0779
3 files changed
+28 -3

No files matched your search

+1 -1
View File
@@ -267,7 +267,7 @@ func cursorLocal() *Agent {
},
}},
Notice: func() string {
return cursorLocalName + " reads magpie's gateway from CURSOR_LOCAL_AGENT_BASE_URL and CURSOR_LOCAL_AGENT_API_KEY, now set for your user: quit it and open it again. A base URL set in its Open configuration comes first, so leave that empty (or set it to " + gateway.URL() + "/v1 with the key " + gateway.TokenFor(CursorLocalID) + ")."
return cursorLocalName + " reads magpie's gateway from CURSOR_LOCAL_AGENT_BASE_URL and CURSOR_LOCAL_AGENT_API_KEY, now set for your user: quit it (the app, not only its window) and open it again, as it keeps the model list it was first given until it quits. A base URL or API key set in its Open configuration or a model's settings comes first, so leave both empty (or set the base URL to " + gateway.URL() + "/v1 and the key to " + gateway.TokenFor(CursorLocalID) + ")."
},
}
}
+17 -1
View File
@@ -29,10 +29,13 @@ func TestCursorLocalModels(t *testing.T) {
if err := catalog.SaveLive("or", "http://127.0.0.1:2/v1", []catalog.Model{{ID: "claude-opus-4-7", Efforts: []string{"low", "medium", "high"}}}); err != nil {
t.Fatal(err)
}
list := func(key string) map[string]map[string]any {
list := func(key string, ua ...string) map[string]map[string]any {
t.Helper()
req := httptest.NewRequest("GET", "/v1/models", nil)
req.Header.Set("Authorization", "Bearer "+key)
for _, v := range ua {
req.Header.Set("User-Agent", v)
}
rec := httptest.NewRecorder()
New().Handler().ServeHTTP(rec, req)
var out struct {
@@ -115,4 +118,17 @@ func TestCursorLocalModels(t *testing.T) {
if got := list(TokenFor("opencode")); got["ds/flash"] == nil {
t.Errorf("taken out of another agent's list: %v", got)
}
// asked with another key of magpie's, typed in its Open configuration,
// it still says who it is in its User-Agent and is shown its own list,
// with the fields its Reasoning control needs (mamba on Discord)
for _, ua := range []string{"Cursor-CLI/unknown", "Cursor/3.23.12"} {
got := list(Token, ua)
if got["ds/flash"] != nil || got["fake/m1"] == nil {
t.Errorf("%s with key %s: %v", ua, Token, got)
}
if c, _ := got["or/claude-opus-4-7"]["capabilities"].(map[string]any); c["supports_reasoning"] != true {
t.Errorf("%s with key %s: capabilities = %v", ua, Token, c)
}
}
}
+10 -1
View File
@@ -58,7 +58,16 @@ func agentOf(r *http.Request) string {
if isClaudeDesktop(r) {
return "claude-desktop"
}
return usage.AgentOf(r.Header.Get("User-Agent"))
ua := r.Header.Get("User-Agent")
if strings.HasPrefix(strings.ToLower(strings.TrimSpace(ua)), "cursor") {
// cursor-agent talks to Cursor's backend alone, never to a
// gateway: a Cursor User-Agent here is Cursor Private Inference's
// ("Cursor-CLI/…") sent with a key other than its own, the one
// typed in its Open configuration or a model's settings ("magpie"),
// and it is shown its models as with its key (mamba on Discord)
return "cursor-local"
}
return usage.AgentOf(ua)
}
// StandIn is the model an agent is set to use in place of one it named that