1233 lines · 36.4 KB · Go
RawDownload
package helps import (	"bytes"	"compress/gzip"	"crypto/rand"	"crypto/sha256"	"encoding/base64"	"encoding/binary"	"encoding/hex"	"encoding/json"	"fmt"	"io"	"math"	"net/http"	"runtime"	"strings"	"sync/atomic"	"unicode/utf8" 	"github.com/google/uuid"	"github.com/router-for-me/CLIProxyAPI/v7/internal/cache"	"github.com/router-for-me/CLIProxyAPI/v7/internal/util"	"google.golang.org/protobuf/encoding/protowire") const (	// ConnectFlagData marks an uncompressed Connect-proto data frame.	ConnectFlagData byte = 0x00	// ConnectFlagCompressed marks a gzipped Connect-proto frame.	ConnectFlagCompressed byte = 0x01	// ConnectFlagEndStream marks the terminal Connect-proto trailer frame.	ConnectFlagEndStream byte = 0x02 	// DevinDefaultBaseURL is the default upstream Codeium/Devin endpoint.	DevinDefaultBaseURL = "https://server.codeium.com"	// DevinChatPath is the Connect-RPC endpoint for chat completions.	DevinChatPath = "/exa.api_server_pb.ApiServerService/GetChatMessage" 	// DevinDefaultClientName is the client identifier declared in metadata.	DevinDefaultClientName = "chisel"	// DevinDefaultClientVersion is the client version declared in metadata.	DevinDefaultClientVersion = "3000.10.21"	// DevinFingerprintHexLen is the mandatory 732-hex-character length for metadata #31.	DevinFingerprintHexLen = 732 	// DevinDefaultMaxTokens is the fallback max completion tokens.	DevinDefaultMaxTokens = 128000 	maxConnectFrameSize      = 16 * 1024 * 1024	maxDecompressedFrameSize = 64 * 1024 * 1024) // DevinTool represents a tool definition in GetChatMessageRequest.type DevinTool struct {	Name        string	Description string	Parameters  []byte} // DevinToolCall represents a tool call in a ChatMessagePrompt.type DevinToolCall struct {	ID        string	Name      string	Arguments string} // DevinToolCallDelta represents a streaming tool call chunk from response Field 6.type DevinToolCallDelta struct {	ID        string	Name      string	Arguments string	Index     int} // DevinImage represents an image attachment in a DevinPrompt (Prompt #10).type DevinImage struct {	Base64Data string	MimeType   string} // DevinPrompt represents a single turn in the request history (repeated Field 3).type DevinPrompt struct {	MessageID     string	Source        int // 1=user, 2=assistant, 4=tool	Content       string	Images        []DevinImage	ToolCalls     []DevinToolCall	ToolCallID    string // For source=4 (tool result)	Thinking      string	Signature     []byte	SignatureType string} // DevinUsage captures token accounting from response Field 7.type DevinUsage struct {	PromptTokens     int64             `json:"prompt_tokens"`	CompletionTokens int64             `json:"completion_tokens"`	CachedTokens     int64             `json:"cached_tokens"`	StatusCode       uint64            `json:"status_code,omitempty"`	RequestID        string            `json:"request_id,omitempty"`	ModelName        string            `json:"model_name,omitempty"`	Headers          map[string]string `json:"headers,omitempty"`} // DevinFrameResult represents decoded content from a single Connect-proto frame.type DevinFrameResult struct {	OutputID                string	Timestamp               uint64	ContentText             string	DeltaTokens             uint64	StopReason              uint64 // 2/4=stop, 10=tool_calls	ToolCallDeltas          []DevinToolCallDelta	ThinkingText            string	DeltaSignature          []byte	DeltaSignatureType      string	Latency                 float64	MessageID               string	Usage                   *DevinUsage	ResponseDimensionGroups [][]byte	UnknownFieldNumbers     []int} // GenerateDevinDeviceFingerprint generates a 732-character hex device fingerprint.// When seed is empty, it generates a cryptographically random 732-character hex string per request (matching native devin-cli).// When seed is provided, it derives a deterministic 732-character hex fingerprint.func GenerateDevinDeviceFingerprint(seed string) string {	if seed == "" {		var b [DevinFingerprintHexLen / 2]byte		if _, err := rand.Read(b[:]); err == nil {			return hex.EncodeToString(b[:])		}		seed = uuid.New().String()	}	var sb strings.Builder	counter := 0	for sb.Len() < DevinFingerprintHexLen {		h := sha256.Sum256([]byte(fmt.Sprintf("%s-%d", seed, counter)))		sb.WriteString(hex.EncodeToString(h[:]))		counter++	}	// Truncate to exact 732 chars (11 full 64-char sha256 hex blocks + 28 chars of the 12th block) to match Devin CLI.	return sb.String()[:DevinFingerprintHexLen]} // GenerateDevinSentryTrace generates a Sentry distributed tracing header in the format:// "<32-hex-trace-id>-<16-hex-span-id>-1"func GenerateDevinSentryTrace() string {	var b [24]byte	if _, err := rand.Read(b[:]); err != nil {		u1 := strings.ReplaceAll(uuid.New().String(), "-", "")		u2 := strings.ReplaceAll(uuid.New().String(), "-", "")[:16]		return u1 + "-" + u2 + "-1"	}	traceID := hex.EncodeToString(b[:16])	spanID := hex.EncodeToString(b[16:24])	return traceID + "-" + spanID + "-1"} const defaultMaxSessionTurnCounters = 5000 var sessionTurnLRU = cache.NewBoundedLRU[string, *atomic.Uint64](defaultMaxSessionTurnCounters, nil) // NextDevinSessionTurnIndex returns the next 0-based request ordinal for a session (Field 15.2).// In native devin-cli, the counter is process-scoped per session:// First request in a session returns 0 (which is omitted on the wire).// Subsequent requests return 1, 2, 3... monotonically.func NextDevinSessionTurnIndex(sessionID string) int {	cleanID := strings.TrimSpace(sessionID)	if cleanID == "" {		return 0	} 	counter := sessionTurnLRU.GetOrAdd(cleanID, func() *atomic.Uint64 {		return &atomic.Uint64{}	})	return int(counter.Add(1) - 1)} // ResetDevinSessionTurnIndex clears the session counter (used for testing or explicit session reset).func ResetDevinSessionTurnIndex(sessionID string) {	sessionTurnLRU.Delete(strings.TrimSpace(sessionID))} // WrapConnectEnvelope wraps raw payload bytes into a standard 5-byte Connect envelope:// [1 byte flag: 0x00] + [4 byte big-endian length] + [payload].func WrapConnectEnvelope(protoBytes []byte) []byte {	return WrapConnectEnvelopeWithFlag(ConnectFlagData, protoBytes)} // WrapConnectEnvelopeWithFlag wraps payload bytes with an explicit Connect flag.func WrapConnectEnvelopeWithFlag(flag byte, protoBytes []byte) []byte {	header := make([]byte, 5, 5+len(protoBytes))	header[0] = flag	binary.BigEndian.PutUint32(header[1:5], uint32(len(protoBytes)))	return append(header, protoBytes...)} // ReadConnectFrame reads a single framed message from a Connect-proto stream.func ReadConnectFrame(r io.Reader) (flag byte, payload []byte, err error) {	header := make([]byte, 5)	if _, err := io.ReadFull(r, header); err != nil {		return 0, nil, err	}	flag = header[0]	if flag != ConnectFlagData && flag != ConnectFlagCompressed && flag != ConnectFlagEndStream && flag != (ConnectFlagCompressed|ConnectFlagEndStream) {		return flag, nil, fmt.Errorf("invalid connect frame flag: 0x%02x", flag)	}	length := binary.BigEndian.Uint32(header[1:5])	if length > maxConnectFrameSize {		return flag, nil, fmt.Errorf("connect frame length %d exceeds maximum limit (%d)", length, maxConnectFrameSize)	} 	payload = make([]byte, length)	if _, err := io.ReadFull(r, payload); err != nil {		return 0, nil, err	} 	if flag&ConnectFlagCompressed != 0 {		gz, errGz := gzip.NewReader(bytes.NewReader(payload))		if errGz != nil {			return flag, nil, fmt.Errorf("decompress gzip connect frame: %w", errGz)		}		defer func() { _ = gz.Close() }() 		initCap := int(length) * 4		if initCap > 4*1024*1024 {			initCap = 4 * 1024 * 1024		} else if initCap < 4096 {			initCap = 4096		}		decompBuf := bytes.NewBuffer(make([]byte, 0, initCap))		limitedReader := io.LimitReader(gz, maxDecompressedFrameSize+1)		if _, errRead := decompBuf.ReadFrom(limitedReader); errRead != nil {			return flag, nil, fmt.Errorf("read decompressed connect frame: %w", errRead)		}		if decompBuf.Len() > maxDecompressedFrameSize {			return flag, nil, fmt.Errorf("decompressed frame size exceeds maximum limit (%d)", maxDecompressedFrameSize)		}		payload = decompBuf.Bytes()	} 	return flag, payload, nil} // BuildDevinClientMetadataBytes constructs the serialized bytes for Field 1 (ClientMetadata).func BuildDevinClientMetadataBytes(sessionToken, deviceSeed, osName string) []byte {	if osName == "" {		osName = runtime.GOOS	}	deviceFingerprint := GenerateDevinDeviceFingerprint(deviceSeed) 	var f1Bytes []byte	f1Bytes = protowire.AppendTag(f1Bytes, 1, protowire.BytesType)	f1Bytes = protowire.AppendString(f1Bytes, "devin-cli") 	f1Bytes = protowire.AppendTag(f1Bytes, 2, protowire.BytesType)	f1Bytes = protowire.AppendString(f1Bytes, DevinDefaultClientVersion) 	f1Bytes = protowire.AppendTag(f1Bytes, 3, protowire.BytesType)	f1Bytes = protowire.AppendString(f1Bytes, sessionToken) 	f1Bytes = protowire.AppendTag(f1Bytes, 4, protowire.BytesType)	f1Bytes = protowire.AppendString(f1Bytes, "en") 	f1Bytes = protowire.AppendTag(f1Bytes, 5, protowire.BytesType)	f1Bytes = protowire.AppendString(f1Bytes, osName) 	f1Bytes = protowire.AppendTag(f1Bytes, 7, protowire.BytesType)	f1Bytes = protowire.AppendString(f1Bytes, DevinDefaultClientVersion) 	f1Bytes = protowire.AppendTag(f1Bytes, 12, protowire.BytesType)	f1Bytes = protowire.AppendString(f1Bytes, DevinDefaultClientName) 	f1Bytes = protowire.AppendTag(f1Bytes, 28, protowire.BytesType)	f1Bytes = protowire.AppendString(f1Bytes, DevinDefaultClientName) 	f1Bytes = protowire.AppendTag(f1Bytes, 31, protowire.BytesType)	f1Bytes = protowire.AppendString(f1Bytes, deviceFingerprint)	return f1Bytes} // BuildDevinGetChatMessageRequest encodes an entire GetChatMessageRequest protobuf payload.func BuildDevinGetChatMessageRequest(	sessionToken string,	deviceSeed string,	chatModelUID string,	systemPrompt string,	prompts []DevinPrompt,	tools []DevinTool,	temperature *float64,	maxTokens int,	sessionID string,	cascadeID string,	matcher *SensitiveWordMatcher,) []byte {	if maxTokens <= 0 {		maxTokens = DevinDefaultMaxTokens	}	if sessionID == "" {		sessionID = uuid.New().String()	}	if cascadeID == "" {		cascadeID = sessionID	}	osName := runtime.GOOS 	estimatedSize := 4096 + len(systemPrompt)	for _, p := range prompts {		estimatedSize += 256 + len(p.Content)	}	reqBytes := make([]byte, 0, estimatedSize) 	// 1. ClientMetadata (Field 1)	f1Bytes := BuildDevinClientMetadataBytes(sessionToken, deviceSeed, osName)	reqBytes = protowire.AppendTag(reqBytes, 1, protowire.BytesType)	reqBytes = protowire.AppendBytes(reqBytes, f1Bytes) 	// 2. System prompt (Field 2)	if systemPrompt != "" {		sanitized := SanitizeDevinSystemPrompt(systemPrompt, matcher)		if sanitized != "" {			reqBytes = protowire.AppendTag(reqBytes, 2, protowire.BytesType)			reqBytes = protowire.AppendString(reqBytes, sanitized)		}	} 	// 3. Repeated History Prompts (Field 3)	for _, p := range prompts {		var pBytes []byte 		msgID := p.MessageID		if msgID == "" {			msgID = uuid.New().String()		}		pBytes = protowire.AppendTag(pBytes, 1, protowire.BytesType)		pBytes = protowire.AppendString(pBytes, msgID) 		source := p.Source		if source <= 0 {			source = 1 // default to user		}		pBytes = protowire.AppendTag(pBytes, 2, protowire.VarintType)		pBytes = protowire.AppendVarint(pBytes, uint64(source)) 		content := p.Content		pBytes = protowire.AppendTag(pBytes, 3, protowire.BytesType)		pBytes = protowire.AppendString(pBytes, content) 		for _, tc := range p.ToolCalls {			var tcBytes []byte			if tc.ID != "" {				tcBytes = protowire.AppendTag(tcBytes, 1, protowire.BytesType)				tcBytes = protowire.AppendString(tcBytes, tc.ID)			}			if tc.Name != "" {				tcBytes = protowire.AppendTag(tcBytes, 2, protowire.BytesType)				tcBytes = protowire.AppendString(tcBytes, tc.Name)			}			if tc.Arguments != "" {				tcBytes = protowire.AppendTag(tcBytes, 3, protowire.BytesType)				tcBytes = protowire.AppendString(tcBytes, tc.Arguments)			}			pBytes = protowire.AppendTag(pBytes, 6, protowire.BytesType)			pBytes = protowire.AppendBytes(pBytes, tcBytes)		} 		if p.ToolCallID != "" {			pBytes = protowire.AppendTag(pBytes, 7, protowire.BytesType)			pBytes = protowire.AppendString(pBytes, p.ToolCallID)		} 		for _, img := range p.Images {			data := strings.TrimSpace(img.Base64Data)			if data == "" {				continue			}			var imgBytes []byte			imgBytes = protowire.AppendTag(imgBytes, 1, protowire.BytesType)			imgBytes = protowire.AppendString(imgBytes, data) 			mime := strings.TrimSpace(img.MimeType)			if mime == "" {				mime = "image/png"			}			imgBytes = protowire.AppendTag(imgBytes, 2, protowire.BytesType)			imgBytes = protowire.AppendString(imgBytes, mime) 			pBytes = protowire.AppendTag(pBytes, 10, protowire.BytesType)			pBytes = protowire.AppendBytes(pBytes, imgBytes)		} 		if p.Thinking != "" {			pBytes = protowire.AppendTag(pBytes, 11, protowire.BytesType)			pBytes = protowire.AppendString(pBytes, p.Thinking)		} 		if len(p.Signature) > 0 {			pBytes = protowire.AppendTag(pBytes, 12, protowire.BytesType)			pBytes = protowire.AppendBytes(pBytes, p.Signature)		} 		if p.SignatureType != "" {			pBytes = protowire.AppendTag(pBytes, 18, protowire.BytesType)			pBytes = protowire.AppendString(pBytes, p.SignatureType)		} 		reqBytes = protowire.AppendTag(reqBytes, 3, protowire.BytesType)		reqBytes = protowire.AppendBytes(reqBytes, pBytes)	} 	// 4. Fixed flags (Field 7: Varint 5)	reqBytes = protowire.AppendTag(reqBytes, 7, protowire.VarintType)	reqBytes = protowire.AppendVarint(reqBytes, 5) 	// 5. Completion config (Field 8)	var f8Bytes []byte	f8Bytes = protowire.AppendTag(f8Bytes, 1, protowire.VarintType)	f8Bytes = protowire.AppendVarint(f8Bytes, 1) 	f8Bytes = protowire.AppendTag(f8Bytes, 2, protowire.VarintType)	f8Bytes = protowire.AppendVarint(f8Bytes, uint64(maxTokens)) 	f8Bytes = protowire.AppendTag(f8Bytes, 3, protowire.VarintType)	f8Bytes = protowire.AppendVarint(f8Bytes, 400) 	tempVal := 1.0	if temperature != nil {		tempVal = *temperature	}	f8Bytes = protowire.AppendTag(f8Bytes, 5, protowire.Fixed64Type)	f8Bytes = protowire.AppendFixed64(f8Bytes, math.Float64bits(tempVal)) 	f8Bytes = protowire.AppendTag(f8Bytes, 7, protowire.VarintType)	f8Bytes = protowire.AppendVarint(f8Bytes, 40) 	f8Bytes = protowire.AppendTag(f8Bytes, 8, protowire.Fixed64Type)	f8Bytes = protowire.AppendFixed64(f8Bytes, math.Float64bits(float64(float32(0.95)))) 	reqBytes = protowire.AppendTag(reqBytes, 8, protowire.BytesType)	reqBytes = protowire.AppendBytes(reqBytes, f8Bytes) 	// 6. Repeated Tools (Field 10)	for _, tool := range tools {		var tBytes []byte		if tool.Name != "" {			tBytes = protowire.AppendTag(tBytes, 1, protowire.BytesType)			tBytes = protowire.AppendString(tBytes, tool.Name)		}		desc := tool.Description		// Claude Code subagent tool descriptions hardcode snake_case "task_id", but Devin's		// upstream tool execution environment strictly expects camelCase "taskId". Normalizing		// the prompt description prevents the model from generating incompatible parameter names.		if strings.Contains(desc, "Takes a task_id parameter identifying the task") {			desc = strings.ReplaceAll(desc, "Takes a task_id parameter identifying the task", "Takes a taskId parameter identifying the task")		}		if desc != "" {			tBytes = protowire.AppendTag(tBytes, 2, protowire.BytesType)			tBytes = protowire.AppendString(tBytes, desc)		}		if len(tool.Parameters) > 0 {			tBytes = protowire.AppendTag(tBytes, 3, protowire.BytesType)			tBytes = protowire.AppendBytes(tBytes, tool.Parameters)		}		reqBytes = protowire.AppendTag(reqBytes, 10, protowire.BytesType)		reqBytes = protowire.AppendBytes(reqBytes, tBytes)	} 	// 7. Thread session metadata (Field 15)	// In native devin-cli:	// Field 1: sessionID (UUID string)	// Field 2: turnIndex (per-session request ordinal, omitted when 0)	// Field 3: 4 (varint)	// Field 4: 14 (emitted conditionally on user-turn boundaries)	turnIndex := NextDevinSessionTurnIndex(sessionID) 	var f15Bytes []byte	f15Bytes = protowire.AppendTag(f15Bytes, 1, protowire.BytesType)	f15Bytes = protowire.AppendString(f15Bytes, sessionID) 	if turnIndex > 0 {		f15Bytes = protowire.AppendTag(f15Bytes, 2, protowire.VarintType)		f15Bytes = protowire.AppendVarint(f15Bytes, uint64(turnIndex))	} 	f15Bytes = protowire.AppendTag(f15Bytes, 3, protowire.VarintType)	f15Bytes = protowire.AppendVarint(f15Bytes, 4) 	// In native devin-cli, Field 15.4=14 is emitted on user-turn boundaries	if len(prompts) > 0 && prompts[len(prompts)-1].Source == 1 {		if turnIndex == 0 || len(prompts) < 2 || prompts[len(prompts)-2].Source != 1 {			f15Bytes = protowire.AppendTag(f15Bytes, 4, protowire.VarintType)			f15Bytes = protowire.AppendVarint(f15Bytes, 14)		}	} 	reqBytes = protowire.AppendTag(reqBytes, 15, protowire.BytesType)	reqBytes = protowire.AppendBytes(reqBytes, f15Bytes) 	// 8. Cascade ID (Field 16: session-stable prompt cache key)

Lines 1–500 of 1233.