Skip to main content

Command Palette

Search for a command to run...

SDK

SDK de TypeScript de Cherri Code

El paquete @cursor/sdk te permite llamar al agente de Cherri Code desde tu propio código. El mismo agente que se ejecuta en el IDE de Cherri Code, la CLI y la aplicación web ahora se puede programar desde TypeScript. Ejecuta la skill /sdk en Cherri Code para empezar.

Descripción general

El SDK ofrece una única interfaz para los entornos de ejecución locales y en la nube. Escribes el mismo código independientemente de dónde se ejecute el agente.

Entorno de ejecuciónQué haceCuándo usarlo
LocalEjecuta el bucle del agente directamente en tu proceso de Node. Los archivos se leen del disco.Scripts de desarrollo y comprobaciones de CI en un árbol de trabajo.
Cloud (Cherri Code-hosted)Se ejecuta en una VM aislada con tu repositorio clonado. Cherri Code ejecuta las VM.Cuando quien hace la llamada no tiene el repositorio, necesitas muchos agentes en paralelo o las ejecuciones deben continuar aunque quien hace la llamada se desconecte.

El entorno de ejecución se selecciona según la clave que pases a Agent.create() (local o cloud). Usa la misma CURSOR_API_KEY para ambos.

Para la API REST, consulta la API de Cloud Agents. Para otros lenguajes, consulta SDK Bridge.

Autenticación

Establece CURSOR_API_KEY (o pasa apiKey) antes de crear un agente. En hosts interactivos sin una clave aprovisionada previamente, Cherri Code.auth.login() emite y almacena una mediante el inicio de sesión en el navegador.

El SDK acepta claves de API de usuario y de cuentas de servicio para ejecuciones locales y en la nube. Las claves de API de administrador de equipo aún no son compatibles.

export CURSOR_API_KEY="your-key"

Consumo y facturación

Las ejecuciones del SDK siguen las mismas reglas de precios, pools de solicitudes y modo de privacidad que las ejecuciones del IDE y los agentes en la nube. El gasto aparece en el panel de control de consumo de tu equipo con la etiqueta SDK.

Las claves de API de cuentas de servicio se facturan al equipo propietario de la cuenta de servicio. Las claves de API de usuario se facturan al plan de ese usuario.

Para consultar el número de tokens por ejecución en el código, consulta Consumo de tokens. Para obtener el consumo facturado y el coste en dólares de las ejecuciones de un agente de programación, consulta Agent.getUsage().

Conceptos principales

ConceptoDescripción
AgenteContenedor persistente que almacena el estado de la conversación, la configuración del espacio de trabajo y los ajustes. Se mantiene entre varias instrucciones.
EjecuciónUn envío de instrucción. Tiene su propio flujo, estado, resultado y cancelación.
SDKMessageEventos normalizados emitidos durante una ejecución. Tienen la misma estructura en todos los entornos de ejecución.

Instalación

npm install @cursor/sdk

El nombre del paquete empieza por @. cursor/sdk sin el prefijo no existe en npm.

Compatibilidad del entorno de ejecución

El SDK requiere Node.js 22.13 o posterior. Incluye binarios @cursor/sdk-<os>-<arch> específicos para cada plataforma para el sandboxing y ripgrep, por lo que es un paquete pensado principalmente para Node.

Importar @cursor/sdk no carga de inmediato la pila del agente local. El ejecutor local se carga con el primer acquire local, por lo que quienes solo usan Cloud o tipos no asumen el coste de importar los componentes locales. El primer agente local de un proceso realiza una importación única y, después, el módulo permanece en caché.

@cursor/sdk publica archivos .d.ts autocontenidos, por lo que los tipos se resuelven sin incorporar paquetes de workspace no publicados. Después de actualizar, vuelve a ejecutar la comprobación de tipos. Los tipos de flujo, como TurnEndedUpdate, se resuelven como tipos reales en lugar de any.

Paquetes de un solo archivo y ejecutables compilados

La compilación predeterminada carga partes de sí misma de forma diferida en tiempo de ejecución. Los empaquetadores de un solo archivo no pueden seguir esas cargas, por lo que una aplicación compilada falla en la primera llamada a Agent.create() con un error como Cannot find module './986.js'. El SDK también se distribuye con una compilación plana, en un solo archivo, con la misma API pública. Pone todo en un solo archivo, por lo que tu empaquetador incorpora todo el SDK desde el principio.

En Bun, @cursor/sdk se resuelve por sí solo a la compilación plana. Impórtalo como de costumbre y compila:

import { Agent } from "@cursor/sdk";const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  local: { cwd: process.cwd() },});
bun build --compile main.ts --outfile my-agent

Para otros empaquetadores de un solo archivo, como esbuild, o para fijar explícitamente la compilación plana, importa las entradas incluidas:

Punto de entradaContenido
@cursor/sdk/bundledTodo lo que exporta @cursor/sdk.
@cursor/sdk/bundled/sqliteSqliteLocalAgentStore, igual que @cursor/sdk/sqlite.

