import { type JsonValue } from "@bufbuild/protobuf"; import type { AssistantMessage, CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, StreamFunction, StreamOptions, TextContent, ThinkingContent, ToolCall, ToolResultMessage } from "../types"; import { kStreamingBlockIndex, kStreamingBlockKind, kStreamingLastParseLen, kStreamingPartialJson } from "../utils/block-symbols"; import { AssistantMessageEventStream } from "../utils/event-stream"; export declare const CURSOR_API_URL = "https://api2.cursor.sh"; export declare const CURSOR_CLIENT_VERSION = "cli-2026.01.09-231024f"; export interface CursorOptions extends StreamOptions { customSystemPrompt?: string; conversationId?: string; execHandlers?: CursorExecHandlers; onToolResult?: CursorToolResultHandler; } export declare const streamCursor: StreamFunction<"cursor-agent">; export type ToolCallState = ToolCall & { [kStreamingBlockIndex]: number; [kStreamingPartialJson]?: string; [kStreamingLastParseLen]?: number; [kStreamingBlockKind]: "mcp" | "todo"; }; export interface BlockState { currentTextBlock: (TextContent & { [kStreamingBlockIndex]: number; }) | null; currentThinkingBlock: (ThinkingContent & { [kStreamingBlockIndex]: number; }) | null; currentToolCall: ToolCallState | null; firstTokenTime: number | undefined; setTextBlock: (b: (TextContent & { [kStreamingBlockIndex]: number; }) | null) => void; setThinkingBlock: (b: (ThinkingContent & { [kStreamingBlockIndex]: number; }) | null) => void; setToolCall: (t: ToolCallState | null) => void; setFirstTokenTime: () => void; } export interface UsageState { sawTokenDelta: boolean; } /** Exported for tests: verifies handler is invoked with correct `this` when passed as bound. */ export declare function resolveExecHandler(args: TArgs, handler: ((args: TArgs) => Promise>) | undefined, onToolResult: CursorToolResultHandler | undefined, buildFromToolResult: (toolResult: ToolResultMessage) => TResult, buildRejected: (reason: string) => TResult, buildError: (error: string) => TResult): Promise<{ execResult: TResult; toolResult?: ToolResultMessage; }>; /** * Merge the decoded completion-frame `McpArgs` map into the args assembled * from streamed `args_text_delta` snapshots. * * The completion frame is authoritative for the scalars it carries — but it * can omit oversized parameters entirely and can downgrade a structured value * to its raw string fallback when `decodeMcpArgValue` cannot parse it as * JSON. Overwriting the streamed args wholesale therefore loses data (e.g. * the task tool's `tasks` array on multi-subagent dispatches, issue #2615). * * Rules per key: * - completion key absent → keep the streamed value. * - completion is a string while the streamed value is structured (object or * array) → keep the streamed value (the completion frame downgraded it). * - otherwise → completion wins. */ export declare function mergeCursorMcpToolCallArgs(streamed: Record | undefined, completion: Record | undefined): Record; /** Exported for tests: drives one Cursor interaction update through the streaming state machine. */ export declare function processInteractionUpdate(update: any, output: AssistantMessage, stream: AssistantMessageEventStream, state: BlockState, usageState: UsageState): void; /** * Build `ConversationStateStructure.rootPromptMessagesJson` blob IDs for the * system prompt plus prior conversation history, as JSON blobs matching * Cursor's internal Vercel-AI-SDK-shaped message format. * * Cursor's server uses `rootPromptMessagesJson` (not `turns[]`) to build the * actual model prompt. `turns[]` is UI/display metadata. Without populating * this field, multi-turn conversations lose prior context — the model sees * only an empty placeholder where historical user turns should be. * The active user message is excluded because it is sent in the action. */ /** * Build one Cursor system-message JSON blob per ordered system prompt. Emitting separate blobs * (rather than a single `\n\n`-joined string) lets Cursor's blob cache hit independently per * entry: changing only the last prompt does not invalidate earlier blob ids, so the prefix * up to the changed prompt remains cached on the server side. * * When no system prompts are provided, returns a single default greeting so we never emit * an empty `rootPromptMessagesJson` head. */ export declare function buildCursorSystemPromptJsons(systemPrompt: readonly string[] | undefined): string[]; /** Exported for tests: decodes Cursor history blobs built from conversation messages. */ export declare function buildCursorHistoryForTest(messages: Message[], activeUserMessageIndex?: number): { rootPromptMessagesJson: unknown[]; turnUserMessagesJson: JsonValue[]; turnStepMessagesJson: JsonValue[][]; };