import { type JsonValue } from "@bufbuild/protobuf"; import type { AssistantMessage, CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, Model, StreamFunction, StreamOptions, ToolCall, ToolResultMessage, Usage } from "../types"; import { kCursorExecResolved } from "../utils/block-symbols"; import { CURSOR_CLIENT_VERSION } from "./cursor/client-version"; import type { CursorRule, RequestedModel_ModelParameterbytes } from "./cursor/gen/agent_pb"; import { type ConversationStateStructure } from "./cursor/gen/agent_pb"; export declare const CURSOR_API_URL = "https://api2.cursor.sh"; export { CURSOR_CLIENT_VERSION }; /** Drop all cached state + blob bytes for a conversation (F15 bound + session-teardown hook). */ export declare function disposeCursorConversation(conversationId: string): void; export interface CursorOptions extends StreamOptions { customSystemPrompt?: string; conversationId?: string; execHandlers?: CursorExecHandlers; onToolResult?: CursorToolResultHandler; } /** Build the ordered global USER rules Cursor expects for the current system prompt. */ export declare function buildCursorRequestContextRules(systemPrompt: readonly string[] | undefined): CursorRule[]; export interface CursorWireModelResolution { modelId: string; parameters: RequestedModel_ModelParameterbytes[]; translated: boolean; } /** Resolve a Cursor model's GPT effort suffix into its wire model and parameter. */ export declare function resolveCursorWireModelForTest(model: Pick, "id" | "wireModelId">): CursorWireModelResolution; /** Turn Cursor's opaque HTTP/2 failure into a useful transport diagnosis. */ export declare function mapH2TransportError(error: unknown, baseUrl: string): unknown; export declare const streamCursor: StreamFunction<"cursor-agent">; type ToolCallState = ToolCall & { index: number; partialJson?: string; kind: "mcp" | "todo_write" | "native" | "cursor-exec"; [kCursorExecResolved]?: true; }; interface UsageState { sawTokenDelta: boolean; /** * Latest `ConversationTokenDetails.used_tokens`: the whole conversation's * token consumption as counted by Cursor, not this turn's output. */ conversationUsedTokens: number; /** Output tokens already included in the latest checkpoint snapshot. */ checkpointOutputTokens: number; /** Whether the current stream received a checkpoint, including an explicit zero. */ hasConversationCheckpoint: boolean; pendingCheckpoint?: ConversationStateStructure; } /** 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; }>; /** Exported for deterministic coverage of ordered server-message handling. */ export declare function createCursorMessageQueueForTest(onError?: (error: unknown) => void): { enqueue(handler: () => void | Promise): Promise; drain(): Promise; }; /** Exported for direct regression coverage of the JSON-safety boundary. */ export declare function cursorJsonSafeValueForTest(value: unknown): unknown; export declare function buildNativeToolCallBlock(toolCall: Record, callId: string, index: number): ToolCallState | null; /** * Cursor streams output tokens as deltas and reports whole-conversation * consumption separately as `ConversationTokenDetails.used_tokens`. Derive * prompt tokens from the difference so context accounting and compaction see a * real prompt size instead of zero. */ export declare function finalizeCursorUsage(output: AssistantMessage, usageState: UsageState): void; /** Exposes {@link finalizeCursorUsage} for tests without a live HTTP/2 stream. */ export declare function finalizeCursorUsageForTest(usedTokens: number, outputTokens: number, options?: { checkpointOutputTokens?: number; hasConversationCheckpoint?: boolean; }): Usage; /** * 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 last 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, modelId?: string): string[]; /** Exported for tests: decodes Cursor history blobs built from conversation messages. */ export declare function buildCursorHistoryForTest(messages: Message[]): { rootPromptMessagesJson: unknown[]; turnUserMessagesJson: JsonValue[]; };