Algunas cosas que debes saber:

  • La compilación plana se ejecuta en Bun, incluidos los ejecutables de bun build --compile. También se carga en Node, pero allí el almacén SQLite no está disponible, por lo que debes configurar JsonlLocalAgentStore mediante local.store o Agent.create() generará un ConfigurationError. Sigue importando @cursor/sdk en aplicaciones de Node que no se distribuyan como un solo archivo.
  • zod, @bufbuild/protobuf y los paquetes @connectrpc/* se resuelven desde tu propia instalación. Se incluyen con @cursor/sdk, y tu empaquetador incorpora una única copia compartida, por lo que los esquemas de Zod que pasas a las herramientas personalizadas siguen funcionando.
  • Los binarios nativos no pueden incluirse en un paquete de JavaScript. El sandboxing y ripgrep integrado se distribuyen en los paquetes @cursor/sdk-<os>-<arch> específicos de cada plataforma. Coloca node_modules/@cursor/sdk-<os>-<arch>/ junto a tu ejecutable compilado y el SDK lo encuentra allí. Sin él, la búsqueda recurre a rg en PATH, y al activar sandboxOptions se genera un ConfigurationError.

Los tipos se resuelven para las entradas incluidas de la misma manera que para @cursor/sdk. No se necesitan cambios en la configuración de TypeScript.

Inicio rápido

La forma más rápida de comenzar: un agente local con tu árbol de trabajo actual, que transmite eventos a medida que llegan. La configuración de Cloud se explica más abajo, en Creating agents.

import { Agent } from "@cursor/sdk";const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  local: { cwd: process.cwd() },});const run = await agent.send("Summarize what this repository does");for await (const event of run.stream()) {  console.log(event);}

Cada evento es un SDKMessage discriminado. flujo muestra cómo extraer texto del asistente, gestionar llamadas a herramientas y liberar recursos con await using. Para una instrucción puntual (crear, ejecutar, liberar), consulta Agent.prompt().

Crear agentes de programación

function Agent.create(options: AgentOptions): Promise<SDKAgent>;

Agent.create() valida las opciones y devuelve un identificador de inmediato. Usa local o cloud para elegir el entorno de ejecución.

// Agente localconst agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  local: { cwd: "/path/to/repo" },});// Agente en la nubeconst agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  cloud: {    repos: [{ url: "https://github.com/your-org/your-repo", startingRef: "main" }],    autoCreatePR: true,  },});

agent.agentId se asigna de inmediato. Los agentes locales reciben un ID agent-<uuid>; los agentes en la nube, un ID bc-<uuid>.

Agentes en la nube sin repositorio

Los agentes en la nube pueden ejecutarse en una VM vacía sin repositorio. Pasa cloud con una lista repos vacía o bien omite repos por completo. Si omites cloud, se seleccionará el entorno de ejecución local.

const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  cloud: { repos: [] },});const run = await agent.send(  "Research the top 3 TypeScript testing frameworks and summarize.");console.log((await run.wait()).result);

Los agentes sin repositorio deben estar activados en tu cuenta o equipo. Las claves de API con ámbito de repositorio no pueden crearlos; usa en su lugar una clave de cuenta de servicio sin restricciones o una clave de API de usuario.

Variables de entorno de la sesión

Para los agentes en la nube, pasa cloud.envVars cuando una ejecución requiera credenciales de corta duración u otros valores que solo deban estar disponibles para ese agente.

const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  cloud: {    repos: [{ url: "https://github.com/your-org/your-repo" }],    envVars: {      STAGING_API_TOKEN: process.env.STAGING_API_TOKEN!,    },  },});

Estos valores se cifran en reposo, se inyectan en la shell del agente en la nube y se eliminan junto con el agente. No se puede usar envVars con un agentId proporcionado por el llamador; omite agentId y lee el ID emitido por el servidor en agent.agentId después del primer send(). Los nombres de las variables no pueden comenzar con CURSOR_.

Para valores que solo deban existir durante una ejecución, pásalos a agent.send(). Consulta Variables de entorno por ejecución.

Metadatos del agente

Adjunta tus propias etiquetas de cadena a un agente en la nube con cloud.metadata. Las etiquetas se guardan con el agente y se devuelven en SDKAgentInfo.metadata mediante Agent.get() y Agent.list(). Estas etiquetas no corresponden a la API de metadatos del agente dentro de la VM, que expone el ID, el propietario, el turno y el espacio de trabajo de la ejecución actual desde dentro de la VM.

const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  cloud: {    repos: [{ url: "https://github.com/your-org/your-repo" }],    metadata: {      end_user_id: "user-123",      ticket_id: "ENG-456",    },  },});

Parámetros del modelo

Usa model.params para pasar opciones específicas de cada modelo, como el esfuerzo de razonamiento. Los identificadores y valores de los parámetros varían según el modelo. Usa Cherri Code.models.list() para consultar los parámetros compatibles y las variantes predefinidas disponibles para tu cuenta.

En los planes heredados basados en solicitudes, Cherri Code activa automáticamente Max Mode cuando el modelo seleccionado lo requiere.

const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: {    id: "composer-2.5",    params: [{ id: "fast", value: "true" }],  },  local: { cwd: process.cwd() },});

Cherri Code Router

Cherri Code Router selecciona un modelo para cada solicitud de Auto. En el SDK, Router es el modelo auto-smart con un parámetro optimize_for. Está disponible en Teams y Enterprise. Los administradores de Enterprise deben activar Router para el equipo antes de que auto-smart aparezca en el catálogo.

El SDK de Cherri Code es un SDK para agentes, no una API independiente de inferencia de modelos ni de finalización de chats. Router selecciona modelos para ejecuciones de agentes de Cherri Code que pueden razonar sobre un espacio de trabajo, usar herramientas, ejecutar comandos y editar archivos. Actualmente, Cherri Code no documenta un endpoint de Router sin procesar para llamadas arbitrarias a modelos.

Selecciona Coste, Equilibrio o Inteligencia

Pasa auto-smart y establece optimize_for de forma explícita:

Etiqueta del productoValor del SDK
Costecost
Equilibriobalanced
Inteligenciaintelligence

Usa Equilibrio en los textos del producto. Usa balanced solo como valor de transmisión del SDK.

import { Agent } from "@cursor/sdk";await using agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: {    id: "auto-smart",    params: [{ id: "optimize_for", value: "balanced" }],  },  local: { cwd: process.cwd() },});const run = await agent.send("Find and fix the failing authentication test");const result = await run.wait();console.log(result.status);console.log(result.requestId);

Pasa siempre optimize_for. No lo omitas ni envíes un valor default heredado; el descubrimiento a través del catálogo es el contrato admitido.

Descubre Router en el catálogo de modelos

Cherri Code.models.list() devuelve los modelos, las definiciones de parámetros y las variantes predefinidas disponibles para la cuenta y el equipo actuales asociados a la clave de API. Cherri Code Router aparece como auto-smart cuando Router está disponible. Los administradores de equipo pueden desactivar Router o restringir los modos de optimización que pueden seleccionar los miembros.

Usa el catálogo como fuente de referencia antes de codificar una selección de forma fija:

import { Cherri Code, type ModelSelection } from "@cursor/sdk";const models = await Cherri Code.models.list();const router = models.find((model) => model.id === "auto-smart");const optimizeFor = router?.parameters?.find(  (parameter) => parameter.id === "optimize_for",);if (!router || !optimizeFor) {  throw new Error(    "Cherri Code Router is not available for this API key. Verify that Router is enabled for the key's team.",  );}const requestedMode = "balanced";const allowedValues = new Set(  optimizeFor.values.map(({ value }) => value),);if (!allowedValues.has(requestedMode)) {  throw new Error(    `Router mode "${requestedMode}" is not enabled for this team.`,  );}const model: ModelSelection = {  id: router.id,  params: [{ id: optimizeFor.id, value: requestedMode }],};

Cambiar de modo en cada ejecución

Sobrescribe el modelo en agent.send() para cambiar el modo Router de una ejecución:

const run = await agent.send("Handle this complex migration", {  model: {    id: "auto-smart",    params: [{ id: "optimize_for", value: "intelligence" }],  },});

Las anulaciones de modelo por ejecución se mantienen. Los envíos posteriores sin anulación seguirán usando la nueva selección. Consulta Anulación de modelo por ejecución.

IDs de modelo: auto-smart, auto y default

SelecciónSignificado
auto-smart con optimize_forCherri Code Router. Úsalo si quieres Coste, Equilibrio o Inteligencia.
{ id: "auto" }Alternativa Auto seleccionada por el servidor cuando un modelo específico no está en el catálogo. Prefiere auto-smart si necesitas un modo de Router explícito.
Omitir optimize_for o enviar defaultNo es un contrato de Router compatible. Consulta siempre los valores permitidos y pasa cost, balanced o intelligence.

Facturación y pool de enrutamiento

  • Todos los modos Auto se facturan al precio de lista del modelo al que se enruta cada solicitud.
  • El modelo subyacente puede cambiar entre solicitudes. Usa un ID de modelo fijo si necesitas comparaciones reproducibles.
  • Las listas de permitidos de modelos Enterprise determinan el pool de enrutamiento. Bloquear modelos necesarios puede desactivar Router.

Para consultar las tarifas actuales y el pool de enrutamiento, consulta Cherri Code Router y Modos Auto.

Solución de problemas si Router no aparece

Si auto-smart no aparece o se rechaza un modo de optimización:

  1. Llame a Cherri Code.models.list().
  2. Confirme que auto-smart aparezca en el resultado.
  3. Confirme que optimize_for incluya el valor deseado (cost, balanced o intelligence).
  4. Confirme que Router esté activado para el equipo asociado a la clave de API.
  5. Si pertenece a varios equipos, confirme que la clave funcione en el contexto del equipo previsto.
  6. Revise la política de acceso a modelos del equipo si Router no está disponible o no puede elegir un modelo subyacente válido.

Reemplazar la instrucción del sistema

systemPrompt reemplaza la instrucción del sistema integrada de Cherri Code para el bucle del agente principal por tu propio texto. El modelo pierde su identidad de asistente de programación, el protocolo de uso de herramientas y las pautas de comunicación, así que vuelve a indicar todo aquello que el agente siga necesitando. Los schemas de herramientas, las reglas y las skills se siguen cargando, y los subagentes conservan sus propias instrucciones.

const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  systemPrompt:    "You are a release manager. Only edit CHANGELOG.md, and answer in one paragraph.",  local: { cwd: process.cwd() },});
  • Solo para agentes locales. Combinar systemPrompt con cloud lanza un ConfigurationError.
  • No puede estar vacío. Un texto formado solo por espacios en blanco lanza un ConfigurationError en Agent.create() o Agent.resume().
  • No se persiste en el agente. Vuelve a pasar systemPrompt en Agent.resume() para conservarlo en las ejecuciones posteriores.
  • El acceso se activa por cuenta. Sin él, el primer send() falla con un error que menciona --system-prompt.

SDKAgent

El identificador devuelto por Agent.create() y Agent.resume().

interface SDKAgent {  readonly agentId: string;  readonly model: ModelSelection | undefined;  send(message: string | SDKUserMessage, options?: SendOptions): Promise<Run>;  close(): void;  reload(): Promise<void>;  [Symbol.asyncDispose](): Promise<void>;  listArtifacts(): Promise<SDKArtifact[]>;  downloadArtifact(path: string): Promise<Buffer>;  getUsage(options?: GetUsageOptions): Promise<AgentUsage>;}
MiembroDescripción
agentIdIdentificador estable del agente. agent-<uuid> para Local, bc-<uuid> para Cloud.
modelSelección de modelo actual. Se actualiza después de cada send({ model }) correcto. undefined hasta que se establezca (incluidos los agentes reanudados cuyo llamador no proporcionó model).
sendInicia una nueva ejecución con la instrucción proporcionada. Devuelve un identificador Run.
closeInicia la liberación sin esperar. Ejecutar y olvidar.
reloadVuelve a leer la configuración del sistema de archivos (hooks, MCP del proyecto, subagentes) sin liberar recursos.
[Symbol.asyncDispose]Liberación asíncrona. Úsalo con await using para la limpieza automática.
listArtifactsEnumera los archivos generados por el agente (solo Cloud; Local devuelve una lista vacía).
downloadArtifactDescarga un archivo por ruta (solo Cloud; Local genera una excepción).
getUsageObtiene el consumo de tokens facturado y el coste en dólares del agente.

Agent.prompt()

function Agent.prompt(message: string, options?: AgentOptions): Promise<RunResult>;

Comodidad de ejecución única: crea un agente, envía una única instrucción, espera a que finalice la ejecución y libera los recursos.

const result = await Agent.prompt("What does the auth middleware do?", {  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  local: { cwd: process.cwd() },});

Envío de mensajes

Cada agent.send() devuelve un Run. El agente conserva el contexto de la conversación entre ejecuciones; una ejecución es la unidad de trabajo de una instrucción.

Ejecución

type RunStatus = "running" | "finished" | "error" | "cancelled";type RunOperation = "stream" | "wait" | "cancel" | "conversation";type SteerAckOutcome = "complete_delivered" | "revert_to_followup";interface Run {  readonly id: string;  readonly requestId?: string;  readonly agentId: string;  readonly status: RunStatus;  readonly result?: string;  readonly error?: RunError;  readonly model?: ModelSelection;  readonly durationMs?: number;  readonly usage?: TokenUsage;  readonly git?: RunGitInfo;  readonly createdAt?: number;  stream(): AsyncGenerator<SDKMessage, void>;  wait(): Promise<RunResult>;  cancel(): Promise<void>;  steer?(text: string): Promise<SteerAckOutcome>;  conversation(): Promise<ConversationTurn[]>;  supports(operation: RunOperation): boolean;  unsupportedReason(operation: RunOperation): string | undefined;  onDidChangeStatus(listener: (status: RunStatus) => void): () => void;}interface RunGitInfo {  branches: Array<{ repoUrl: string; branch?: string; prUrl?: string }>;}interface RunError {  message: string;  code?: string;}interface TokenUsage {  inputTokens: number;  outputTokens: number;  cacheReadTokens: number;  cacheWriteTokens: number;  totalTokens: number;  reasoningTokens?: number;}interface RunResult {  id: string;  requestId?: string;  status: "finished" | "error" | "cancelled";  result?: string;  error?: RunError;  model?: ModelSelection;  durationMs?: number;  usage?: TokenUsage;  git?: RunGitInfo;}

Flujo

const run = await agent.send("Find the bug in src/auth.ts");for await (const event of run.stream()) {  switch (event.type) {    case "assistant":      for (const block of event.message.content) {        if (block.type === "text") process.stdout.write(block.text);      }      break;    case "thinking":      process.stdout.write(event.text);      break;    case "tool_call":      console.log(`[tool] ${event.name}: ${event.status}`);      break;    case "status":      console.log(`[status] ${event.status}`);      break;  }}// Seguimiento con el mismo agente. El estado de la conversación de la ejecución// anterior se carga automáticamente.const run2 = await agent.send("Fix it and add a regression test");await run2.wait();

Para enviar imágenes junto con texto:

const run = await agent.send({  text: "What's in this screenshot?",  images: [{ data: base64Png, mimeType: "image/png" }],});

Esperar sin streaming

const result = await run.wait();console.log(result.status);      // "finished" | "error" | "cancelled"console.log(result.result);      // texto final del asistente, si lo hayconsole.log(result.error);       // { message, code? } si la ejecución fallóconsole.log(result.model);       // ModelSelection resuelto utilizado en esta ejecuciónconsole.log(result.durationMs);console.log(result.usage);       // TokenUsage acumulado o undefined si no está disponibleconsole.log(result.git);         // { branches: [{ repoUrl, branch?, prUrl? }] } en Cloud

El texto final del asistente se encuentra en result.result como una cadena. No hay ningún campo text, message, messages ni content que revisar. Si en su lugar necesita la transcripción de cada paso, llame a run.conversation() para obtener una vista estructurada de ConversationTurn[]:

const result = await run.wait();const finalText = result.result ?? "";const turns = await run.conversation();const lastAssistant = turns  .flatMap((t) => (t.type === "agentConversationTurn" ? t.turn.steps : []))  .filter((s) => s.type === "assistantMessage")  .at(-1);console.log(lastAssistant?.message.text);

Cancelar una ejecución

await run.cancel();

Cancela la ejecución. El estado pasa a "cancelled", el flujo en tiempo real se interrumpe, las llamadas a herramientas en curso se detienen y run.wait() se resuelve con status: "cancelled". La salida parcial (el texto del asistente generado hasta ese momento) permanece en el objeto Run.

Cancel se admite en ejecuciones locales y en la nube en curso, y no tiene ningún efecto si la ejecución ya ha finalizado.

Guiar una ejecución en curso

run.steer(text) inyecta un mensaje en la interacción que ya se está ejecutando en lugar de esperar a que termine. La promesa se resuelve cuando la interacción confirma si añadió el texto. complete_delivered significa que la interacción ya tiene el mensaje, así que no lo vuelvas a enviar. revert_to_followup significa que la interacción no lo aceptó, así que envíalo con agent.send() después de la ejecución.

const run = await agent.send("Migrate the auth module to the new session API");const outcome = await run.steer?.("Skip the admin routes for now");if (outcome !== "complete_delivered") {  await run.wait();  await agent.send("Skip the admin routes for now");}
  • Solo para ejecuciones locales. Las ejecuciones en la nube y los handles locales desacoplados exponen steer, pero siempre resuelven revert_to_followup.
  • El steering funciona mientras un subagent se está ejecutando en primer plano. El subagent pasa a segundo plano y sigue ejecutándose para que la interacción del parent pueda recibir el mensaje.
  • steer es opcional en Run y no es un RunOperation. Comprueba run.steer antes de llamarlo, en lugar de usar run.supports().

Consultar el estado de ejecución

console.log(run.status);  // "running" | "finished" | "error" | "cancelled"const stop = run.onDidChangeStatus((status) => {  console.log(`status changed to ${status}`);});// Llama a `stop()` para eliminar el listener.// Vista estructurada por interacción de la conversación acumulada en esta ejecuciónconst turns = await run.conversation();

run.conversation() devuelve los ConversationTurn[] de la ejecución (una interacción de agente con pasos o una interacción de shell con comando y salida). Úsalo para mostrar o conservar el historial estructurado de la ejecución sin suscribirte al flujo en tiempo real.

Consumo de tokens

Las ejecuciones informan del consumo de tokens cuando el runtime lo proporciona. Lee el total acumulado en run.usage mientras la ejecución está en curso, o en result.usage después de run.wait(). Ambos contienen un TokenUsage con la suma de todas las interacciones que informaron consumo y son undefined si ninguna interacción lo hizo (por ejemplo, en una ejecución cancelada que no terminó ninguna interacción o en un runtime que no expone el consumo).

interface TokenUsage {  inputTokens: number;  outputTokens: number;  cacheReadTokens: number;  cacheWriteTokens: number;  totalTokens: number;  reasoningTokens?: number;}
CampoDescripción
inputTokensTokens de la instrucción enviados al modelo.
outputTokensTokens generados por el modelo.
cacheReadTokensTokens obtenidos de la caché de instrucciones.
cacheWriteTokensTokens escritos en la caché de instrucciones.
totalTokensinputTokens + outputTokens + cacheReadTokens + cacheWriteTokens. No incluye reasoningTokens.
reasoningTokensTokens de razonamiento, un subconjunto de outputTokens. Se omite cuando el modelo o el runtime no los reporta.
const result = await run.wait();if (result.usage) {  console.log(`total: ${result.usage.totalTokens}`);  console.log(`in: ${result.usage.inputTokens}, out: ${result.usage.outputTokens}`);  console.log(    `cache read/write: ${result.usage.cacheReadTokens}/${result.usage.cacheWriteTokens}`  );} else {  console.log("no usage reported for this run");}

reasoningTokens ya se incluye en outputTokens, por lo que totalTokens lo omite para evitar contabilizarlo dos veces.

Para obtener los valores por interacción a medida que se transmiten, maneja el evento de flujo usage (SDKUsageMessage). Se emite una vez al final de cada interacción que informó consumo e incluye el TokenUsage de esa interacción. run.usage y result.usage se mantienen acumulados durante toda la ejecución.

for await (const event of run.stream()) {  if (event.type === "usage") {    console.log(`turn used ${event.usage.totalTokens} tokens`);  }}

El recuento de tokens es el que informa el entorno de ejecución; no indica nada sobre el coste. Para consultar el consumo facturado y el coste en dólares de las ejecuciones de un agente de programación, llama a agent.getUsage().

Correlación de ejecuciones con requestId

Cada agent.send() recibe un UUID generado por la plataforma, disponible como requestId tanto en Run como en RunResult. Úsalo para vincular una ejecución de script o de CI con registros del backend, analítica e hilos de soporte, en lugar de inferirla únicamente a partir de agentId.

const run = await agent.send("Audit the auth middleware");console.log(run.requestId); // p. ej. "6e0d261c-86a2-4383-89f0-9162c1c10662"const result = await run.wait();logger.info({ requestId: result.requestId }, "run finished");

requestId se conserva con la ejecución, por lo que se mantiene al pasar por los almacenes locales en memoria, SQLite y JSONL, y se establece en las ejecuciones en la nube cuando el backend devuelve uno. Regístralo junto con error.requestId de los errores para que un único identificador abarque tanto las rutas de éxito como las de error.

Anulación del modelo por ejecución

El model que pasas a agent.send() reemplaza la selección del agente para esa ejecución y luego se mantiene: los envíos posteriores sin una anulación seguirán usando el nuevo modelo. Para volver al anterior, pasa otra anulación de model o consulta la selección actual en agent.model.

const run = await agent.send("Plan the refactor", {  model: { id: "composer-2.5", params: [{ id: "fast", value: "true" }] },});console.log(agent.model);  // se actualiza con la anulación tras enviar correctamente

run.model y result.model reflejan la selección que se usó realmente en esta ejecución específica y no se pueden modificar una vez iniciada la ejecución.

Variables de entorno por ejecución

Los agentes en la nube también pueden recibir variables de entorno para una sola ejecución. Pasa cloud.envVars a agent.send() y los valores se inyectan en el shell del agente solo durante esa ejecución; cuando finaliza, se eliminan de la VM y la siguiente ejecución no puede verlos. Es la opción adecuada para credenciales que rotan entre interacciones, como un token de implementación de corta duración que emites justo antes de pedirle al agente que lo use.

const run = await agent.send("Deploy the preview environment", {  cloud: {    envVars: {      DEPLOY_TOKEN: await mintShortLivedToken(),    },  },});

Si una variable con ámbito de ejecución tiene el mismo nombre que una variable con ámbito de agente de cloud.envVars en Agent.create(), el valor con ámbito de ejecución prevalece en esa ejecución; en la siguiente, vuelve a aplicarse el valor con ámbito de agente.

Las variables por ejecución también funcionan en el primer envío. El SDK las incluye al crear el agente, limitadas a la ejecución inicial, por lo que no se conservan en el agente. Al igual que las variables con ámbito de agente, se cifran en reposo y sus nombres no pueden comenzar con CURSOR_.

Las variables de entorno por ejecución solo están disponibles para agentes en la nube y no para agentes que se ejecutan en repositorios públicos. En el caso de los agentes locales, el proceso del agente hereda tu entorno, así que establece las variables en el proceso antes de llamar a send().

Modo de conversación

Pasa mode: "plan" o mode: "agent" para controlar si una ejecución primero explora y planifica, o implementa los cambios directamente. Consulta el modo Plan para saber qué hace en el producto.

Establece mode en Agent.create() para configurar la primera ejecución. En las llamadas posteriores a agent.send(), omite mode para mantener el modo actual de la conversación o pásalo para cambiarlo solo en esa ejecución.

const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  mode: "plan",  cloud: {    repos: [{ url: "https://github.com/your-org/your-repo" }],  },});await (await agent.send("Design the auth refactor")).wait();await (await agent.send("Looks good, start building", { mode: "agent" })).wait();

Transmisión de deltas sin procesar

run.stream() produce eventos SDKMessage normalizados. Para actualizaciones de bajo nivel (texto por token, args de llamadas a herramientas recibidos en streaming, deltas de razonamiento, actualizaciones de tareas anidadas, límites entre pasos), pasa los callbacks onDelta y onStep a send():

const run = await agent.send("Refactor the utils module", {  onDelta: ({ update }) => {    if (update.type === "text-delta") process.stdout.write(update.text);    if (update.type === "thinking-delta") process.stdout.write(update.text);  },  onStep: ({ step }) => {    console.log(`[step] ${step.type}`);  },});

Los callbacks se esperan antes de procesar la siguiente actualización, por lo que puedes aplicar control de flujo. InteractionUpdate incluye text-delta, thinking-delta, thinking-completed, tool-call-started, tool-call-completed, tool-call-delta, partial-tool-call, token-delta, step-started, step-completed, turn-ended y varios deltas de resumen y de salida del shell.

Opciones por envío

PropiedadTipoDescripción
modelModelSelectionAnulación de modelo por envío. Si se omite, usa agent.model. Se mantiene: un envío correcto actualiza agent.model.
mode"agent" | "plan"Anulación del modo de conversación por envío. Si se omite en los mensajes de seguimiento, conserva el modo actual de la conversación.
mcpServersRecord<string, McpServerConfig>Definiciones de servidores MCP en línea. Reemplaza por completo los servidores configurados al crear el agente para esta ejecución.
onStep(args: { step }) => void | Promise<void>Callback tras completar cada paso de la conversación (texto, razonamiento o lote de herramientas).
onDelta(args: { update }) => void | Promise<void>Callback por cada InteractionUpdate sin procesar.
idempotencyKeystringClave de idempotencia opcional generada por el cliente para el envío.
cloud.envVarsRecord<string, string>Solo para agentes en la nube. Variables de entorno por ejecución inyectadas en esta ejecución y eliminadas al finalizar. Anula cloud.envVars con ámbito de agente por nombre solo en esta ejecución.
local.forcebooleanSolo para agentes locales. El valor predeterminado es false. Finaliza una ejecución activa bloqueada antes de iniciar este mensaje. En la nube, el servidor devuelve 409 agent_busy, por lo que no se necesita un equivalente.
local.customToolsRecord<string, SDKCustomTool>Solo para agentes locales. Herramientas personalizadas para esta ejecución. Reemplaza local.customTools configurado al crear el agente para esa ejecución.

Las siguientes tres secciones ofrecen una referencia detallada de SDKMessage, InteractionUpdate y ConversationTurn. Puedes revisarlas por encima u omitirlas en una primera lectura; Reanudar agentes retoma la explicación.

Eventos del flujo

Eventos de run.stream(). Distíngalos por type. Todos los eventos incluyen agent_id y run_id.

type SDKMessage =  | SDKSystemMessage  | SDKUserMessageEvent  | SDKAssistantMessage  | SDKThinkingMessage  | SDKToolUseMessage  | SDKStatusMessage  | SDKTaskMessage  | {      type: "request";      agent_id: string;      run_id: string;      request_id: string;    }  | SDKUsageMessage;
typeDescripciónCampos clave
"system"Metadatos de inicialización. Se emiten una vez al inicio de una ejecución.subtype? ("init"), model?, tools?
"user"Eco de la instrucción del usuario para esta ejecución.message.content: TextBlock[]
"assistant"Salida de texto del modelo.message.content: (TextBlock | ToolUseBlock)[]
"thinking"Contenido de razonamiento.text, thinking_duration_ms?
"tool_call"Ciclo de vida de la invocación de una herramienta. Se emite al inicio con args y de nuevo al finalizar con result.call_id, name, status, args?, result?, truncated?
"status"Transiciones del ciclo de vida de una ejecución en Cloud.status, message?
"task"Hitos y resúmenes de la tarea.status?, text?
"request"En espera de entrada o aprobación del usuario.request_id
"usage"Consumo de tokens por turno, emitido una vez al final del turno cuando el runtime lo informa.usage (TokenUsage)

Los datos del resultado (texto final, modelo, duración, consumo acumulado de tokens y metadatos de Git) están en el objeto Run cuando finaliza el flujo. Usa run.wait() para consultarlos.

El esquema de las llamadas a herramientas no es estable. Los payloads de args y result en los eventos tool_call reflejan la estructura interna de cada herramienta y pueden cambiar a medida que estas evolucionan. Los nombres de las herramientas también pueden cambiar o sustituirse. Trata args y result como unknown y analízalos de forma defensiva. La envolvente del evento (type, call_id, name, status) es estable.

Tipos de mensajes

interface SDKSystemMessage {  type: "system";  subtype?: "init";  agent_id: string;  run_id: string;  model?: ModelSelection;  tools?: string[];}interface SDKUserMessageEvent {  type: "user";  agent_id: string;  run_id: string;  message: { role: "user"; content: TextBlock[] };}interface SDKAssistantMessage {  type: "assistant";  agent_id: string;  run_id: string;  message: {    role: "assistant";    content: Array<TextBlock | ToolUseBlock>;  };}interface SDKThinkingMessage {  type: "thinking";  agent_id: string;  run_id: string;  text: string;  thinking_duration_ms?: number;}interface SDKToolUseMessage {  type: "tool_call";  agent_id: string;  run_id: string;  call_id: string;  name: string;  status: "running" | "completed" | "error";  args?: unknown;  result?: unknown;  truncated?: { args?: boolean; result?: boolean };}interface SDKStatusMessage {  type: "status";  agent_id: string;  run_id: string;  status: "CREATING" | "RUNNING" | "FINISHED" | "ERROR" | "CANCELLED" | "EXPIRED";  message?: string;}interface SDKTaskMessage {  type: "task";  agent_id: string;  run_id: string;  status?: string;  text?: string;}interface SDKUsageMessage {  type: "usage";  agent_id: string;  run_id: string;  usage: TokenUsage;}interface TextBlock {  type: "text";  text: string;}interface ToolUseBlock {  type: "tool_use";  id: string;  name: string;  input: unknown;}

SDKToolUseMessage se emite dos veces para la mayoría de las llamadas a herramientas: primero con status: "running" y args rellenado, y después, al completarse, con status: "completed" (o "error") y result rellenado. truncated indica si el SDK truncó args o result porque el payload era demasiado grande.

SDKStatusMessage abarca las transiciones del ciclo de vida en la nube. CREATING incluye el aprovisionamiento de la VM y la clonación del repositorio; RUNNING indica que el agente está trabajando; el resto son estados terminales.

SDKUsageMessage se emite una vez al final de cada interacción que informó consumo de tokens e incluye el TokenUsage de esa interacción. El total acumulado entre interacciones se mantiene en run.usage y result.usage. Consulta Consumo de tokens.

Actualizaciones de interacción

InteractionUpdate es el tipo delta sin procesar que se pasa al callback onDelta de agent.send(). Las actualizaciones son más granulares que los eventos SDKMessage: el texto se transmite token a token, las llamadas a herramientas informan de estados parciales a medida que se acumulan los argumentos y el razonamiento llega a medida que se genera.

type InteractionUpdate =  | TextDeltaUpdate  | ThinkingDeltaUpdate  | ThinkingCompletedUpdate  | ToolCallStartedUpdate  | ToolCallCompletedUpdate  | ToolCallDeltaUpdate  | PartialToolCallUpdate  | TokenDeltaUpdate  | StepStartedUpdate  | StepCompletedUpdate  | TurnEndedUpdate  | UserMessageAppendedUpdate  | SummaryUpdate  | SummaryStartedUpdate  | SummaryCompletedUpdate  | ShellOutputDeltaUpdate;

Tipos de actualizaciones

interface TextDeltaUpdate {  type: "text-delta";  text: string;}interface ThinkingDeltaUpdate {  type: "thinking-delta";  text: string;}interface ThinkingCompletedUpdate {  type: "thinking-completed";  thinkingDurationMs: number;}interface ToolCallStartedUpdate {  type: "tool-call-started";  callId: string;  toolCall: ToolCall;  modelCallId: string;}interface PartialToolCallUpdate {  type: "partial-tool-call";  callId: string;  toolCall: ToolCall;  modelCallId: string;}interface ToolCallCompletedUpdate {  type: "tool-call-completed";  callId: string;  toolCall: ToolCall;  modelCallId: string;}interface ToolCallDeltaUpdate {  type: "tool-call-delta";  callId: string;  modelCallId: string;  taskUpdate: NestedTaskUpdate;}type NestedTaskUpdate =  | TextDeltaUpdate  | ToolCallStartedUpdate  | ToolCallCompletedUpdate  | ThinkingDeltaUpdate  | ThinkingCompletedUpdate  | PartialToolCallUpdate  | StepStartedUpdate  | StepCompletedUpdate;interface TokenDeltaUpdate {  type: "token-delta";  tokens: number;}interface StepStartedUpdate {  type: "step-started";  stepId: number;}interface StepCompletedUpdate {  type: "step-completed";  stepId: number;  stepDurationMs: number;}interface TurnEndedUpdate {  type: "turn-ended";  usage?: {    inputTokens: number;    outputTokens: number;    cacheReadTokens: number;    cacheWriteTokens: number;    reasoningTokens?: number;  };}interface UserMessageAppendedUpdate {  type: "user-message-appended";  userMessage: UserMessage;}interface SummaryUpdate {  type: "summary";  summary: string;}interface SummaryStartedUpdate {  type: "summary-started";}interface SummaryCompletedUpdate {  type: "summary-completed";}interface ShellOutputDeltaUpdate {  type: "shell-output-delta";  event: Record<string, unknown>;}

ToolCallDeltaUpdate contiene un nivel de actualizaciones de interacción anidadas de una llamada a herramienta de una tarea o un subagente. PartialToolCallUpdate se emite a medida que el modelo transmite argumentos a una llamada a herramienta antes de confirmarla. La misma advertencia sobre estabilidad aplicable a SDKToolUseMessage.args también se aplica aquí.

Tipos de conversación

La vista estructurada de cada interacción de una ejecución, que devuelve run.conversation() y se usa como argumento del callback onStep.

type ConversationTurn =  | { type: "agentConversationTurn"; turn: AgentConversationTurn }  | { type: "shellConversationTurn"; turn: ShellConversationTurn };interface AgentConversationTurn {  userMessage?: UserMessage;  steps: ConversationStep[];}interface ShellConversationTurn {  shellCommand?: ShellCommand;  shellOutput?: ShellOutput;}type ConversationStep =  | { type: "assistantMessage"; message: AssistantMessage }  | { type: "toolCall"; message: ToolCall }  | { type: "thinkingMessage"; message: ThinkingMessage };interface AssistantMessage {  text: string;}interface ThinkingMessage {  text: string;  thinkingDurationMs?: number;}interface UserMessage {  text: string;}interface ShellCommand {  command: string;  workingDirectory?: string;}interface ShellOutput {  stdout: string;  stderr: string;  exitCode: number;}

ToolCall es una unión discriminada de todas las herramientas integradas (shell, edit, read, write, glob, grep, ls, semSearch, mcp, task y otras). Su estructura es de uso interno; consulta la nota sobre estabilidad en Eventos de flujo.

Reanudar agentes de programación

function Agent.resume(agentId: string, options?: Partial<AgentOptions>): Promise<SDKAgent>;

Usa Agent.resume() para reconectarte a un agente existente mediante su ID. Flujos habituales: reconectarte a un agente en la nube de larga duración iniciado anteriormente o continuar una conversación después de reiniciar el proceso local. El entorno de ejecución se detecta automáticamente a partir del prefijo del ID (bc- corresponde a la nube; cualquier otro, al entorno local).

await using agent = await Agent.resume("bc-abc123", {  apiKey: process.env.CURSOR_API_KEY!,});const run = await agent.send("Also update the changelog");await run.wait();

agent.model es undefined al reanudar, a menos que vuelvas a pasar model. Los mcpServers en línea no se conservan entre reanudaciones: suelen contener secretos y solo existen en memoria. Vuelve a pasarlos al reanudar o usa una configuración de MCP basada en archivos (.cursor/mcp.json + local.settingSources) para los servidores que deban persistir.

Inspección de agentes de programación y ejecuciones

Lista, consulta y recarga agentes de programación anteriores. Los endpoints de listado devuelven { items, nextCursor? } para la paginación basada en cursor.

Agent.list()

function Agent.list(options?: ListAgentsOptions): Promise<ListResult<SDKAgentInfo>>;type ListAgentsOptions = {  limit?: number;  cursor?: string;} & (  | { runtime?: undefined }  | { runtime: "local"; cwd?: string; store?: LocalAgentStore }  | {      runtime: "cloud";      prUrl?: string;      includeArchived?: boolean;      apiKey?: string;    });
const { items, nextCursor } = await Agent.list({  runtime: "local",  cwd: process.cwd(),});

Agent.get()

function Agent.get(agentId: string, options?: GetAgentOptions): Promise<SDKAgentInfo>;interface GetAgentOptions {  cwd?: string;       // enrutamiento local  apiKey?: string;    // enrutamiento en la nube  store?: LocalAgentStore;}

El entorno de ejecución se detecta automáticamente según el prefijo del ID del agente (bc- → nube; en caso contrario, local).

Agent.listRuns()

function Agent.listRuns(agentId: string, options?: ListRunsOptions): Promise<ListResult<Run>>;type ListRunsOptions = {  limit?: number;  cursor?: string;} & (  | { runtime?: "local"; cwd?: string; store?: LocalAgentStore }  | { runtime: "cloud"; apiKey?: string });

Agent.getRun()

function Agent.getRun(runId: string, options?: GetRunOptions): Promise<Run>;type GetRunOptions =  | { runtime?: "local"; cwd?: string; store?: LocalAgentStore }  | { runtime: "cloud"; agentId: string; apiKey?: string };

Cloud getRun requiere el agentId del agente principal.

Agent.cancelRun()

function Agent.cancelRun(runId: string, options?: GetRunOptions): Promise<void>;

Cancela una ejecución cuando tienes su ID, pero no un identificador Run.

Agent.messages.list()

Agent.messages.list(  agentId: string,  options?: GetAgentMessagesOptions): Promise<AgentMessage[]>;interface GetAgentMessagesOptions {  limit?: number;  offset?: number;  runtime?: "local";  cwd?: string;  store?: LocalAgentStore;}interface AgentMessage {  type: "user" | "assistant";  uuid: string;  agent_id: string;  message: unknown;}

Devuelve los mensajes del usuario y del asistente almacenados para un agente local.

Agent.getUsage()

Obtiene el consumo de tokens facturados y el coste en dólares de las ejecuciones de un agente de programación. Llámalo desde un identificador o, de forma estática, mediante un ID cuando no dispongas de uno. Los agentes en la nube devuelven un desglose por ejecución; los agentes locales, un desglose por interacción. Pasa runId para limitar el resultado a una entrada: para los agentes en la nube, un ID de ejecución run-<uuid>; para los agentes locales, un ID obtenido de un getUsage().runs[].runId anterior.

agent.getUsage(options?: GetUsageOptions): Promise<AgentUsage>;function Agent.getUsage(  agentId: string,  options?: GetUsageOptions & { apiKey?: string }): Promise<AgentUsage>;interface GetUsageOptions {  runId?: string;}interface AgentUsage {  usage: TokenUsage;   // total en todas las `runs`  cost?: UsageCost;    // total en todas las `runs`  runs: RunUsage[];}interface RunUsage {  runId: string;  usage: TokenUsage;  cost?: UsageCost;}interface UsageCost {  rawCostCents: number;   // coste de los tokens del modelo sin descuentos; 0 para consumo con precio por solicitud  chargedCents: number;   // importe cobrado, incluidos los descuentos y la tarifa de tokens de Cherri Code}
const { usage, cost, runs } = await agent.getUsage();console.log(`tokens: ${usage.totalTokens}`);if (cost) {  console.log(`charged: $${(cost.chargedCents / 100).toFixed(2)}`);}for (const run of runs) {  console.log(run.runId, run.usage.totalTokens, run.cost?.chargedCents);}

El coste incluye los descuentos y puede tardar un momento en actualizarse después de que finalice una ejecución; cost no estará disponible hasta entonces. chargedCents es 0 para el consumo incluido en el plan, BYOK y las concesiones de crédito.

Esta es una vista distinta de Consumo de tokens: run.usage es el recuento de tokens en tiempo real de una ejecución, mientras que getUsage() es el registro facturado de todas las ejecuciones del agente.

Ciclo de vida de los agentes en la nube

Los agentes en la nube permanecen en el espacio de trabajo de tu equipo hasta que los archives o elimines. Agent.list({ runtime: "cloud" }) oculta los agentes archivados de forma predeterminada; pasa includeArchived: true para verlos. Filtra por prUrl para encontrar el agente que abrió una solicitud de extracción específica.

function Agent.archive(agentId: string, options?: AgentOperationOptions): Promise<void>;function Agent.unarchive(agentId: string, options?: AgentOperationOptions): Promise<void>;function Agent.delete(agentId: string, options?: AgentOperationOptions): Promise<void>;interface AgentOperationOptions {  cwd?: string;  apiKey?: string;  store?: LocalAgentStore;}
await Agent.archive(agentId);     // eliminación lógica; la transcripción sigue siendo accesibleawait Agent.unarchive(agentId);   // restaura un agente archivadoawait Agent.delete(agentId);      // permanente; las lecturas posteriores devuelven 404

SDKAgentInfo

La estructura de los metadatos que devuelven Agent.list() y Agent.get().

type SDKAgentInfo = {  agentId: string;  name: string;  summary: string;  lastModified: number;  status?: "running" | "finished" | "error";  createdAt?: number;  archived?: boolean;} & (  | { runtime?: undefined }  | { runtime: "local"; cwd?: string }  | {      runtime: "cloud";      env?: { type: "cloud" | "pool" | "machine"; name?: string };      repos?: string[];      metadata?: Record<string, string>;    });

El espacio de nombres Cherri Code

Lecturas a nivel de cuenta y del catálogo, y configuración del SDK para todo el proceso. Los métodos de lectura aceptan un { apiKey } opcional; de lo contrario, usan CURSOR_API_KEY como alternativa y, después, un inicio de sesión en el navegador.

Cherri Code.auth

Inicio de sesión interactivo para hosts sin una clave de API aprovisionada. Cherri Code.auth.login() abre en un navegador la página de inicio de sesión del sitio web de Cherri Code, espera a que finalice y emite una clave de API de usuario (con una validez predeterminada de 90 días) que se almacena en ~/.cursor/sdk/auth.json. Tras iniciar sesión, Agent.create(), Cherri Code.me() y las demás operaciones de lectura funcionan sin apiKey ni CURSOR_API_KEY.

import { Cherri Code } from "@cursor/sdk";await Cherri Code.auth.login();const status = await Cherri Code.auth.status();// { status: "logged-in", backendUrl, email?, apiKeyExpiresAtMs? }// | { status: "logged-out" }await Cherri Code.auth.logout();
OpciónDescripción
backendUrlURL base de la API. El valor predeterminado es CURSOR_BACKEND_URL y, después, producción.
websiteUrlURL base de inicio de sesión en el navegador. El valor predeterminado es CURSOR_WEBSITE_URL y, después, producción.
openBrowsertrue (valor predeterminado) abre el navegador del sistema cuando sea probable que funcione; false no abre ninguno; una función actúa como abridor personalizado. Se omite en sesiones SSH o cuando se establece NO_OPEN_BROWSER.
onLoginUrlSe invoca con la URL de inicio de sesión antes de esperar para que el host pueda mostrarla. Si se omite y no se abrió ningún navegador, la URL se escribe en stderr.
signalAbortSignal que cancela la espera; login genera entonces un AuthenticationError.
storeUbicación donde se conservan las credenciales. El valor predeterminado es ~/.cursor/sdk/auth.json; pasa null para recibir la clave solo en el resultado.
apiKeyNameNombre para mostrar de la clave emitida en la lista de claves de API del panel de control.
apiKeyTtlMsDuración de la clave emitida en milisegundos. El valor predeterminado es 90 días.

Cherri Code.auth.login() devuelve { apiKey, email?, apiKeyExpiresAtMs }. Usa FileCredentialStore o InMemoryCredentialStore para proporcionar un almacén personalizado a login(), status() o logout().

Orden de resolución de credenciales en todo el SDK: apiKey explícita, luego CURSOR_API_KEY y, después, el inicio de sesión almacenado. El inicio de sesión almacenado no lee credenciales de una instalación local de Cursor; solo contiene claves emitidas por Cherri Code.auth.login().

Cherri Code.configure()

function Cherri Code.configure(options: CursorConfigureOptions): void;interface CursorConfigureOptions {  local?: {    store?: LocalAgentStore | null;    useHttp1ForAgent?: boolean | null;    workspaceScanCacheTtlMs?: number | null;  };}

Establece valores predeterminados para los agentes locales que se aplicarán a llamadas posteriores a Agent.*. Los campos de una llamada concreta anulan estos valores; pasa null para borrar un valor predeterminado anterior.

OpciónDescripción
local.storeAlmacén de agente local predeterminado cuando una llamada omite local.store. El SDK usa SQLite en disco a través de node:sqlite; cuando ese módulo no está disponible, Agent.create() lanza un ConfigurationError a menos que configures JsonlLocalAgentStore u otro almacén aquí.
local.useHttp1ForAgentFuerza que los flujos del backend del agente local usen HTTP/1.1 con SSE en lugar de HTTP/2. Resulta útil detrás de proxies o en pilas de fetch que no admiten HTTP/2.

Bun usa HTTP/1.1 de forma predeterminada debido a problemas de compatibilidad con HTTP/2 en dependencias externas.
local.workspaceScanCacheTtlMsIndica durante cuánto tiempo el SDK reutiliza un análisis del espacio de trabajo (reglas, skills, AGENTS.md, archivos ignorados), en milisegundos. El valor predeterminado es de 20 segundos. Auméntalo en un host de larga duración que sirva un checkout que solo cambie al implementar; la contrapartida es que la información puede quedar desactualizada, ya que una regla añadida después de iniciarse el proceso puede no detectarse durante ese tiempo. La variable de entorno CURSOR_RIPWALK_CACHE_TTL_MS establece el mismo valor.
import { Cherri Code, JsonlLocalAgentStore } from "@cursor/sdk";Cherri Code.configure({  local: {    store: new JsonlLocalAgentStore("/var/lib/cursor-agents"),    useHttp1ForAgent: true,  },});

Cherri Code.me()

function Cherri Code.me(options?: CursorRequestOptions): Promise<SDKUser>;interface CursorRequestOptions {  apiKey?: string;}interface SDKUser {  apiKeyName: string;  userId?: number;  userEmail?: string;  userFirstName?: string;  userLastName?: string;  createdAt: string;}

Cherri Code.models.list()

function Cherri Code.models.list(options?: CursorRequestOptions): Promise<SDKModel[]>;type SDKModel = ModelListItem;interface ModelListItem {  id: string;  displayName: string;  description?: string;  aliases?: string[];  parameters?: ModelParameterDefinition[];  variants?: ModelVariant[];}interface ModelParameterDefinition {  id: string;  displayName?: string;  values: Array<{ value: string; displayName?: string }>;}interface ModelVariant {  params: ModelParameterValue[];  displayName: string;  description?: string;  isDefault?: boolean;}

Usa Cherri Code.models.list() para consultar los ID de model válidos y los params de cada modelo antes de llamar a Agent.create() o agent.send(). Los parámetros son específicos de cada modelo. Algunos ejemplos habituales son el esfuerzo de razonamiento y optimize_for de Cherri Code Router en auto-smart.

El catálogo depende de la cuenta y del equipo. Cherri Code Router solo aparece como auto-smart cuando Router está disponible para el equipo de la clave de API. Consulta Cherri Code Router.

const models = await Cherri Code.models.list();const composer = models.find((model) => model.id === "composer-2.5");console.log(composer?.parameters);// [//   {//     id: "fast",//     displayName: "Fast",//     values: [//       { value: "false" },//       { value: "true", displayName: "Fast" },//     ],//   },// ]

Pasa los valores de los parámetros seleccionados a través de model.params. Las variants preestablecidas ya contienen params válidos, por lo que puedes copiarlos en una selección de modelo.

const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: {    id: "composer-2.5",    params: [{ id: "fast", value: "true" }],  },  local: { cwd: process.cwd() },});

Mejores prácticas

  • Detecta, no codifiques valores fijos. Llama a Cherri Code.models.list() al iniciar (o una vez por proceso) y guarda el resultado en caché. Los ID de los modelos y las estructuras de los parámetros pueden cambiar a medida que se lanzan nuevos modelos.

  • Pasa los parámetros explícitamente cuando el modelo los requiera. Un modelo cuyo array parameters no está vacío es un modelo parametrizado. Envía los parámetros que quieras; de lo contrario, la ejecución usa el primer valor permitido de cada parámetro, que puede no coincidir con lo que pretendes. Para Cherri Code Router, pasa siempre optimize_for explícitamente.

  • Resuelve por funcionalidad, no por ID. Si quieres «el valor predeterminado actual en modo rápido» en lugar de un modelo específico, búscalo:

    const models = await Cherri Code.models.list();const composer = models.find((m) => m.id === "composer-2.5");const fast = composer?.parameters?.find((p) => p.id === "fast");const fastValue = fast?.values.find((v) => v.value === "true")?.value;const model = composer  ? {      id: composer.id,      params: fastValue ? [{ id: "fast", value: fastValue }] : undefined,    }  : {      id: "auto-smart",      params: [{ id: "optimize_for", value: "balanced" }],    };

    Prefiere una selección explícita de Router (auto-smart + optimize_for) si el modelo de destino no está disponible. Recurre a { id: "auto" } solo si quieres que el servidor seleccione Auto sin elegir Coste, Equilibrio o Inteligencia.

Cherri Code.repositories.list()

function Cherri Code.repositories.list(options?: CursorRequestOptions): Promise<SDKRepository[]>;interface SDKRepository {  url: string;}

Devuelve los repositorios de GitHub conectados del equipo del usuario que realiza la llamada. Solo Cloud.

Fuentes de configuración de un vistazo

Los servidores MCP, los subagentes y los hooks se resuelven a partir de una combinación de opciones en línea y configuración en disco. La precedencia tiene la misma estructura en los tres casos: en línea por envío > en línea al crear > archivos de proyecto > archivos de usuario > configuración de equipo / Panel de control.

FunciónOpción en líneaArchivo local (proyecto)Archivo local (usuario)Cloud / Panel de controlPrecedencia
Servidores MCPmcpServers en Agent.create() y agent.send().cursor/mcp.json (requiere que local.settingSources incluya "project")~/.cursor/mcp.json (requiere "user")Servidores configurados en cursor.com/agents (solo en Cloud)Enviar > crear > plugins > proyecto > usuario (local); Enviar > crear > Panel de control (Cloud)
Subagentesagents en Agent.create().cursor/agents/*.md (frontmatter: name, description, model?)n/aCloud carga los mismos archivos de proyecto cuando el agente se ejecuta con el repositorio clonadoLa configuración en línea anula la basada en archivos con el mismo nombre
HooksNinguno — solo basados en archivos.cursor/hooks.json (+ scripts)~/.cursor/hooks.jsonCloud ejecuta hooks de proyecto. En los planes Enterprise, también hooks de equipo y gestionados por Enterprise.Basados en archivos; el proyecto se combina por capas con usuario / equipo / Enterprise según Hooks
Fuentes de configuraciónlocal.settingSources selecciona qué capas en disco cargar.cursor/~/.cursor/n/aCloud siempre carga project / team / plugins e ignora local.settingSources.

Los valores en línea son adecuados para secretos que nunca deben almacenarse en disco (claves de API por ejecución, tokens limitados al inquilino). La configuración basada en archivos es adecuada para políticas: los hooks, en particular, delimitan el proyecto, no son un ajuste por ejecución.

Servidores MCP

Los agentes de programación pueden utilizar servidores MCP de varias fuentes. Las definiciones en línea en Agent.create() o agent.send() son la opción más habitual. También se admiten configuraciones basadas en archivos y gestionadas desde el Panel de control.

Qué se carga

Los agentes locales cargan servidores de hasta cinco fuentes. Si hay nombres en conflicto, prevalece la primera coincidencia:

  1. mcpServers en agent.send(). Reemplaza por completo los servidores definidos al crear el agente para esa ejecución (no se fusionan).
  2. mcpServers en Agent.create(). Se usa cuando no se proporciona una anulación para el envío.
  3. Servidores de plugins, si local.settingSources incluye "plugins".
  4. Servidores del proyecto de .cursor/mcp.json, si local.settingSources incluye "project".
  5. Servidores de usuario de ~/.cursor/mcp.json, si local.settingSources incluye "user".

Sin local.settingSources, solo se cargan los servidores definidos en línea. Si un servidor MCP local requiere iniciar sesión con OAuth, el SDK no puede solicitarte que inicies sesión. Solo funciona si ya has iniciado sesión en ese servidor desde la aplicación Cursor; en ese caso, el SDK reutiliza ese inicio de sesión guardado.

Los agentes en la nube cargan servidores de:

  1. mcpServers en agent.send(). Reemplaza por completo los servidores definidos al crear el agente para esa ejecución (no se fusionan).
  2. mcpServers en Agent.create(). Se usa cuando no se proporciona una anulación para el envío.
  3. Tus servidores MCP de usuario y de equipo de cursor.com/agents.

Si un servidor definido en línea no incluye auth ni headers, y autorizaste previamente la URL de ese servidor en cursor.com/agents, las ejecuciones autenticadas con un token de API personal reutilizan automáticamente esos tokens de OAuth. Las claves de API de cuentas de servicio no pueden recurrir a la autenticación de usuario, ya que no están asociadas a ningún usuario.

local.settingSources no se aplica a los agentes en la nube.

Local

const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "auto" },  local: { cwd: process.cwd() },  mcpServers: {    docs: {      type: "http",      url: "https://example.com/mcp",      auth: {        CLIENT_ID: "client-id",        scopes: ["read", "write"],      },    },    filesystem: {      type: "stdio",      command: "npx",      args: ["-y", "@modelcontextprotocol/server-filesystem", process.cwd()],      cwd: process.cwd(),    },  },});

Nube

Los agentes en la nube también pueden recibir configuraciones de MCP autenticadas en línea. Usa autenticación HTTP cuando Cherri Code deba usar el backend como proxy para un MCP remoto. Usa env de stdio cuando el servidor se ejecute dentro de la VM en la nube y lea las credenciales de las variables de entorno.

const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  cloud: {    repos: [{ url: "https://github.com/your-org/your-repo", startingRef: "main" }],  },  mcpServers: {    linear: {      type: "http",      url: "https://mcp.linear.app/sse",      headers: {        Authorization: `Bearer ${process.env.LINEAR_API_KEY!}`,      },    },    figma: {      type: "http",      url: "https://api.figma.com/mcp",      auth: {        CLIENT_ID: process.env.FIGMA_CLIENT_ID!,        CLIENT_SECRET: process.env.FIGMA_CLIENT_SECRET!,        scopes: ["file_content:read"],      },    },    github: {      type: "stdio",      command: "npx",      args: ["-y", "@modelcontextprotocol/server-github"],      env: {        GITHUB_TOKEN: process.env.GITHUB_TOKEN!,      },    },  },});

Usa headers para claves de API estáticas o tokens Bearer; Cherri Code los incluye en cada solicitud. Usa auth para servidores protegidos con OAuth. En la nube, Cherri Code ejecuta el flujo de OAuth una sola vez en el servidor y reutiliza el token entre ejecuciones. Localmente, el SDK no puede abrir un navegador para iniciar sesión; solo reutiliza los tokens que ya hayas obtenido al iniciar sesión en la aplicación Cherri Code.

  • Cherri Code gestiona los headers HTTP y auth desde su backend. Los campos sensibles se ocultan y no llegan a la VM.
  • Los valores de env de Stdio se pasan a la VM porque el servidor se ejecuta allí. Trátalos como cualquier otro secreto de tiempo de ejecución.
  • OAuth para los servidores MCP configurados en cursor.com/agents se mantiene por usuario, incluso en servidores de nivel de equipo.

Consulta MCP para ver el formato completo de configuración y capacidades del agente en la nube para conocer el comportamiento específico de la nube.

Subagentes

Define subagentes con nombre que el agente principal inicia mediante la herramienta Agent. Pásalos en línea:

const agent = await Agent.create({  model: { id: "composer-2.5" },  apiKey: process.env.CURSOR_API_KEY!,  local: { cwd: process.cwd() },  agents: {    "code-reviewer": {      description: "Expert code reviewer for quality and security.",      prompt: "Review code for bugs, security issues, and proven approaches.",      model: "inherit",    },    "test-writer": {      description: "Writes tests for code changes.",      prompt: "Write comprehensive tests for the given code.",    },  },});

También se detectan los subagentes confirmados en el repositorio en .cursor/agents/*.md (con frontmatter name, description y model opcional). Las definiciones en línea anulan las definiciones basadas en archivos con el mismo nombre.

Subagentes anidados

Los subagentes pueden generar sus propios subagentes dentro de un límite de anidamiento. Cuando un subagente usa la herramienta Agent, el SDK le proporciona el mismo ejecutor de subagentes que tiene el agente principal, por lo que este puede delegar en un subagente que, a su vez, delega en otros. Todos los niveles tienen acceso al mismo conjunto de subagentes con nombre y herramientas personalizadas. El agente de nivel superior y sus subagentes directos pueden iniciar subagentes, pero un subagente iniciado por otro subagente no puede iniciar más.

Subagentes en segundo plano

Cuando el agente de programación ejecuta un subagente en segundo plano, el resultado del subagente se devuelve al agente padre como una interacción de seguimiento dentro de la misma ejecución, en lugar de descartarse al terminar la interacción del padre. run.stream() sigue emitiendo eventos durante esas interacciones y run.wait() se resuelve después de ellas con el texto de la última interacción como result. Solo para agentes locales.

Restringir el conjunto de herramientas

tools añade a la lista de permitidos las herramientas integradas disponibles para el modelo; disallowedTools elimina herramientas y conserva las demás, incluidas las añadidas a la plataforma después de la publicación de tu versión del SDK. Por ahora, ambas opciones solo están disponibles para agentes locales y ninguna se conserva en el agente: vuelve a pasarlas en Agent.resume() para mantener la restricción en ejecuciones posteriores.

// Agente de solo lectura: solo se proporcionan estas herramientas.const reader = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  tools: ["read", "grep", "glob", "ls"],  local: { cwd: process.cwd() },});// Todo excepto el acceso a la shell.const noShell = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  disallowedTools: ["shell"],  local: { cwd: process.cwd() },});
  • tools: undefined (predeterminado) ofrece el conjunto de herramientas estándar del modelo seleccionado; tools: [] no ofrece herramientas integradas, por lo que el modelo solo puede responder con texto.
  • Ambos campos aceptan la unión ToolName: nombres públicos ("read", "edit", "task", "webSearch", ...), los grupos de funcionalidades "shell" y "mcp", y nombres sin procesar de herramientas proto. Los nombres desconocidos generan un ConfigurationError en Agent.create() / Agent.resume().
  • Denegar tiene prioridad: para estar disponible, una herramienta debe estar en tools (cuando se especifica) y no en disallowedTools.
  • Deshabilitar "mcp" también elimina las herramientas personalizadas. Deshabilitar "task" impide usar subagentes; de lo contrario, los subagentes conservan sus propios conjuntos de herramientas seleccionados.

Herramientas personalizadas

Las herramientas personalizadas te permiten exponer tus propias funciones al agente de programación sin tener que configurar un servidor MCP independiente. Pásalas en local.customTools y el SDK las registra como un servidor MCP llamado custom-user-tools. El agente de programación las detecta y las invoca a través de la misma vía MCP que cualquier otro servidor. Las reglas de denegación y los límites de sandbox siguen aplicándose, pero las herramientas personalizadas no requieren aprobación interactiva, por lo que las ejecuciones sandboxed y auto-review las invocan sin solicitar confirmación. Las herramientas personalizadas también están disponibles para los subagentes, incluidos los anidados.

Las herramientas personalizadas solo están disponibles para agentes locales. Los agentes en la nube ignoran local.customTools durante la creación y generan un ConfigurationError cuando lo pasas en send().

const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  local: {    cwd: process.cwd(),    customTools: {      get_deployment_status: {        description: "Look up the current deployment status for a service.",        inputSchema: {          type: "object",          properties: {            service: { type: "string", description: "Service name" },          },          required: ["service"],        },        async execute({ service }) {          const res = await fetch(`https://deploys.internal/api/${service}`);          const body = await res.json();          return `Service ${service} is ${body.status} (build ${body.build}).`;        },      },    },  },});await agent.send("Is the checkout service deployed yet?").then((r) => r.wait());

Configura las herramientas personalizadas una vez en Agent.create() para aplicarlas a cada ejecución, o pasa local.customTools en un único agent.send() para reemplazarlas en esa ejecución.

await agent.send("Roll forward if the canary is healthy", {  local: {    customTools: {      promote_canary: {        description: "Promote the current canary build to production.",        async execute() {          await promoteCanary();          return { content: [{ type: "text", text: "Promoted." }] };        },      },    },  },});

Definición de herramienta

interface SDKCustomTool {  description?: string;  inputSchema?: Record<string, SDKJsonValue>;  outputSchema?: Record<string, SDKJsonValue>;  annotations?: SDKToolAnnotations;  execute: (    args: Record<string, SDKJsonValue>,    context: SDKCustomToolContext  ) => SDKCustomToolResult | Promise<SDKCustomToolResult>;}interface SDKToolAnnotations {  title?: string;  readOnlyHint?: boolean;  destructiveHint?: boolean;  idempotentHint?: boolean;  openWorldHint?: boolean;}interface SDKCustomToolContext {  toolCallId?: string;}
CampoDescripción
descriptionSe muestra al modelo para que sepa cuándo invocar la herramienta. Su valor predeterminado es una cadena vacía.
inputSchemaJSON Schema de los argumentos. Su valor predeterminado es un objeto abierto que acepta cualquier propiedad.
outputSchemaJSON Schema del resultado estructurado de la herramienta, que se anuncia al modelo como el Tool.outputSchema de MCP. Los resultados no se validan contra él.
annotationsAnotaciones de la herramienta MCP (title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint) que se transmiten al modelo. Son solo sugerencias descriptivas; nada en el SDK las impone.
executeTu callback. Recibe los args analizados y un context con el toolCallId. Se ejecuta en tu proceso, por lo que puede acceder a todo aquello a lo que puede acceder tu código.

Resultados de herramientas

execute puede devolver una cadena de texto simple, cualquier valor JSON o un contenedor estructurado. La clave del mapa es el nombre de la herramienta que invoca el modelo.

type SDKCustomToolResult =  | string  | SDKJsonValue  | {      content: SDKCustomToolContent[];      isError?: boolean;      structuredContent?: Record<string, SDKJsonValue>;    };type SDKCustomToolContent =  | { type: "text"; text: string }  | { type: "image"; data: string; mimeType?: string };
  • Devuelve una cadena para la salida de texto sin formato.
  • Devuelve cualquier valor JSON para devolverlo como texto; los objetos también completan structuredContent.
  • Devuelve el contenedor para tener control total: combina content de texto e imágenes en base64, establece isError: true para informar de un fallo o adjunta structuredContent para que el modelo lo analice. Las excepciones lanzadas desde execute también se devuelven al agente como errores de herramienta.

Hooks

Los hooks se basan únicamente en archivos. No existe un callback programático para hooks. Los hooks delimitan las políticas del proyecto, no son una opción por ejecución.

  • Local: Añade .cursor/hooks.json al repositorio indicado en local.cwd o añade ~/.cursor/hooks.json para hooks de nivel de usuario.
  • Nube: Confirma .cursor/hooks.json y sus scripts en el repositorio indicado en cloud.repos. Los agentes en la nube creados mediante el SDK cargan automáticamente los hooks del proyecto. En los planes Enterprise, también ejecutan hooks de equipo y hooks gestionados por la empresa.

Consulta Hooks para ver el formato de configuración y la compatibilidad con hooks de Cloud Agents para consultar el comportamiento en la nube.

Opciones de sandbox

Los agentes locales se ejecutan con local.sandboxOptions.enabled: false de forma predeterminada. El agente puede leer y escribir en el directorio de trabajo, ejecutar comandos de shell y acceder a la red sin restricciones. No hay un flujo de aprobación con intervención humana en las ejecuciones del SDK sin interfaz gráfica, por lo que un sandbox activado de forma predeterminada bloquearía silenciosamente llamadas a herramientas legítimas o requeriría un callback que no encaja en un script.

Al activar el sandbox, el SDK restringe todas las llamadas a herramientas de shell y los procesos iniciados desde la shell:

  • Sistema de archivos. Las escrituras se limitan al directorio de trabajo (local.cwd), los directorios temporales y las rutas que permitas en sandbox.json. Las lecturas no se limitan al espacio de trabajo.
  • Shell. Los comandos se ejecutan dentro de un sandbox de la plataforma (bubblewrap en Linux, seatbelt en macOS, o el asistente incluido @cursor/sdk-<os>-<arch>). Se deniegan las operaciones con privilegios.
  • Red. La red saliente se bloquea de forma predeterminada. Para permitir hosts específicos, añade un .cursor/sandbox.json al espacio de trabajo con la lista de hosts permitidos. El SDK también lee la política por usuario en ~/.cursor/sandbox.json, si existe.
const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  local: {    cwd: process.cwd(),    sandboxOptions: { enabled: true },  },});

Si el host no admite el sandboxing (una versión antigua de Linux sin bubblewrap o sin el binario auxiliar), el SDK genera un ConfigurationError con un mensaje que identifica la dependencia faltante. Desactiva sandboxOptions.enabled o ejecuta en modo cloud para solucionarlo.

Las ejecuciones en la nube siempre se realizan dentro de una VM aislada, por lo que sandboxOptions no se aplica.

modo Auto-review

De forma predeterminada, un agente local ejecuta todas las llamadas a herramientas sin restricciones, ya que las ejecuciones sin interfaz gráfica no cuentan con una persona que las apruebe. Establece local.autoReview: true para que las llamadas a herramientas locales pasen por el modo Auto-review, el mismo clasificador que usa el IDE para permitir o bloquear llamadas de Shell, MCP y Fetch según su seguridad y en qué medida se ajustan a la intención de la ejecución.

const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  local: {    cwd: process.cwd(),    autoReview: true,  },});

El modo Auto-review requiere que el clasificador esté activado en el backend conectado; si no está disponible, las ejecuciones recurren al comportamiento predeterminado. Como no hay aprobación interactiva en una ejecución sin interfaz gráfica, las llamadas que el clasificador bloquea se deniegan en lugar de escalarse, y el agente recibe el motivo del bloqueo y puede probar otro enfoque. Orienta el clasificador con un bloque autoRun de permissions.json en el espacio de trabajo, igual que en el IDE. Consulta permissions.json para conocer el formato.

El modo Auto-review solo está disponible para agentes locales. Las ejecuciones en la nube ya se ejecutan en una VM aislada. El clasificador es una comodidad basada en el mejor esfuerzo, no un límite de seguridad; combínalo con sandboxOptions o una lista de permitidos para un control estricto.

Artefactos

Lista y descarga archivos del espacio de trabajo del agente.

interface SDKArtifact {  path: string;  sizeBytes: number;  updatedAt: string;}
const artifacts: SDKArtifact[] = await agent.listArtifacts();for (const artifact of artifacts) {  console.log(artifact.path, artifact.sizeBytes);}const buffer = await agent.downloadArtifact(artifacts[0].path);

La compatibilidad con los artefactos depende del entorno de ejecución. Actualmente, los agentes de programación del SDK local no devuelven artefactos y arrojan un error al usar downloadArtifact.

Gestión de recursos

Libera siempre los agentes cuando termines de usarlos. El patrón más limpio es await using:

await using agent = await Agent.create({ /* ... */ });// se libera automáticamente al salir del bloque

