diff --git a/open-sse/executors/cursor.js b/open-sse/executors/cursor.js index 2873782..0c92b6e 100644 --- a/open-sse/executors/cursor.js +++ b/open-sse/executors/cursor.js @@ -132,7 +132,10 @@ export class CursorExecutor extends BaseExecutor { const messages = body.messages || []; const tools = body.tools || []; const reasoningEffort = body.reasoning_effort || null; - return generateCursorBody(messages, model, tools, reasoningEffort); + // Detect Claude Code UA to force Agent mode (issue #643) + const ua = credentials?.rawHeaders?.["user-agent"] || ""; + const forceAgentMode = ua.includes("claude-cli") || ua.includes("claude-code") || ua.includes("Claude Code"); + return generateCursorBody(messages, model, tools, reasoningEffort, forceAgentMode); } async makeFetchRequest(url, headers, body, signal, proxyOptions = null) { diff --git a/open-sse/translator/request/openai-to-cursor.js b/open-sse/translator/request/openai-to-cursor.js index a36f25c..1e3aa9c 100644 --- a/open-sse/translator/request/openai-to-cursor.js +++ b/open-sse/translator/request/openai-to-cursor.js @@ -169,8 +169,10 @@ function convertMessages(messages) { export function buildCursorRequest(model, body, stream, credentials) { const messages = convertMessages(body.messages || []); + // Strip fields irrelevant to Cursor (OpenAI/Anthropic-specific) const { user, metadata, tool_choice, stream_options, system, ...rest } = body; + return { ...rest, messages, diff --git a/open-sse/utils/cursorProtobuf.js b/open-sse/utils/cursorProtobuf.js index e6f7f8b..c870921 100644 --- a/open-sse/utils/cursorProtobuf.js +++ b/open-sse/utils/cursorProtobuf.js @@ -447,9 +447,9 @@ export function encodeMcpTool(tool) { // ==================== REQUEST BUILDING ==================== -export function encodeRequest(messages, modelName, tools = [], reasoningEffort = null) { +export function encodeRequest(messages, modelName, tools = [], reasoningEffort = null, forceAgentMode = false) { const hasTools = tools?.length > 0; - const isAgentic = hasTools; + const isAgentic = hasTools || forceAgentMode; const formattedMessages = []; const messageIds = []; const normalizedMessages = []; @@ -583,8 +583,8 @@ export function encodeRequest(messages, modelName, tools = [], reasoningEffort = ); } -export function buildChatRequest(messages, modelName, tools = [], reasoningEffort = null) { - return encodeField(FIELD.REQUEST, WIRE_TYPE.LEN, encodeRequest(messages, modelName, tools, reasoningEffort)); +export function buildChatRequest(messages, modelName, tools = [], reasoningEffort = null, forceAgentMode = false) { + return encodeField(FIELD.REQUEST, WIRE_TYPE.LEN, encodeRequest(messages, modelName, tools, reasoningEffort, forceAgentMode)); } /** @@ -646,10 +646,10 @@ export function wrapConnectRPCFrame(payload, compress = false) { return frame; } -export function generateCursorBody(messages, modelName, tools = [], reasoningEffort = null) { - log("BODY", `Generating: ${messages.length} msgs, model=${modelName}, tools=${tools.length}, reasoning=${reasoningEffort || "none"}`); +export function generateCursorBody(messages, modelName, tools = [], reasoningEffort = null, forceAgentMode = false) { + log("BODY", `Generating: ${messages.length} msgs, model=${modelName}, tools=${tools.length}, reasoning=${reasoningEffort || "none"}, forceAgentMode=${forceAgentMode}`); - const protobuf = buildChatRequest(messages, modelName, tools, reasoningEffort); + const protobuf = buildChatRequest(messages, modelName, tools, reasoningEffort, forceAgentMode); const framed = wrapConnectRPCFrame(protobuf, false); // Cursor doesn't support compressed requests log("BODY", `Protobuf=${protobuf.length}B, Framed=${framed.length}B`);