@stablekernel/opencode-cursor 0.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -0
- package/LICENSE +21 -0
- package/README.md +356 -0
- package/dist/chunk-YYO6O43T.js +502 -0
- package/dist/chunk-YYO6O43T.js.map +1 -0
- package/dist/plugin/index.d.ts +17 -0
- package/dist/plugin/index.js +572 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/provider/index.d.ts +77 -0
- package/dist/provider/index.js +427 -0
- package/dist/provider/index.js.map +1 -0
- package/dist/sidecar/agent-host.d.ts +128 -0
- package/dist/sidecar/agent-host.js +93 -0
- package/dist/sidecar/agent-host.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/provider/index.ts","../../src/provider/language-model.ts","../../src/provider/message-map.ts","../../src/provider/stream-map.ts"],"sourcesContent":["import type { EmbeddingModelV3, ImageModelV3, ProviderV3 } from \"@ai-sdk/provider\";\nimport { NoSuchModelError } from \"@ai-sdk/provider\";\nimport type { AgentDefinition, AgentModeOption, McpServerConfig, SettingSource } from \"@cursor/sdk\";\nimport { resolveCursorApiKey } from \"../api-key.js\";\nimport { CursorLanguageModel, type CursorModelConfig } from \"./language-model.js\";\nimport type { ToolDisplay } from \"./stream-map.js\";\n\nexport interface CursorProviderOptions {\n /**\n * Cursor API key. opencode passes this from the provider's resolved auth /\n * options. When omitted, falls back to the CURSOR_API_KEY environment\n * variable at call time.\n */\n apiKey?: string;\n /** Provider id, supplied by opencode as `name`. Defaults to \"cursor\". */\n name?: string;\n /** Working directory for the local Cursor agent. Defaults to process.cwd(). */\n cwd?: string;\n /** Default conversation mode: \"agent\" (default) or \"plan\". Overridable per-request. */\n mode?: AgentModeOption;\n /** Default Cursor model params (id -> value), e.g. { thinking: \"high\" }. */\n params?: Record<string, string>;\n /**\n * MCP servers to make available to the Cursor agent, keyed by name. The\n * plugin's `config` hook populates this by translating opencode's configured\n * `config.mcp` servers, so the agent can use the same MCP servers (e.g.\n * Serena) that opencode does.\n */\n mcpServers?: Record<string, McpServerConfig>;\n /**\n * Cursor settings layers to load from the local filesystem (\"project\",\n * \"user\", \"all\", ...). Enables the agent to pick up your Cursor skills,\n * rules, and `.cursor/mcp.json` servers.\n */\n settingSources?: SettingSource[];\n /** Run the agent's tools inside Cursor's sandbox. */\n sandbox?: boolean;\n /** Cursor subagent definitions (`{ description, prompt, model?, mcpServers? }`). */\n agents?: Record<string, AgentDefinition>;\n /**\n * Reuse one Cursor agent per opencode session (resume across turns instead of\n * creating a fresh agent each turn). Off by default.\n */\n session?: boolean;\n /**\n * How Cursor's internal tool activity (shell/read/edit/mcp/…) is surfaced:\n * - `\"reasoning\"` (default): compact reasoning lines (works on every host).\n * - `\"blocks\"`: structured provider-executed `tool-call`/`tool-result` parts\n * so opencode renders proper tool blocks. Opt-in; requires a V3-native host.\n */\n toolDisplay?: ToolDisplay;\n}\n\n/**\n * Cursor provider for the Vercel AI SDK (V3), backed by the official\n * `@cursor/sdk` local agent runtime.\n *\n * opencode loads this package by its `npm` provider config, finds the export\n * whose name starts with `create`, calls it with `{ name, apiKey, ...options }`,\n * and then calls `.languageModel(modelId)`.\n */\nexport function createCursor(options: CursorProviderOptions = {}): ProviderV3 {\n const mcpServers =\n options.mcpServers && Object.keys(options.mcpServers).length > 0 ? options.mcpServers : undefined;\n const config: CursorModelConfig = {\n providerName: options.name ?? \"cursor\",\n apiKey: resolveCursorApiKey(options.apiKey),\n cwd: options.cwd ?? process.cwd(),\n mode: options.mode ?? \"agent\",\n ...(options.params ? { params: options.params } : {}),\n ...(mcpServers ? { mcpServers } : {}),\n ...(options.settingSources ? { settingSources: options.settingSources } : {}),\n ...(options.sandbox !== undefined ? { sandbox: options.sandbox } : {}),\n ...(options.agents ? { agents: options.agents } : {}),\n ...(options.session !== undefined ? { session: options.session } : {}),\n ...(options.toolDisplay ? { toolDisplay: options.toolDisplay } : {}),\n };\n\n const notImplemented = (kind: string, modelId: string): never => {\n throw new NoSuchModelError({\n modelId,\n modelType: kind as \"languageModel\",\n message: `The Cursor provider does not support ${kind} models.`,\n });\n };\n\n return {\n specificationVersion: \"v3\",\n languageModel: (modelId: string) => new CursorLanguageModel(modelId, config),\n embeddingModel: (modelId: string): EmbeddingModelV3 =>\n notImplemented(\"embeddingModel\", modelId),\n imageModel: (modelId: string): ImageModelV3 => notImplemented(\"imageModel\", modelId),\n };\n}\n","import type {\n LanguageModelV3,\n LanguageModelV3CallOptions,\n LanguageModelV3Content,\n LanguageModelV3FinishReason,\n LanguageModelV3StreamPart,\n LanguageModelV3Usage,\n} from \"@ai-sdk/provider\";\nimport { LoadAPIKeyError } from \"@ai-sdk/provider\";\nimport type {\n AgentDefinition,\n McpServerConfig,\n SettingSource,\n AgentModeOption,\n} from \"@cursor/sdk\";\nimport { resolveCursorApiKey } from \"../api-key.js\";\nimport { latestUserMessage, promptToCursorMessage } from \"./message-map.js\";\nimport { streamAgentTurn, type CursorEvent } from \"./agent-events.js\";\nimport { cursorEventsToContent, cursorEventsToStream, type ToolDisplay } from \"./stream-map.js\";\nimport { resolveControls } from \"./controls.js\";\nimport { acquireAgent } from \"./session-pool.js\";\n\nexport interface CursorModelConfig {\n /** Provider id used for logging and the providerOptions key (e.g. \"cursor\"). */\n providerName: string;\n /** Explicit API key; re-resolved against the env at call time when absent. */\n apiKey?: string;\n /** Working directory the local Cursor agent operates in. */\n cwd: string;\n /** Default conversation mode; overridable per-request via providerOptions. */\n mode: AgentModeOption;\n /** Default Cursor model params (id -> value); overridable per-request. */\n params?: Record<string, string>;\n /** MCP servers forwarded to the Cursor agent (e.g. opencode's Serena). */\n mcpServers?: Record<string, McpServerConfig>;\n /** Cursor settings layers to load from disk (skills, rules, .cursor/mcp.json). */\n settingSources?: SettingSource[];\n /** Run the agent's tools inside Cursor's sandbox. */\n sandbox?: boolean;\n /** Cursor subagent definitions made available to the agent. */\n agents?: Record<string, AgentDefinition>;\n /**\n * Reuse one Cursor agent per opencode session (resume across turns, sending\n * only the new message). Off by default; the default per-turn-fresh path\n * re-sends the full transcript and is robust to opencode's non-chat calls.\n */\n session?: boolean;\n /**\n * How Cursor's internal tool activity is surfaced (see {@link ToolDisplay}).\n * Defaults to `\"reasoning\"`.\n */\n toolDisplay?: ToolDisplay;\n}\n\n/**\n * A Vercel AI SDK `LanguageModelV3` backed by a local Cursor agent. opencode\n * loads this via the provider factory and calls `doStream` / `doGenerate`.\n * The event→stream translation lives in stream-map.ts so it can be unit tested\n * without a live agent.\n */\nexport class CursorLanguageModel implements LanguageModelV3 {\n readonly specificationVersion = \"v3\" as const;\n readonly modelId: string;\n readonly provider: string;\n // Images are passed inline as base64 data, so no URLs are fetched natively.\n readonly supportedUrls: Record<string, RegExp[]> = {};\n\n constructor(\n modelId: string,\n private readonly config: CursorModelConfig,\n ) {\n this.modelId = modelId;\n this.provider = config.providerName;\n }\n\n private requireApiKey(): string {\n const apiKey = resolveCursorApiKey(this.config.apiKey);\n if (!apiKey) {\n throw new LoadAPIKeyError({\n message:\n \"Cursor API key missing. Run `opencode auth login` and choose Cursor, or set CURSOR_API_KEY.\",\n });\n }\n return apiKey;\n }\n\n private async *agentRun(options: LanguageModelV3CallOptions): AsyncGenerator<CursorEvent> {\n // opencode delivers per-request controls (merged model options + selected\n // variant) under providerOptions keyed by our provider id. The session id is\n // injected there by the plugin's chat.params hook.\n const providerOptions = options.providerOptions?.[this.provider] as\n | Record<string, unknown>\n | undefined;\n const { mode, modelSelection } = resolveControls(\n this.modelId,\n { mode: this.config.mode, params: this.config.params },\n providerOptions,\n );\n const sessionID =\n typeof providerOptions?.[\"sessionID\"] === \"string\"\n ? (providerOptions[\"sessionID\"] as string)\n : undefined;\n const useSession = this.config.session === true && Boolean(sessionID);\n // Power users can resume a specific Cursor agent via\n // `providerOptions.cursor.agentId`; it takes precedence over session pooling.\n const explicitAgentId =\n typeof providerOptions?.[\"agentId\"] === \"string\"\n ? (providerOptions[\"agentId\"] as string)\n : undefined;\n\n const acquired = await acquireAgent({\n apiKey: this.requireApiKey(),\n modelSelection,\n mode,\n cwd: this.config.cwd,\n ...(this.config.settingSources ? { settingSources: this.config.settingSources } : {}),\n ...(this.config.sandbox !== undefined ? { sandbox: this.config.sandbox } : {}),\n ...(this.config.mcpServers ? { mcpServers: this.config.mcpServers } : {}),\n ...(this.config.agents ? { agents: this.config.agents } : {}),\n ...(useSession ? { name: `opencode/${sessionID!.slice(-8)}` } : {}),\n ...(explicitAgentId ? { agentId: explicitAgentId } : {}),\n sessionID,\n session: useSession,\n });\n\n // A resumed agent already remembers the prior conversation, so send only the\n // new turn; otherwise send the full transcript.\n const message = acquired.resumed\n ? (latestUserMessage(options.prompt) ?? promptToCursorMessage(options.prompt))\n : promptToCursorMessage(options.prompt);\n\n try {\n yield* streamAgentTurn(acquired.agent, message, { mode, abortSignal: options.abortSignal });\n } finally {\n acquired.release();\n }\n }\n\n async doStream(options: LanguageModelV3CallOptions): Promise<{\n stream: ReadableStream<LanguageModelV3StreamPart>;\n }> {\n return { stream: cursorEventsToStream(this.agentRun(options), this.config.toolDisplay) };\n }\n\n async doGenerate(options: LanguageModelV3CallOptions): Promise<{\n content: Array<LanguageModelV3Content>;\n finishReason: LanguageModelV3FinishReason;\n usage: LanguageModelV3Usage;\n warnings: Array<never>;\n }> {\n const result = await cursorEventsToContent(this.agentRun(options), this.config.toolDisplay);\n return { ...result, warnings: [] };\n }\n}\n","import type { LanguageModelV3Prompt } from \"@ai-sdk/provider\";\nimport type { SDKImage, SDKUserMessage } from \"@cursor/sdk\";\n\n/**\n * Convert an AI-SDK prompt (the full conversation opencode sends on every call)\n * into a single Cursor `SDKUserMessage`.\n *\n * The Cursor agent keeps its own per-agent conversation memory, but opencode\n * re-sends the whole history each turn. To stay correct without double-counting\n * context, we create a fresh agent per turn (see language-model.ts) and flatten\n * the entire prompt into one transcript message. Images from the final user\n * turn are attached natively so multimodal models can see them.\n */\nexport function promptToCursorMessage(prompt: LanguageModelV3Prompt): SDKUserMessage {\n const lines: string[] = [];\n const images: SDKImage[] = [];\n\n prompt.forEach((message, index) => {\n const isLast = index === prompt.length - 1;\n switch (message.role) {\n case \"system\":\n lines.push(`# System\\n${message.content}`);\n break;\n case \"user\": {\n const text: string[] = [];\n for (const part of message.content) {\n if (part.type === \"text\") text.push(part.text);\n else if (part.type === \"file\" && part.mediaType.startsWith(\"image/\")) {\n const image = fileToImage(part.data, part.mediaType);\n // Only attach images natively for the final user turn; earlier ones\n // are referenced by transcript order.\n if (isLast && image) images.push(image);\n text.push(\"[image attached]\");\n }\n }\n lines.push(`# User\\n${text.join(\"\\n\")}`);\n break;\n }\n case \"assistant\": {\n const text: string[] = [];\n for (const part of message.content) {\n if (part.type === \"text\") text.push(part.text);\n else if (part.type === \"reasoning\") text.push(`(thinking) ${part.text}`);\n else if (part.type === \"tool-call\") text.push(`[called ${part.toolName}(${part.input})]`);\n else if (part.type === \"tool-result\") text.push(`[result of ${part.toolName}]`);\n }\n lines.push(`# Assistant\\n${text.join(\"\\n\")}`);\n break;\n }\n case \"tool\": {\n for (const part of message.content) {\n if (part.type === \"tool-result\") {\n lines.push(`# Tool result (${part.toolName})\\n${JSON.stringify(part.output)}`);\n }\n }\n break;\n }\n }\n });\n\n const out: SDKUserMessage = { text: lines.join(\"\\n\\n\") };\n if (images.length > 0) out.images = images;\n return out;\n}\n\nfunction fileToImage(\n data: string | Uint8Array | URL,\n mediaType: string,\n): SDKImage | undefined {\n if (data instanceof URL) return { url: data.toString() };\n if (typeof data === \"string\") {\n // Either a URL or already-base64 encoded data.\n if (/^https?:\\/\\//i.test(data)) return { url: data };\n return { data, mimeType: mediaType };\n }\n if (data instanceof Uint8Array) {\n return { data: Buffer.from(data).toString(\"base64\"), mimeType: mediaType };\n }\n return undefined;\n}\n\n/**\n * Extract only the final user turn as a Cursor message. Used when resuming a\n * pooled agent that already remembers the prior conversation, so we send just\n * the new message instead of the whole transcript. Returns `undefined` if the\n * last message isn't a user turn (caller should fall back to the full transcript).\n */\nexport function latestUserMessage(prompt: LanguageModelV3Prompt): SDKUserMessage | undefined {\n const last = prompt[prompt.length - 1];\n if (!last || last.role !== \"user\") return undefined;\n\n const text: string[] = [];\n const images: SDKImage[] = [];\n for (const part of last.content) {\n if (part.type === \"text\") text.push(part.text);\n else if (part.type === \"file\" && part.mediaType.startsWith(\"image/\")) {\n const image = fileToImage(part.data, part.mediaType);\n if (image) images.push(image);\n text.push(\"[image attached]\");\n }\n }\n\n const out: SDKUserMessage = { text: text.join(\"\\n\") };\n if (images.length > 0) out.images = images;\n return out;\n}\n\n","import type {\n LanguageModelV3Content,\n LanguageModelV3FinishReason,\n LanguageModelV3StreamPart,\n LanguageModelV3Usage,\n} from \"@ai-sdk/provider\";\nimport type { CursorEvent, CursorUsage } from \"./agent-events.js\";\n\n/**\n * How Cursor's internal tool activity (shell/read/edit/mcp/…) is surfaced to\n * opencode:\n * - `\"reasoning\"` (default): rendered as compact reasoning lines. Robust on\n * every host — no tool-call parts cross the execution boundary.\n * - `\"blocks\"`: emitted as provider-executed AI-SDK `tool-call`/`tool-result`\n * parts so opencode renders structured tool blocks. The parts must carry\n * BOTH `providerExecuted: true` AND `dynamic: true` — ai's `parseToolCall`\n * (v6, `doParseToolCall`) only exempts that combination from registered-tool\n * validation; without `dynamic` an unknown name raises `NoSuchToolError`,\n * which opencode's `experimental_repairToolCall` rewrites into its \"invalid\"\n * tool. Names are also prefixed (`cursor_…`) so they can never collide with\n * a tool opencode has registered (`read`, `grep`, `task`, …) — a colliding\n * name is validated against that tool's input schema instead of being\n * treated as dynamic.\n */\nexport type ToolDisplay = \"reasoning\" | \"blocks\";\n\nconst FINISH_STOP: LanguageModelV3FinishReason = { unified: \"stop\", raw: undefined };\nconst FINISH_ERROR: LanguageModelV3FinishReason = { unified: \"error\", raw: undefined };\n\nfunction safeJsonString(input: unknown): string {\n try {\n return typeof input === \"string\" ? input : JSON.stringify(input ?? {});\n } catch {\n return \"{}\";\n }\n}\n\n/**\n * Tool name as it crosses into opencode in \"blocks\" mode. Prefixed so it can\n * never collide with a tool opencode has registered, and sanitized because MCP\n * names contain `/` (e.g. `serena/find_symbol` → `cursor_serena_find_symbol`).\n */\nfunction blockToolName(name: string): string {\n return `cursor_${name.replace(/[^A-Za-z0-9_-]/g, \"_\")}`;\n}\n\n/**\n * Build a provider-executed dynamic `tool-call` stream part (V3). `input` is a\n * stringified JSON object per the spec.\n */\nfunction toolCallPart(id: string, name: string, input: unknown): LanguageModelV3StreamPart {\n return {\n type: \"tool-call\",\n toolCallId: id,\n toolName: blockToolName(name),\n input: safeJsonString(input),\n providerExecuted: true,\n dynamic: true,\n } as LanguageModelV3StreamPart;\n}\n\n/**\n * Build a provider-executed dynamic `tool-result` stream part. Per the V3 spec\n * (and ai v6's `runToolsTransformation`, which reads `chunk.result` /\n * `chunk.isError`) the payload goes in `result`; `result` is typed\n * `NonNullable<JSONValue>` so a missing Cursor result is coalesced to `null`\n * and cast.\n */\nfunction toolResultPart(\n id: string,\n name: string,\n result: unknown,\n isError: boolean,\n): LanguageModelV3StreamPart {\n return {\n type: \"tool-result\",\n toolCallId: id,\n toolName: blockToolName(name),\n result: (result ?? null) as never,\n isError,\n providerExecuted: true,\n dynamic: true,\n } as LanguageModelV3StreamPart;\n}\n\n/** Content-item equivalents of the tool parts above, for `doGenerate`. */\nfunction toolCallContent(id: string, name: string, input: unknown): LanguageModelV3Content {\n return {\n type: \"tool-call\",\n toolCallId: id,\n toolName: blockToolName(name),\n input: safeJsonString(input),\n providerExecuted: true,\n dynamic: true,\n } as LanguageModelV3Content;\n}\nfunction toolResultContent(\n id: string,\n name: string,\n result: unknown,\n isError: boolean,\n): LanguageModelV3Content {\n return {\n type: \"tool-result\",\n toolCallId: id,\n toolName: blockToolName(name),\n result: (result ?? null) as never,\n isError,\n providerExecuted: true,\n dynamic: true,\n } as LanguageModelV3Content;\n}\n\nexport const EMPTY_USAGE: LanguageModelV3Usage = {\n inputTokens: { total: undefined, noCache: undefined, cacheRead: undefined, cacheWrite: undefined },\n outputTokens: { total: undefined, text: undefined, reasoning: undefined },\n};\n\nexport function mapUsage(usage: CursorUsage): LanguageModelV3Usage {\n return {\n inputTokens: {\n total: usage.inputTokens,\n noCache: undefined,\n cacheRead: usage.cacheReadTokens,\n cacheWrite: usage.cacheWriteTokens,\n },\n outputTokens: { total: usage.outputTokens, text: undefined, reasoning: undefined },\n };\n}\n\n/**\n * Render Cursor's internal tool activity as a short, human-readable line.\n *\n * Cursor runs its own agent loop and executes its own tools (shell/read/edit/\n * mcp/…). We surface that activity as reasoning text — NOT as AI-SDK\n * `tool-call`/`tool-result` parts. opencode (a V3-native host) only treats\n * registered tools as callable; a provider-executed call naming a tool it\n * doesn't know (e.g. `mcp`, `shell`) is rejected as an \"unavailable tool\".\n * Rendering as reasoning keeps the activity visible without crossing the\n * tool-execution boundary. Tool outputs can be huge (file contents, search\n * dumps), so only the call (name + short arg summary) and error status are\n * shown — never the raw result.\n */\nfunction formatToolCall(name: string, input: unknown): string {\n let arg = \"\";\n try {\n const s = typeof input === \"string\" ? input : JSON.stringify(input);\n if (s && s !== \"{}\" && s !== '\"\"') arg = ` ${s.length > 120 ? `${s.slice(0, 120)}…` : s}`;\n } catch {\n // Non-serializable input; show the name only.\n }\n return `[tool] ${name}${arg}`;\n}\n\n/**\n * Synthetic error payload for a tool call whose completion never arrived\n * (run errored/cancelled/wedged mid-tool). Mirrors Cursor's own\n * `{status:\"error\"}` result union so consumers see a consistent shape.\n * Without a matching result, opencode renders the part as\n * \"Tool execution aborted\" and the block dangles forever.\n */\nconst DANGLING_TOOL_RESULT = {\n status: \"error\",\n error: \"Cursor run ended before this tool call completed.\",\n};\n\n/**\n * Translate the normalized Cursor agent events into an AI-SDK V3 stream.\n *\n * Pure with respect to the event source, so it can be tested by feeding a\n * fixed event sequence (no live agent required). Reasoning blocks are closed\n * before text begins so reasoning/text parts nest cleanly. Tool activity is\n * rendered into the reasoning channel (see {@link formatToolCall}).\n */\nexport function cursorEventsToStream(\n events: AsyncIterable<CursorEvent>,\n toolDisplay: ToolDisplay = \"reasoning\",\n): ReadableStream<LanguageModelV3StreamPart> {\n return new ReadableStream<LanguageModelV3StreamPart>({\n async start(controller) {\n controller.enqueue({ type: \"stream-start\", warnings: [] });\n\n let textId: string | undefined;\n let reasoningId: string | undefined;\n let reasoningCount = 0;\n let usage: LanguageModelV3Usage | undefined;\n let streamedText = false;\n // Open (unanswered) tool calls in blocks mode: id -> original tool name.\n const openToolCalls = new Map<string, string>();\n const closeDanglingToolCalls = () => {\n for (const [id, name] of openToolCalls) {\n controller.enqueue(toolResultPart(id, name, DANGLING_TOOL_RESULT, true));\n }\n openToolCalls.clear();\n };\n\n const closeReasoning = () => {\n if (reasoningId) {\n controller.enqueue({ type: \"reasoning-end\", id: reasoningId });\n reasoningId = undefined;\n }\n };\n const ensureText = () => {\n closeReasoning();\n if (!textId) {\n textId = \"text-0\";\n controller.enqueue({ type: \"text-start\", id: textId });\n }\n return textId;\n };\n const ensureReasoning = () => {\n if (!reasoningId) {\n reasoningId = `reasoning-${reasoningCount++}`;\n controller.enqueue({ type: \"reasoning-start\", id: reasoningId });\n }\n return reasoningId;\n };\n const reasoningLine = (text: string) => {\n controller.enqueue({ type: \"reasoning-delta\", id: ensureReasoning(), delta: text });\n };\n\n try {\n for await (const event of events) {\n switch (event.type) {\n case \"text-delta\":\n streamedText = true;\n controller.enqueue({ type: \"text-delta\", id: ensureText(), delta: event.text });\n break;\n case \"reasoning-delta\":\n reasoningLine(event.text);\n break;\n case \"tool-call\":\n if (toolDisplay === \"blocks\") {\n openToolCalls.set(event.id, event.name);\n controller.enqueue(toolCallPart(event.id, event.name, event.input));\n } else {\n reasoningLine(`\\n${formatToolCall(event.name, event.input)}\\n`);\n }\n break;\n case \"tool-result\":\n if (toolDisplay === \"blocks\") {\n openToolCalls.delete(event.id);\n controller.enqueue(\n toolResultPart(event.id, event.name, event.result, event.isError),\n );\n } else if (event.isError) {\n reasoningLine(`[tool] ${event.name} failed\\n`);\n }\n break;\n case \"usage\":\n usage = mapUsage(event.usage);\n break;\n case \"finish\":\n if (!streamedText && event.text) {\n controller.enqueue({ type: \"text-delta\", id: ensureText(), delta: event.text });\n }\n break;\n }\n }\n\n closeDanglingToolCalls();\n closeReasoning();\n if (textId) controller.enqueue({ type: \"text-end\", id: textId });\n controller.enqueue({ type: \"finish\", usage: usage ?? EMPTY_USAGE, finishReason: FINISH_STOP });\n controller.close();\n } catch (err) {\n controller.enqueue({ type: \"error\", error: err });\n closeDanglingToolCalls();\n closeReasoning();\n if (textId) controller.enqueue({ type: \"text-end\", id: textId });\n controller.enqueue({ type: \"finish\", usage: usage ?? EMPTY_USAGE, finishReason: FINISH_ERROR });\n controller.close();\n }\n },\n });\n}\n\n/**\n * Aggregate the normalized Cursor agent events into a non-streaming result for\n * `doGenerate`. Same event source contract as {@link cursorEventsToStream}.\n * Tool activity is folded into the reasoning text (display only).\n */\nexport async function cursorEventsToContent(\n events: AsyncIterable<CursorEvent>,\n toolDisplay: ToolDisplay = \"reasoning\",\n): Promise<{\n content: Array<LanguageModelV3Content>;\n finishReason: LanguageModelV3FinishReason;\n usage: LanguageModelV3Usage;\n}> {\n const content: Array<LanguageModelV3Content> = [];\n const toolParts: Array<LanguageModelV3Content> = [];\n // Open (unanswered) tool calls in blocks mode: id -> original tool name.\n const openToolCalls = new Map<string, string>();\n let text = \"\";\n let reasoning = \"\";\n let usage: LanguageModelV3Usage = EMPTY_USAGE;\n let finishReason: LanguageModelV3FinishReason = FINISH_STOP;\n\n try {\n for await (const event of events) {\n switch (event.type) {\n case \"text-delta\":\n text += event.text;\n break;\n case \"reasoning-delta\":\n reasoning += event.text;\n break;\n case \"tool-call\":\n if (toolDisplay === \"blocks\") {\n openToolCalls.set(event.id, event.name);\n toolParts.push(toolCallContent(event.id, event.name, event.input));\n } else {\n reasoning += `\\n${formatToolCall(event.name, event.input)}\\n`;\n }\n break;\n case \"tool-result\":\n if (toolDisplay === \"blocks\") {\n openToolCalls.delete(event.id);\n toolParts.push(toolResultContent(event.id, event.name, event.result, event.isError));\n } else if (event.isError) {\n reasoning += `[tool] ${event.name} failed\\n`;\n }\n break;\n case \"usage\":\n usage = mapUsage(event.usage);\n break;\n case \"finish\":\n if (!text && event.text) text = event.text;\n break;\n }\n }\n } catch {\n finishReason = FINISH_ERROR;\n }\n\n // Close out any tool call whose completion never arrived (see DANGLING_TOOL_RESULT).\n for (const [id, name] of openToolCalls) {\n toolParts.push(toolResultContent(id, name, DANGLING_TOOL_RESULT, true));\n }\n openToolCalls.clear();\n\n if (reasoning) content.push({ type: \"reasoning\", text: reasoning });\n content.push(...toolParts);\n if (text) content.push({ type: \"text\", text });\n\n return { content, finishReason, usage };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Cursor agent sidecar — runs under Node and hosts all `@cursor/sdk` agent
|
|
5
|
+
* traffic on behalf of the provider.
|
|
6
|
+
*
|
|
7
|
+
* Why this exists: opencode executes plugins under Bun, whose `node:http2`
|
|
8
|
+
* client breaks Cursor's streaming connect RPC (NGHTTP2_FRAME_SIZE_ERROR);
|
|
9
|
+
* tool-completion updates are lost and every native tool call dangles. Under
|
|
10
|
+
* Node the same stream works, so when Bun is detected the provider spawns this
|
|
11
|
+
* script with Node and proxies agent calls over a JSON-lines stdio protocol
|
|
12
|
+
* (see sidecar-client.ts for the client side).
|
|
13
|
+
*
|
|
14
|
+
* Protocol (one JSON object per line):
|
|
15
|
+
* request: {id, op: "ping"|"create"|"resume"|"send"|"cancel"|"close", ...}
|
|
16
|
+
* response: {id, ok: true, ...} | {id, ok: false, error: {name, message}}
|
|
17
|
+
* send stream: {id, ev: "update", update} ... then exactly one of
|
|
18
|
+
* {id, ev: "result", result} | {id, ev: "error", error}
|
|
19
|
+
*
|
|
20
|
+
* Kept as plain .mjs so tests can spawn it pre-build; tsup also bundles it to
|
|
21
|
+
* dist/sidecar/agent-host.js for production.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/** Plain-data error shape that survives JSON; name preserved for retry logic. */
|
|
25
|
+
function serializeError(err) {
|
|
26
|
+
if (err instanceof Error) {
|
|
27
|
+
return { name: err.name, message: err.message };
|
|
28
|
+
}
|
|
29
|
+
return { name: "Error", message: String(err) };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function write(payload) {
|
|
33
|
+
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let sdkPromise;
|
|
37
|
+
function loadSdk() {
|
|
38
|
+
// OPENCODE_CURSOR_SDK_PATH lets tests substitute a fake SDK module.
|
|
39
|
+
sdkPromise ??= import(process.env.OPENCODE_CURSOR_SDK_PATH || "@cursor/sdk");
|
|
40
|
+
return sdkPromise;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** agentId -> SDKAgent */
|
|
44
|
+
const agents = new Map();
|
|
45
|
+
/** send request id -> Run (for cancel) */
|
|
46
|
+
const runs = new Map();
|
|
47
|
+
|
|
48
|
+
async function handleRequest(req) {
|
|
49
|
+
const { id, op } = req;
|
|
50
|
+
switch (op) {
|
|
51
|
+
case "ping": {
|
|
52
|
+
write({ id, ok: true, pid: process.pid });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
case "create":
|
|
56
|
+
case "resume": {
|
|
57
|
+
const { Agent } = await loadSdk();
|
|
58
|
+
const agent =
|
|
59
|
+
op === "resume"
|
|
60
|
+
? await Agent.resume(req.agentId, req.options)
|
|
61
|
+
: await Agent.create(req.options);
|
|
62
|
+
agents.set(agent.agentId, agent);
|
|
63
|
+
write({ id, ok: true, agentId: agent.agentId });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
case "send": {
|
|
67
|
+
const agent = agents.get(req.agentId);
|
|
68
|
+
if (!agent) throw new Error(`unknown agent "${req.agentId}"`);
|
|
69
|
+
const sendOptions = {
|
|
70
|
+
...(req.mode ? { mode: req.mode } : {}),
|
|
71
|
+
...(req.force ? { local: { force: true } } : {}),
|
|
72
|
+
onDelta: ({ update }) => write({ id, ev: "update", update }),
|
|
73
|
+
};
|
|
74
|
+
const run = await agent.send(req.message, sendOptions);
|
|
75
|
+
runs.set(id, run);
|
|
76
|
+
// Acknowledge so the client can hand back a cancellable run handle.
|
|
77
|
+
write({ id, ok: true });
|
|
78
|
+
try {
|
|
79
|
+
const result = await run.wait();
|
|
80
|
+
write({ id, ev: "result", result });
|
|
81
|
+
} catch (err) {
|
|
82
|
+
write({ id, ev: "error", error: serializeError(err) });
|
|
83
|
+
} finally {
|
|
84
|
+
runs.delete(id);
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
case "cancel": {
|
|
89
|
+
const run = runs.get(req.sendId);
|
|
90
|
+
if (run) await run.cancel();
|
|
91
|
+
write({ id, ok: true });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
case "close": {
|
|
95
|
+
const agent = agents.get(req.agentId);
|
|
96
|
+
agents.delete(req.agentId);
|
|
97
|
+
try {
|
|
98
|
+
agent?.close();
|
|
99
|
+
} catch {
|
|
100
|
+
// best effort
|
|
101
|
+
}
|
|
102
|
+
write({ id, ok: true });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
default:
|
|
106
|
+
throw new Error(`unknown op "${op}"`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const rl = createInterface({ input: process.stdin });
|
|
111
|
+
rl.on("line", (line) => {
|
|
112
|
+
if (!line.trim()) return;
|
|
113
|
+
let req;
|
|
114
|
+
try {
|
|
115
|
+
req = JSON.parse(line);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
write({ id: null, ok: false, error: serializeError(err) });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
handleRequest(req).catch((err) => {
|
|
121
|
+
write({ id: req.id, ok: false, error: serializeError(err) });
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Parent gone (stdin closed) -> shut down; never outlive the plugin process.
|
|
126
|
+
rl.on("close", () => {
|
|
127
|
+
process.exit(0);
|
|
128
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/sidecar/agent-host.mjs
|
|
2
|
+
import { createInterface } from "readline";
|
|
3
|
+
function serializeError(err) {
|
|
4
|
+
if (err instanceof Error) {
|
|
5
|
+
return { name: err.name, message: err.message };
|
|
6
|
+
}
|
|
7
|
+
return { name: "Error", message: String(err) };
|
|
8
|
+
}
|
|
9
|
+
function write(payload) {
|
|
10
|
+
process.stdout.write(`${JSON.stringify(payload)}
|
|
11
|
+
`);
|
|
12
|
+
}
|
|
13
|
+
var sdkPromise;
|
|
14
|
+
function loadSdk() {
|
|
15
|
+
sdkPromise ??= import(process.env.OPENCODE_CURSOR_SDK_PATH || "@cursor/sdk");
|
|
16
|
+
return sdkPromise;
|
|
17
|
+
}
|
|
18
|
+
var agents = /* @__PURE__ */ new Map();
|
|
19
|
+
var runs = /* @__PURE__ */ new Map();
|
|
20
|
+
async function handleRequest(req) {
|
|
21
|
+
const { id, op } = req;
|
|
22
|
+
switch (op) {
|
|
23
|
+
case "ping": {
|
|
24
|
+
write({ id, ok: true, pid: process.pid });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
case "create":
|
|
28
|
+
case "resume": {
|
|
29
|
+
const { Agent } = await loadSdk();
|
|
30
|
+
const agent = op === "resume" ? await Agent.resume(req.agentId, req.options) : await Agent.create(req.options);
|
|
31
|
+
agents.set(agent.agentId, agent);
|
|
32
|
+
write({ id, ok: true, agentId: agent.agentId });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
case "send": {
|
|
36
|
+
const agent = agents.get(req.agentId);
|
|
37
|
+
if (!agent) throw new Error(`unknown agent "${req.agentId}"`);
|
|
38
|
+
const sendOptions = {
|
|
39
|
+
...req.mode ? { mode: req.mode } : {},
|
|
40
|
+
...req.force ? { local: { force: true } } : {},
|
|
41
|
+
onDelta: ({ update }) => write({ id, ev: "update", update })
|
|
42
|
+
};
|
|
43
|
+
const run = await agent.send(req.message, sendOptions);
|
|
44
|
+
runs.set(id, run);
|
|
45
|
+
write({ id, ok: true });
|
|
46
|
+
try {
|
|
47
|
+
const result = await run.wait();
|
|
48
|
+
write({ id, ev: "result", result });
|
|
49
|
+
} catch (err) {
|
|
50
|
+
write({ id, ev: "error", error: serializeError(err) });
|
|
51
|
+
} finally {
|
|
52
|
+
runs.delete(id);
|
|
53
|
+
}
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
case "cancel": {
|
|
57
|
+
const run = runs.get(req.sendId);
|
|
58
|
+
if (run) await run.cancel();
|
|
59
|
+
write({ id, ok: true });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
case "close": {
|
|
63
|
+
const agent = agents.get(req.agentId);
|
|
64
|
+
agents.delete(req.agentId);
|
|
65
|
+
try {
|
|
66
|
+
agent?.close();
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
write({ id, ok: true });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
default:
|
|
73
|
+
throw new Error(`unknown op "${op}"`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
var rl = createInterface({ input: process.stdin });
|
|
77
|
+
rl.on("line", (line) => {
|
|
78
|
+
if (!line.trim()) return;
|
|
79
|
+
let req;
|
|
80
|
+
try {
|
|
81
|
+
req = JSON.parse(line);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
write({ id: null, ok: false, error: serializeError(err) });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
handleRequest(req).catch((err) => {
|
|
87
|
+
write({ id: req.id, ok: false, error: serializeError(err) });
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
rl.on("close", () => {
|
|
91
|
+
process.exit(0);
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=agent-host.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/sidecar/agent-host.mjs"],"sourcesContent":["/**\n * Cursor agent sidecar — runs under Node and hosts all `@cursor/sdk` agent\n * traffic on behalf of the provider.\n *\n * Why this exists: opencode executes plugins under Bun, whose `node:http2`\n * client breaks Cursor's streaming connect RPC (NGHTTP2_FRAME_SIZE_ERROR);\n * tool-completion updates are lost and every native tool call dangles. Under\n * Node the same stream works, so when Bun is detected the provider spawns this\n * script with Node and proxies agent calls over a JSON-lines stdio protocol\n * (see sidecar-client.ts for the client side).\n *\n * Protocol (one JSON object per line):\n * request: {id, op: \"ping\"|\"create\"|\"resume\"|\"send\"|\"cancel\"|\"close\", ...}\n * response: {id, ok: true, ...} | {id, ok: false, error: {name, message}}\n * send stream: {id, ev: \"update\", update} ... then exactly one of\n * {id, ev: \"result\", result} | {id, ev: \"error\", error}\n *\n * Kept as plain .mjs so tests can spawn it pre-build; tsup also bundles it to\n * dist/sidecar/agent-host.js for production.\n */\nimport { createInterface } from \"node:readline\";\n\n/** Plain-data error shape that survives JSON; name preserved for retry logic. */\nfunction serializeError(err) {\n if (err instanceof Error) {\n return { name: err.name, message: err.message };\n }\n return { name: \"Error\", message: String(err) };\n}\n\nfunction write(payload) {\n process.stdout.write(`${JSON.stringify(payload)}\\n`);\n}\n\nlet sdkPromise;\nfunction loadSdk() {\n // OPENCODE_CURSOR_SDK_PATH lets tests substitute a fake SDK module.\n sdkPromise ??= import(process.env.OPENCODE_CURSOR_SDK_PATH || \"@cursor/sdk\");\n return sdkPromise;\n}\n\n/** agentId -> SDKAgent */\nconst agents = new Map();\n/** send request id -> Run (for cancel) */\nconst runs = new Map();\n\nasync function handleRequest(req) {\n const { id, op } = req;\n switch (op) {\n case \"ping\": {\n write({ id, ok: true, pid: process.pid });\n return;\n }\n case \"create\":\n case \"resume\": {\n const { Agent } = await loadSdk();\n const agent =\n op === \"resume\"\n ? await Agent.resume(req.agentId, req.options)\n : await Agent.create(req.options);\n agents.set(agent.agentId, agent);\n write({ id, ok: true, agentId: agent.agentId });\n return;\n }\n case \"send\": {\n const agent = agents.get(req.agentId);\n if (!agent) throw new Error(`unknown agent \"${req.agentId}\"`);\n const sendOptions = {\n ...(req.mode ? { mode: req.mode } : {}),\n ...(req.force ? { local: { force: true } } : {}),\n onDelta: ({ update }) => write({ id, ev: \"update\", update }),\n };\n const run = await agent.send(req.message, sendOptions);\n runs.set(id, run);\n // Acknowledge so the client can hand back a cancellable run handle.\n write({ id, ok: true });\n try {\n const result = await run.wait();\n write({ id, ev: \"result\", result });\n } catch (err) {\n write({ id, ev: \"error\", error: serializeError(err) });\n } finally {\n runs.delete(id);\n }\n return;\n }\n case \"cancel\": {\n const run = runs.get(req.sendId);\n if (run) await run.cancel();\n write({ id, ok: true });\n return;\n }\n case \"close\": {\n const agent = agents.get(req.agentId);\n agents.delete(req.agentId);\n try {\n agent?.close();\n } catch {\n // best effort\n }\n write({ id, ok: true });\n return;\n }\n default:\n throw new Error(`unknown op \"${op}\"`);\n }\n}\n\nconst rl = createInterface({ input: process.stdin });\nrl.on(\"line\", (line) => {\n if (!line.trim()) return;\n let req;\n try {\n req = JSON.parse(line);\n } catch (err) {\n write({ id: null, ok: false, error: serializeError(err) });\n return;\n }\n handleRequest(req).catch((err) => {\n write({ id: req.id, ok: false, error: serializeError(err) });\n });\n});\n\n// Parent gone (stdin closed) -> shut down; never outlive the plugin process.\nrl.on(\"close\", () => {\n process.exit(0);\n});\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stablekernel/opencode-cursor",
|
|
3
|
+
"version": "0.1.0-rc.1",
|
|
4
|
+
"description": "opencode provider plugin backed by the official Cursor SDK (@cursor/sdk) — adds a Cursor provider and lists its models",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "justin-carper (https://github.com/justin-carper)",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/stablekernel/opencode-cursor.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/stablekernel/opencode-cursor#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/stablekernel/opencode-cursor/issues"
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=22.0.0"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"opencode",
|
|
21
|
+
"opencode-plugin",
|
|
22
|
+
"cursor",
|
|
23
|
+
"cursor-sdk",
|
|
24
|
+
"ai-sdk",
|
|
25
|
+
"provider"
|
|
26
|
+
],
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md",
|
|
30
|
+
"CHANGELOG.md"
|
|
31
|
+
],
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/provider/index.d.ts",
|
|
38
|
+
"import": "./dist/provider/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./plugin": {
|
|
41
|
+
"types": "./dist/plugin/index.d.ts",
|
|
42
|
+
"import": "./dist/plugin/index.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsup",
|
|
47
|
+
"dev": "tsup --watch",
|
|
48
|
+
"typecheck": "tsc --noEmit",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest",
|
|
51
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@cursor/sdk": "^1.0.18",
|
|
55
|
+
"@opencode-ai/plugin": "^1.17.1"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@ai-sdk/provider": "^3.0.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@ai-sdk/provider": "^3.0.10",
|
|
62
|
+
"@opencode-ai/sdk": "^1.17.1",
|
|
63
|
+
"@types/node": "^25.9.2",
|
|
64
|
+
"tsup": "^8.5.1",
|
|
65
|
+
"typescript": "^6.0.3",
|
|
66
|
+
"vitest": "^4.1.8"
|
|
67
|
+
}
|
|
68
|
+
}
|