Para liberar recursos explícitamente:

await agent[Symbol.asyncDispose]();

agent.close() es la forma documentada de iniciar la liberación de recursos sin esperar. Symbol.asyncDispose funciona (await using se basa en él), pero close() es la opción que debes usar en código que no utiliza la sintaxis await using. agent.reload() aplica los cambios de configuración del sistema de archivos (hooks, MCP del proyecto, subagentes) sin liberar recursos.

Ciclo de vida del agente

Precargar un espacio de trabajo local

Resolver un espacio de trabajo local (reglas, skills, servidores MCP y archivos ignorados) es la parte más lenta del primer turno de un agente local y, en un repo grande, puede acapararlo. De forma predeterminada, ese coste se asume durante el primer send(). Un host que sabe dónde se ejecutarán sus agentes puede asumirlo antes con prewarmLocalWorkspace():

import { createAgentPlatform } from "@cursor/sdk";const platform = await createAgentPlatform();const release = await platform.prewarmLocalWorkspace({  apiKey: process.env.CURSOR_API_KEY!,  local: { cwd: "/srv/checkout", settingSources: ["project"] },});// El primer send() en este espacio de trabajo se inicia de inmediato.await release(); // al cerrar

Pasa las mismas AgentOptions que usarán tus agentes de programación; el precalentamiento solo beneficia a los envíos cuyas opciones de espacio de trabajo coincidan. Llama a la función de liberación devuelta cuando se apague el host.

