Merge PR #525 (v6.9.27)

This commit is contained in:
Luis Pater
2026-04-16 03:16:28 +08:00
68 changed files with 3075 additions and 3239 deletions

View File

@@ -4,11 +4,11 @@ import (
"bytes"
"context"
"crypto/sha256"
"errors"
"crypto/tls"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
@@ -30,14 +30,14 @@ import (
)
const (
cursorAPIURL = "https://api2.cursor.sh"
cursorRunPath = "/agent.v1.AgentService/Run"
cursorModelsPath = "/agent.v1.AgentService/GetUsableModels"
cursorClientVersion = "cli-2026.02.13-41ac335"
cursorAuthType = "cursor"
cursorAPIURL = "https://api2.cursor.sh"
cursorRunPath = "/agent.v1.AgentService/Run"
cursorModelsPath = "/agent.v1.AgentService/GetUsableModels"
cursorClientVersion = "cli-2026.02.13-41ac335"
cursorAuthType = "cursor"
cursorHeartbeatInterval = 5 * time.Second
cursorSessionTTL = 5 * time.Minute
cursorCheckpointTTL = 30 * time.Minute
cursorSessionTTL = 5 * time.Minute
cursorCheckpointTTL = 30 * time.Minute
)
// CursorExecutor handles requests to the Cursor API via Connect+Protobuf protocol.
@@ -63,9 +63,9 @@ type cursorSession struct {
pending []pendingMcpExec
cancel context.CancelFunc // cancels the session-scoped heartbeat (NOT tied to HTTP request)
createdAt time.Time
authID string // auth file ID that created this session (for multi-account isolation)
toolResultCh chan []toolResultInfo // receives tool results from the next HTTP request
resumeOutCh chan cliproxyexecutor.StreamChunk // output channel for resumed response
authID string // auth file ID that created this session (for multi-account isolation)
toolResultCh chan []toolResultInfo // receives tool results from the next HTTP request
resumeOutCh chan cliproxyexecutor.StreamChunk // output channel for resumed response
switchOutput func(ch chan cliproxyexecutor.StreamChunk) // callback to switch output channel
}
@@ -148,7 +148,7 @@ type cursorStatusErr struct {
msg string
}
func (e cursorStatusErr) Error() string { return e.msg }
func (e cursorStatusErr) Error() string { return e.msg }
func (e cursorStatusErr) StatusCode() int { return e.code }
func (e cursorStatusErr) RetryAfter() *time.Duration { return nil } // no retry-after info from Cursor; conductor uses exponential backoff
@@ -786,7 +786,7 @@ func (e *CursorExecutor) resumeWithToolResults(
func openCursorH2Stream(accessToken string) (*cursorproto.H2Stream, error) {
headers := map[string]string{
":path": cursorRunPath,
"content-type": "application/connect+proto",
"content-type": "application/connect+proto",
"connect-protocol-version": "1",
"te": "trailers",
"authorization": "Bearer " + accessToken,
@@ -876,21 +876,21 @@ func processH2SessionFrames(
buf.Write(data)
log.Debugf("cursor: processH2SessionFrames[%s]: buf total=%d", stream.ID(), buf.Len())
// Process all complete frames
for {
currentBuf := buf.Bytes()
if len(currentBuf) == 0 {
break
}
flags, payload, consumed, ok := cursorproto.ParseConnectFrame(currentBuf)
if !ok {
// Log detailed info about why parsing failed
previewLen := min(20, len(currentBuf))
log.Debugf("cursor: incomplete frame in buffer, waiting for more data (buf=%d bytes, first bytes: %x = %q)", len(currentBuf), currentBuf[:previewLen], string(currentBuf[:previewLen]))
break
}
buf.Next(consumed)
log.Debugf("cursor: parsed Connect frame flags=0x%02x payload=%d bytes consumed=%d", flags, len(payload), consumed)
// Process all complete frames
for {
currentBuf := buf.Bytes()
if len(currentBuf) == 0 {
break
}
flags, payload, consumed, ok := cursorproto.ParseConnectFrame(currentBuf)
if !ok {
// Log detailed info about why parsing failed
previewLen := min(20, len(currentBuf))
log.Debugf("cursor: incomplete frame in buffer, waiting for more data (buf=%d bytes, first bytes: %x = %q)", len(currentBuf), currentBuf[:previewLen], string(currentBuf[:previewLen]))
break
}
buf.Next(consumed)
log.Debugf("cursor: parsed Connect frame flags=0x%02x payload=%d bytes consumed=%d", flags, len(payload), consumed)
if flags&cursorproto.ConnectEndStreamFlag != 0 {
if err := cursorproto.ParseConnectEndStream(payload); err != nil {
@@ -1080,15 +1080,15 @@ func processH2SessionFrames(
// --- OpenAI request parsing ---
type parsedOpenAIRequest struct {
Model string
Messages []gjson.Result
Tools []gjson.Result
Stream bool
Model string
Messages []gjson.Result
Tools []gjson.Result
Stream bool
SystemPrompt string
UserText string
Images []cursorproto.ImageData
Turns []cursorproto.TurnData
ToolResults []toolResultInfo
UserText string
Images []cursorproto.ImageData
Turns []cursorproto.TurnData
ToolResults []toolResultInfo
}
type toolResultInfo struct {