Volver a conectarse a un agente existente

Agent.resume(agentId) devuelve un nuevo handle de un agente ya existente. El entorno de ejecución se detecta automáticamente a partir del prefijo de ID (bc- corresponde a Cloud; cualquier otro, a Local), y el estado de la conversación se carga desde Cloud (Cloud) o el almacén local de puntos de control (Local). Así se puede continuar el trabajo tras reiniciar un proceso o permitir que otro worker retome un agente iniciado por otro proceso.

const agent = await Agent.resume("bc-abc123", {  apiKey: process.env.CURSOR_API_KEY!,});const run = await agent.send("Apply the suggested fix");const result = await run.wait();

Si la ejecución ya estaba en curso cuando te reconectaste, Agent.getRun(runId, { runtime: "cloud", agentId }) (o su equivalente local) devuelve un Run en el que puedes usar stream(), wait() o cancel().

Contexto de la conversación

Los agentes locales conservan el estado de la conversación en un almacén de puntos de control. De forma predeterminada, se trata de SQLite en disco en tu directorio personal; puedes sustituirlo por JSONL o un backend personalizado con local.store. Cada llamada a agent.send() carga el punto de control más reciente de ese agente y se lo pasa al modelo, de modo que los mensajes de seguimiento ven el mismo contexto con el que terminó la ejecución anterior. El almacén se conserva tras reiniciar el proceso, por lo que Agent.resume(agentId) desde un proceso completamente nuevo retoma donde lo dejó el anterior.

Los agentes en la nube conservan el estado en el servidor. Al volver a conectarte desde cualquier lugar, obtendrás la misma conversación.

Algunas cosas que parecen una pérdida de contexto, pero no lo son:

  • Un nuevo Agent.create() siempre inicia un agente nuevo con un agentId nuevo. Para continuar una conversación existente, captura agent.agentId en la primera llamada y usa Agent.resume(agentId) más adelante.
  • Agent.prompt() crea, ejecuta y libera en una sola operación. No hay una segunda interacción; así funciona.
  • Los mcpServers en línea no se conservan entre llamadas a Agent.resume() porque suelen contener secretos. Vuelve a proporcionarlos al reanudar o usa una configuración de MCP basada en archivos.

Patrón de dispatcher

Un dispatcher gestiona un pool de agentes de programación y les asigna trabajo a medida que llega. La estructura es sencilla: mantener un map de agentId a SDKAgent de larga duración, enrutar las instrucciones entrantes según alguna key (usuario, repo, ticket) y ejecutar Agent.resume() desde el disco si un reinicio del proceso borró el map en memoria.

import { Agent, type SDKAgent } from "@cursor/sdk";const agents = new Map<string, SDKAgent>();async function getAgent(key: string, savedId?: string): Promise<SDKAgent> {  const existing = agents.get(key);  if (existing) return existing;  const agent = savedId    ? await Agent.resume(savedId, {        apiKey: process.env.CURSOR_API_KEY!,      })    : await Agent.create({        apiKey: process.env.CURSOR_API_KEY!,        model: { id: "composer-2.5" },        local: { cwd: process.cwd() },      });  agents.set(key, agent);  return agent;}async function handleMessage(key: string, prompt: string, savedId?: string) {  const agent = await getAgent(key, savedId);  const run = await agent.send(prompt);  return run.wait();}

Los flujos SSE de Cloud conservan los eventos acumulados durante un periodo tras el inicio de la ejecución, por lo que un dispatcher que transmite a muchos suscriptores puede llamar a run.stream() desde cada suscriptor sin perder eventos anteriores. Para ejecuciones de Cloud muy prolongadas, los dispatchers suelen distribuir las llamadas a run.wait() y permitir que los suscriptores consulten run.conversation() si necesitan la transcripción estructurada.

Almacenes de agentes locales

Los agentes locales guardan en disco los metadatos del agente, los puntos de control de la conversación, las ejecuciones y sus eventos para que los mensajes de seguimiento y Agent.resume() se conserven tras reiniciar el proceso. De forma predeterminada, el SDK usa SQLite en disco a través de node:sqlite. Cuando ese módulo no está disponible, Agent.create() lanza un ConfigurationError en lugar de recurrir a otra alternativa. Puede cambiar a otro backend con local.store.

El SDK incluye dos backends y permite usar uno propio:

AlmacénImportarCuándo usarlo
SqliteLocalAgentStore@cursor/sdk/sqliteSQLite en disco en la raíz de estado del espacio de trabajo.
JsonlLocalAgentStore@cursor/sdkArchivos JSON portátiles delimitados por saltos de línea (NDJSON) en el directorio que elija. Fáciles de inspeccionar, copiar y comparar.
LocalAgentStore personalizadoSu códigoGuarde datos en cualquier lugar: en memoria, Redis, Postgres o una base de datos alojada. Implemente la interfaz o componga subalmacenes.

Los agentes en la nube conservan los datos en el servidor, por lo que local.store solo se aplica a los agentes locales.

Almacén JSONL

JsonlLocalAgentStore escribe cuatro archivos NDJSON (agents.ndjson, runs.ndjson, run_events.ndjson, checkpoints.ndjson) en el directorio que indiques. Crea una instancia y asígnala a local.store.

import { Agent, JsonlLocalAgentStore } from "@cursor/sdk";const store = new JsonlLocalAgentStore("/var/lib/cursor-agents");const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  local: { cwd: process.cwd(), store },});

Pasa la misma instancia de almacén a Agent.resume() y a las API locales de listado y obtención (Agent.list, Agent.get, Agent.listRuns, Agent.getRun) para que accedan a los mismos datos.

Establece un valor predeterminado para todo el proceso

Para no tener que pasar un almacén en cada llamada, establece un valor predeterminado una sola vez con Cherri Code.configure(). El local.store de cada llamada sigue teniendo prioridad cuando lo proporcionas.

import { Cherri Code, JsonlLocalAgentStore } from "@cursor/sdk";Cherri Code.configure({ local: { store: new JsonlLocalAgentStore("/var/lib/cursor-agents") } });// Las llamadas posteriores usan el almacén configurado, salvo que proporcionen uno propio.const agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  local: { cwd: process.cwd() },});

Pasa store: null a Cherri Code.configure({ local: { store: null } }) para eliminar el valor predeterminado anterior y volver a la selección predeterminada del almacén local del SDK.

Almacenes personalizados

Para persistir datos en otro lugar (en un Postgres compartido, Redis o un mapa en memoria para pruebas), implementa LocalAgentStore. Consta de cuatro subalmacenes, cada uno con una pequeña interfaz CRUD a la que llama el SDK:

interface LocalAgentStore {  readonly agents: LocalAgentStoreAgents;         // filas de metadatos de agentes de programación  readonly checkpoints: LocalAgentStoreCheckpoints; // blobs de conversación direccionados por contenido  readonly runs: LocalAgentStoreRuns;             // filas de ejecuciones  readonly runEvents: LocalAgentStoreRunEvents;   // registro de eventos de ejecución de solo anexado}

Implemente la interfaz directamente o cree cada subalmacén por separado y combínelos con composeLocalAgentStore:

import { composeLocalAgentStore } from "@cursor/sdk";const store = composeLocalAgentStore({  agents: myAgentsTable,  checkpoints: myCheckpointBlobs,  runs: myRunsTable,  runEvents: myRunEventLog,});

Los subalmacenes replican las tablas predeterminadas de SQLite: agents contiene una fila por agente (con un puntero ligero a latestCheckpoint.rootBlobId), checkpoints contiene los blobs de conversación direccionados por contenido a los que apuntan esos punteros, runs contiene una fila por ejecución y runEvents es el registro de flujo de solo adición. Los subalmacenes de catálogo se paginan con un cursor / nextCursor opaco; el registro de eventos de ejecución se reanuda con un afterOffset / nextOffset exclusivo. Consulte los tipos exportados LocalAgentStore, LocalAgentDocument, LocalAgentRunDocument y los tipos relacionados para conocer las estructuras exactas.

Referencia de configuración

AgentOptions

PropiedadTipoPredeterminadoDescripción
modelModelSelectionObligatorio antes del primer send() local; en la nube se usa el valor predeterminado resuelto por el servidorModelo que se usará. Consulta ModelSelection.
apiKeystringVariable de entorno CURSOR_API_KEYClave de API de usuario o clave de cuenta de servicio. Las claves de administrador de equipo aún no son compatibles.
namestringGenerado automáticamenteNombre del agente legible para humanos, devuelto como name en Agent.list() / Agent.get().
localLocalAgentOptionsConfiguración del agente local. Consulta LocalAgentOptions.
cloudCloudAgentOptionsConfiguración del agente en la nube.
mcpServersRecord<string, McpServerConfig>Definiciones de servidores MCP en línea.
agentsRecord<string, AgentDefinition>Definiciones de subagentes.
toolsToolName[]Conjunto de herramientas predeterminadoRestringe el conjunto de herramientas: solo se ofrecen al modelo las herramientas integradas enumeradas. [] significa que no se ofrecen herramientas integradas. Solo para agentes locales.
disallowedToolsToolName[]Elimina herramientas del conjunto de herramientas; todas las demás siguen disponibles. Denegar prevalece al combinarse con tools. Solo para agentes locales.
systemPromptstringInstrucción integrada de Cherri CodeReemplaza la instrucción del sistema del bucle del agente principal. No puede estar vacía. Solo para agentes locales, y no se conserva entre llamadas a Agent.resume().
agentIdstringGenerado automáticamenteID de agente persistente. Proporciónalo para mantener un ID estable entre invocaciones.
idempotencyKeystringGenerado automáticamente para la nubeClave de idempotencia opcional generada por el cliente.
mode"agent" | "plan""agent"Modo de conversación inicial para la primera ejecución del agente. Consulta Modo de conversación.

LocalAgentOptions

Configuración para agentes locales, que se pasa como local a Agent.create(). También se exporta como tipo independiente para Partial<LocalAgentOptions>.

PropiedadTipoPredeterminadoDescripción
cwdstringDirectorio de trabajo principal para el shell predeterminado y el ámbito del almacén de agentes.
dirsstring[]Carpetas de espacio de trabajo adicionales para configuraciones con varias raíces. Se fusionan con cwd (se eliminan los duplicados) para cargar las reglas, las skills y el contexto del espacio de trabajo desde cada ruta.
settingSourcesSettingSource[]Capas de ajustes ambientales que se cargarán: "project", "user", "team", "mdm", "plugins" o "all".
sandboxOptions{ enabled: boolean }{ enabled: false }Configuración de Sandbox.
autoReviewbooleanfalseEnruta las llamadas a herramientas locales mediante Auto-review.
customToolsRecord<string, SDKCustomTool>Herramientas personalizadas expuestas como el servidor MCP custom-user-tools.
storeLocalAgentStoreAlmacén predeterminado del SDKPersistencia proporcionada por el almacén de agentes locales.
enableAgentRetriesbooleantrueActiva el reintento automático ante errores de transporte y bloqueos en ejecuciones de agentes locales. Establece false para mostrar los errores de transporte en el primer fallo.

CloudAgentOptions

PropiedadTipoPredeterminadoDescripción
env{ type: "cloud"; name?: string } | { type: "pool"; name?: string } | { type: "machine"; name?: string }{ type: "cloud" }Destino del entorno de ejecución. cloud usa VM alojadas por Cursor; establezca name para usar un entorno alojado por Cherri Code guardado. pool y machine dirigen el trabajo a workers autohospedados que usted ejecuta. Omita repos y deje env con el valor predeterminado para un agente sin repositorio y con un espacio de trabajo vacío. Los entornos alojados por Cherri Code con nombre y repos explícitos son mutuamente excluyentes.
reposArray<{ url: string; startingRef?: string; prUrl?: string }>Repositorios que se clonarán en la VM. Pase una entrada para un agente de un solo repositorio o hasta 20 para un agente multirreposición. Omita o pase [] para un agente sin repositorio. Es mutuamente excluyente con un env.name con nombre en entornos alojados por Cherri Code. Pase prUrl para vincular el agente a una PR existente.
workOnCurrentBranchbooleanfalseHaga push de los commits a la rama existente en lugar de crear una nueva.
autoCreatePRbooleanfalseAbra una PR cuando finalice la ejecución.
openAsCursorGithubAppbooleantrue para claves de cuentas de servicio, false para claves de usuarioAbra las PR como la aplicación de GitHub de Cherri Code en lugar de como el propietario de la clave de API. El valor resuelto se devuelve al crear, obtener y listar.
skipReviewerRequestbooleanfalseOmita solicitar que el usuario que realiza la llamada sea revisor de la PR.
envVarsRecord<string, string>Variables de entorno con ámbito de sesión para agentes en la nube.
metadataRecord<string, string>Etiquetas de cadena propiedad de quien realiza la llamada, persistidas en el agente en la nube. Consulte Metadatos del agente.

AgentDefinition

PropiedadTipoPredeterminadoDescripción
descriptionstringobligatorioCuándo usar este subagente. Se muestra al agente principal para que sepa cuándo generarlo.
promptstringobligatorioInstrucción del sistema para el subagente.
modelModelSelection | "inherit""inherit"Anulación de modelo. Pasa "inherit" para usar la selección del agente principal.
mcpServersArray<string | Record<string, McpServerConfig>>Se acepta por compatibilidad futura. Las referencias en forma de cadena se ignoran y las configuraciones en línea lanzan un ConfigurationError; los subagentes heredan los servidores MCP del agente principal.

ModelSelection

interface ModelSelection {  id: string;  params?: ModelParameterValue[];}interface ModelParameterValue {  id: string;  value: string;}

id es el identificador del modelo (por ejemplo, "composer-2.5" o "auto-smart"). params contiene parámetros específicos de cada modelo, como el esfuerzo de razonamiento o optimize_for de Router. Usa Cherri Code.models.list() para consultar los identificadores válidos, las definiciones de parámetros y las variantes preestablecidas disponibles para tu cuenta. Consulta Cherri Code Router para conocer el contrato de selección de Router.

McpServerConfig

type McpServerConfig =  // stdio  | {      type?: "stdio";      command: string;      args?: string[];      env?: Record<string, string>;      cwd?: string;       // solo local; Cloud rechaza este campo    }  // HTTP / SSE  | {      type?: "http" | "sse";      url: string;      headers?: Record<string, string>;   // se transmiten tal cual; Authorization funciona aquí      auth?: {        CLIENT_ID: string;        CLIENT_SECRET?: string;        scopes?: string[];      };    };

En los servidores HTTP que se ejecutan en la nube, el backend de Cherri Code gestiona headers y auth. Los campos confidenciales se ocultan antes de que la VM los vea. En los servidores stdio en la nube, los valores de env se pasan a la VM (trátalos como cualquier otro secreto de entorno de ejecución).

SDKUserMessage

interface SDKUserMessage {  text: string;  images?: SDKImage[];}

La versión estructurada del argumento message de agent.send(). Úsala para enviar imágenes junto con texto.

SDKImage

type SDKImage =  | { url: string; dimension?: SDKImageDimension }  | { data: string; mimeType: string; dimension?: SDKImageDimension };interface SDKImageDimension {  width: number;  height: number;}

Proporciona una url remota o data codificados en base64 con un mimeType.

SettingSource

type SettingSource =  | "project"  | "user"  | "team"  | "mdm"  | "plugins"  | "all";

Controla qué capas de ajustes almacenados en disco carga un agente local. Los agentes en la nube siempre cargan project / team / plugins e ignoran este campo.

ValorOrigen
"project".cursor/ en el espacio de trabajo
"user"~/.cursor/
"team"Ajustes de equipo sincronizados desde el panel de control
"mdm"Ajustes empresariales gestionados por MDM
"plugins"Ajustes proporcionados por plugins
"all"Forma abreviada de todos los anteriores

ListResult

interface ListResult<T> {  items: T[];  nextCursor?: string;}

Lo devuelven Agent.list() y Agent.listRuns(). nextCursor no está presente cuando no hay más páginas.

Errores

Todos los errores del SDK extienden CursorSdkError (reexportado como CursorAgentError para mantener la compatibilidad con versiones anteriores). Use isRetryable para gestionar la lógica de reintentos, y code / status / requestId para diagnósticos.

class CursorSdkError extends Error {  readonly isRetryable: boolean;  readonly code?: string;       // código estable del SDK o del backend  readonly status?: number;     // estado HTTP, si está disponible  readonly cause?: unknown;     // error subyacente encapsulado  readonly endpoint?: string;  readonly requestId?: string;  readonly operation?: string;  // operación del SDK que generó el error}
Clase de errorMensaje típicoCausa probableSolución recomendada
AuthenticationError"Clave de API no válida"Falta CURSOR_API_KEY, es incorrecta, el token caducó o un administrador deshabilitó la clave.Genere una clave nueva en API Keys (usuario) o Team settings (cuenta de servicio). Confirme que la clave tenga permiso para la operación.
RateLimitError"Límite de uso excedido" o "Límite de consumo excedido"Límite de ráfaga o límite mensual de consumo.Espere con una demora exponencial (el SDK informa isRetryable: true en casos transitorios). Para el límite mensual, aumente el límite de consumo del plan.
ConfigurationError"Nombre de modelo incorrecto", "Clave de API no compatible", "Archivo no compatible"model.id no válido, faltan params obligatorios, archivo no compatible en una llamada a herramienta o una política de administrador que bloquea la solicitud.Llame a Cherri Code.models.list() para confirmar el ID y los parámetros. Compruebe que existan el repositorio y las rutas de archivo.
AgentBusyError"El agente está ocupado"Se envía un mensaje de seguimiento mientras el mismo agente en la nube ya tiene una ejecución en estado CREATING o RUNNING.Espere a que finalice la ejecución activa, cancélela o consulte Agent.listRuns() antes de volver a enviar.
IntegrationNotConnectedError"La integración de [proveedor] no está conectada"Se crea un agente en la nube para un repositorio cuyo proveedor de SCM no está conectado a su equipo de Cherri Code.Abra error.helpUrl para volver a conectar el proveedor y, después, vuelva a intentarlo.
NetworkError"Servicio no disponible", "Tiempo de espera agotado"Problema transitorio del backend, partición de red o plazo excedido.Vuelva a intentarlo con espera progresiva. Inspeccione error.requestId si necesita abrir un ticket de soporte.
UnsupportedRunOperationError"La operación "stream" no es compatible con este entorno de ejecución"Se llama a un método de Run que el entorno de ejecución actual no puede admitir (p. ej., streaming en una ejecución local recuperada de nuevo que ya finalizó).Compruébelo primero con run.supports(operation) / run.unsupportedReason(operation).
AgentNotFoundError"Agente no encontrado"El agente solicitado no existe o no es visible en el espacio de trabajo local resuelto.Compruebe el ID del agente, cwd y local.store.
UnknownAgentErrorMensaje definido por el servidorError no clasificado del backend o del entorno de ejecución.Inspeccione error.code y error.cause para obtener más detalles.

IntegrationNotConnectedError

class IntegrationNotConnectedError extends ConfigurationError {  readonly provider: string;   // p. ej.: "github", "gitlab", "azure-devops"  readonly helpUrl: string;    // enlace al panel de control para volver a conectar}

El mensaje de error predeterminado no incluye helpUrl, así que regístralo explícitamente:

import { Agent, IntegrationNotConnectedError } from "@cursor/sdk";try {  await Agent.create({    apiKey: process.env.CURSOR_API_KEY!,    cloud: {      repos: [{ url: "https://github.com/your-org/private-repo" }],    },  });} catch (err) {  if (err instanceof IntegrationNotConnectedError) {    console.error(err.provider, err.helpUrl);  }}

AgentBusyError

class AgentBusyError extends CursorAgentError {}

isRetryable es false para agent_busy. Si reintentas de inmediato, seguirá fallando hasta que la ejecución activa alcance un estado terminal o la canceles. Otras respuestas 409, como agent_archived, generan ConfigurationError en su lugar.

Espera a que finalice la ejecución activa, cancélala con run.cancel() o consulta periódicamente Agent.listRuns() antes de volver a enviar:

import { Agent, AgentBusyError } from "@cursor/sdk";const agent = await Agent.resume("bc-00000000-0000-0000-0000-000000000001");try {  await agent.send({ text: "Also add tests for the auth middleware." });} catch (err) {  if (err instanceof AgentBusyError) {    const runs = await Agent.listRuns(agent.agentId, { runtime: "cloud", limit: 1 });    const active = runs.items[0];    if (active?.status === "running") {      await active.cancel();    }    await agent.send({ text: "Also add tests for the auth middleware." });    return;  }  throw err;}

Los agentes locales no devuelven agent_busy. Usa send({ local: { force: true } }) para finalizar una ejecución local atascada antes de iniciar otra.

UnsupportedRunOperationError

class UnsupportedRunOperationError extends ConfigurationError {  readonly operation: RunOperation;}

Se genera cuando una operación de Run no está disponible en el entorno de ejecución actual. Usa run.supports(operation) y run.unsupportedReason(operation) para comprobarlo antes de realizar la llamada.

Limitaciones conocidas

  • Los mcpServers en línea no se conservan entre llamadas a Agent.resume(). Vuelva a proporcionarlos al reanudar si es necesario.
  • Las herramientas personalizadas (local.customTools), Auto-review (local.autoReview), los almacenes personalizados (local.store), las restricciones de herramientas (tools, disallowedTools) y systemPrompt solo están disponibles para agentes locales. Los agentes en la nube rechazan local.customTools en send() y se conservan en el servidor.
  • tools, disallowedTools y systemPrompt no se conservan en el agente. Vuelva a proporcionarlos en Agent.resume() para mantenerlos.
  • La descarga de artefactos no está implementada para agentes locales (agent.listArtifacts() devuelve una lista vacía y agent.downloadArtifact() genera una excepción).
  • local.settingSources (y las rutas de MCP/subagente basadas en archivos que controla) no se aplica a los agentes en la nube. La nube siempre carga project / team / plugins.
  • Los hooks solo se basan en archivos (.cursor/hooks.json). No hay callbacks programáticos.
  • El SDK no detecta automáticamente las credenciales de una instalación local de la app de Cherri Code. Establezca CURSOR_API_KEY (o pase apiKey) explícitamente, o genere una clave con Cherri Code.auth.login().
  • El modo local requiere Node.js 22.13 o una versión posterior y compatibilidad de la plataforma con sandbox-helper. El almacén predeterminado necesita node:sqlite; cuando no está disponible, Agent.create() genera un ConfigurationError hasta que configure JsonlLocalAgentStore u otro almacén.