Cherri Code TypeScript SDK
O pacote @cursor/sdk permite chamar o agente do Cherri Code pelo seu próprio código. O mesmo agente executado no IDE, na CLI e no app web do Cherri Code agora pode ser usado em scripts TypeScript. Execute a skill /sdk no Cherri Code para começar.
Exemplos completos estão no Cherri Code Cookbook: um início rápido do SDK, uma ferramenta de prototipagem para criação de apps, um quadro kanban para agentes na nuvem e uma CLI de agente de programação. Bons pontos de partida para bots de correção automática em CI, workers de triagem de bugs, revisões de código, agentes incorporados ao produto e orquestradores.
Visão geral
O SDK abstrai os tempos de execução locais e em nuvem em uma única interface. Você escreve o mesmo código, independentemente de onde o agente é executado.
| Tempo de execução | O que faz | Quando usar |
|---|---|---|
| Local | Executa o loop do agente inline no seu processo Node. Os arquivos vêm do disco. | Scripts de desenvolvimento e verificações de CI em uma árvore de trabalho. |
| Nuvem (hospedado pelo Cherri Code) | Executa em uma VM isolada com seu repositório clonado. O Cherri Code executa as VMs. | Quando o chamador não tem o repositório, você quer vários agentes em paralelo ou as execuções precisam continuar mesmo após o chamador se desconectar. |
"Local" descreve onde o loop do agente e o acesso ao sistema de arquivos são executados, não onde o modelo é executado. Toda a inferência passa pelos modelos hospedados do Cherri Code nos dois modos. O modo local mantém seus arquivos na sua máquina; o modo em nuvem é executado em um ambiente do Cherri Code. O próprio modelo é hospedado em ambos os casos.
O tempo de execução é definido pela chave passada para Agent.create() (local ou cloud). Use a mesma CURSOR_API_KEY para ambos.
Para a REST API, consulte a Cloud Agents API. Para outras linguagens, consulte a Ponte do SDK.
Autenticação
Defina CURSOR_API_KEY (ou passe apiKey) antes de criar um agente. Em hosts interativos sem uma chave provisionada previamente, Cherri Code.auth.login() emite e armazena uma chave por meio do login no navegador.
O SDK aceita chaves de API de usuário e chaves de API de contas de serviço para execuções locais e na nuvem. As chaves de API de administração da equipe ainda não são compatíveis.
- Chave de API de usuário no Cherri Code Dashboard → API Keys
- Chave de API de conta de serviço nas Configurações da equipe. Consulte Contas de serviço
export CURSOR_API_KEY="your-key"Uso e faturamento
As execuções do SDK seguem as mesmas regras de preços, pools de solicitações e Privacy Mode que as execuções do IDE e dos Cloud Agents. Os gastos aparecem no dashboard de uso da sua equipe com a tag SDK.
As chaves de API de conta de serviço são cobradas da equipe proprietária da conta de serviço. As chaves de API de usuário são cobradas no plano desse usuário.
Para consultar a contagem de tokens por execução no código, veja Uso de tokens. Para consultar o uso faturado e o custo em dólares das execuções de um agente, veja Agent.getUsage().
Conceitos principais
| Conceito | Descrição |
|---|---|
| Agente | Contêiner durável que armazena o estado da conversa, a configuração do espaço de trabalho e outras configurações. Persiste entre vários prompts. |
| Execução | Um envio de prompt. Tem seu próprio stream, status, resultado e cancelamento. |
| SDKMessage | Eventos normalizados do stream emitidos durante uma execução. Têm o mesmo formato em todos os tempos de execução. |
Instalação
npm install @cursor/sdkO nome do pacote começa com @. O cursor/sdk sem o @ não existe no npm.
Suporte ao tempo de execução
O SDK requer Node.js 22.13 ou posterior. Ele inclui binários @cursor/sdk-<os>-<arch> específicos para cada plataforma, destinados a sandboxing e ripgrep, portanto é um pacote voltado principalmente ao Node.
Importar @cursor/sdk não carrega imediatamente a pilha de agentes locais. O executor local é carregado na primeira acquire local, portanto quem usa apenas a nuvem ou apenas tipos não arca com o custo da importação local. O primeiro agente local em um processo faz uma importação única, e o módulo permanece em cache.
@cursor/sdk publica arquivos .d.ts independentes, portanto os tipos são resolvidos sem incluir pacotes não publicados do espaço de trabalho. Após a atualização, execute novamente a verificação de tipos. Tipos de stream, como TurnEndedUpdate, são resolvidos como tipos reais em vez de any.
Pacotes de arquivo único e executáveis compilados
A compilação padrão carrega partes do SDK de forma preguiçosa em tempo de execução. Empacotadores de arquivo único não conseguem acompanhar esses carregamentos, portanto um app compilado falha na primeira chamada a Agent.create() com um erro como Cannot find module './986.js'. O SDK também é distribuído com uma compilação plana, em um único arquivo, com a mesma API pública. Ela coloca tudo em um arquivo, para que seu empacotador incorpore todo o SDK antecipadamente.
No Bun, @cursor/sdk é resolvido para a compilação plana automaticamente. Importe-o normalmente e compile:
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-agentPara outros empacotadores de arquivo único, como o esbuild, ou para fixar explicitamente a compilação plana, importe as entradas empacotadas:
| Entrada | Conteúdo |
|---|---|
@cursor/sdk/bundled | Tudo o que @cursor/sdk exporta. |
@cursor/sdk/bundled/sqlite | SqliteLocalAgentStore, equivalente a @cursor/sdk/sqlite. |
Alguns pontos importantes:
- A compilação plana é executada no Bun, incluindo executáveis de
bun build --compile. Ela também carrega no Node, mas o store SQLite não está disponível nele, então configureJsonlLocalAgentStorepor meio delocal.storeouAgent.create()gera umConfigurationError. Continue importando@cursor/sdkem apps Node que não são distribuídos como um único arquivo. zod,@bufbuild/protobufe os pacotes@connectrpc/*são resolvidos a partir da sua própria instalação. Eles vêm com@cursor/sdk, e seu empacotador incorpora uma única cópia compartilhada, para que os schemas Zod passados para ferramentas personalizadas continuem funcionando.- Binários nativos não podem ficar dentro de um bundle JavaScript. O sandboxing e o ripgrep integrado são fornecidos nos pacotes
@cursor/sdk-<os>-<arch>específicos de cada plataforma. Coloquenode_modules/@cursor/sdk-<os>-<arch>/ao lado do executável compilado, e o SDK o encontrará lá. Sem ele, a busca recorre aorgnoPATH, e ativarsandboxOptionsgera umConfigurationError.
Os tipos são resolvidos para as entradas empacotadas da mesma forma que para @cursor/sdk. Não são necessárias alterações na configuração do TypeScript.
Início rápido
A maneira mais rápida de começar: um agente local na sua árvore de trabalho atual, transmitindo eventos à medida que chegam. A configuração da Cloud está em Criar agentes abaixo.
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 é uma SDKMessage discriminada. Streaming mostra como extrair o texto do assistente, processar chamadas de ferramenta e liberar recursos com await using. Para um prompt de execução única (criar, executar, descartar), consulte Agent.prompt().
Por padrão, o agente local executa chamadas de ferramenta (shell, edição, gravação etc.) sem
pedir aprovação; no modo headless, não há solicitação de intervenção humana. Para
controlar chamadas de ferramenta, configure hooks (como beforeShellExecution ou
preToolUse) ou execute com local.sandboxOptions.enabled: true.
Criar agentes
function Agent.create(options: AgentOptions): Promise<SDKAgent>;Agent.create() valida as opções e retorna um handle imediatamente. Passe local ou cloud para selecionar o tempo de execução.
// Agente localconst agent = await Agent.create({ apiKey: process.env.CURSOR_API_KEY!, model: { id: "composer-2.5" }, local: { cwd: "/path/to/repo" },});// Agente em nuvemconst 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 é preenchido imediatamente. Agentes locais recebem um ID agent-<uuid>; agentes em nuvem recebem um ID bc-<uuid>.
Agentes em nuvem iniciados pelo SDK não aparecem na lista padrão de agentes. Para visualizá-los no Cherri Code Web ou em uma janela do Cherri Code, clique em Filtro > Origem > SDK.
Agentes na nuvem sem repositório
Os agentes na nuvem podem ser executados em uma VM vazia, sem repositório. Passe cloud com uma lista repos vazia ou omita repos por completo. Ao omitir cloud, o tempo de execução local é selecionado.
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);Agentes sem repositório precisam estar habilitados para sua conta ou equipe. Chaves de API com escopo de repositório não podem criá-los; use uma chave de conta de serviço irrestrita ou uma chave de API de usuário.
Variáveis de ambiente da sessão
Para agentes na nuvem, passe cloud.envVars quando uma execução precisar de credenciais temporárias ou outros valores que devam existir apenas para esse 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!, }, },});Esses valores são criptografados quando armazenados, injetados no shell do agente em nuvem e excluídos junto com o agente. Não é possível usar envVars com um agentId fornecido pelo chamador; omita agentId e leia o ID emitido pelo servidor em agent.agentId após o primeiro send(). Os nomes das variáveis não podem começar com CURSOR_.
Para valores que devem existir apenas durante uma única execução, passe-os em agent.send(). Consulte Variáveis de ambiente por execução.
Metadados do agente
Anexe suas próprias tags de texto a um agente em nuvem com cloud.metadata. As tags são
salvas com o agente e retornadas em SDKAgentInfo.metadata por
Agent.get() e Agent.list(). Essas tags não correspondem à API de metadados do agente
na VM, que expõe o id, proprietário, turno e espaço de trabalho da
execução atual de dentro da 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 do modelo
Use model.params para passar opções específicas de cada modelo, como esforço de raciocínio. Os IDs e valores dos parâmetros variam conforme o modelo. Use Cherri Code.models.list() para descobrir os parâmetros compatíveis e as variantes predefinidas disponíveis para sua conta.
Nos planos legados de precificação por solicitação, o Cherri Code ativa o Modo Max automaticamente quando o modelo selecionado exige isso.
O Composer 2 foi descontinuado. As solicitações do SDK que ainda passam composer-2 ou
composer-2-fast são redirecionadas para o Composer 2.5 durante a autenticação, para que os scripts existentes
continuem funcionando. Se você dependia da variante composer-2-fast, confirme
se o comportamento rápido ainda corresponde ao esperado.
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
O Cherri Code Router seleciona um modelo para cada solicitação Auto. No SDK, o Router é o modelo auto-smart, com o parâmetro optimize_for. Ele está disponível nos planos Teams e Enterprise. Admins do Enterprise devem habilitar o Router para a equipe antes que auto-smart apareça no catálogo.
O Cherri Code SDK é um SDK de agentes, não uma API independente de inferência de modelos ou de conclusões de chat. O Router escolhe modelos para execuções de agentes do Cherri Code que podem analisar um espaço de trabalho, chamar ferramentas, executar comandos e editar arquivos. Atualmente, o Cherri Code não documenta um endpoint raw do Router para chamadas arbitrárias de modelos.
Selecione Custo, Balance ou Inteligência
Passe auto-smart e defina optimize_for explicitamente:
| Rótulo do produto | Valor do SDK |
|---|---|
| Custo | cost |
| Balance | balanced |
| Inteligência | intelligence |
Use Balance nos textos do produto. Use balanced apenas como o valor enviado pelo 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);Sempre informe optimize_for. Não o omita nem envie um valor default legado; a descoberta pelo catálogo é o contrato compatível.
Conheça o Router no catálogo de modelos
Cherri Code.models.list() retorna os modelos, as definições de parâmetros e as variantes predefinidas disponíveis para a conta e a equipe atuais da chave de API. O Cherri Code Router aparece como auto-smart quando está disponível. Os administradores da equipe podem desabilitar o Router ou restringir os modos de otimização que os membros podem selecionar.
Use o catálogo como fonte de verdade antes de codificar uma seleção:
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 }],};Alterne os modos em cada execução
Substitua o modelo em agent.send() para mudar o modo Router de uma execução:
const run = await agent.send("Handle this complex migration", { model: { id: "auto-smart", params: [{ id: "optimize_for", value: "intelligence" }], },});As substituições de modelo por Run permanecem ativas. Envios posteriores sem substituição continuam usando a nova seleção. Consulte Substituição de modelo por Run.
IDs de modelo: auto-smart, auto e default
| Seleção | Significado |
|---|---|
auto-smart com optimize_for | Cherri Code Router. Use quando quiser custo, Balance ou inteligência. |
{ id: "auto" } | Fallback Auto selecionado pelo servidor quando um modelo específico não estiver no catálogo. Prefira auto-smart quando precisar de um modo Router explícito. |
Omitir optimize_for ou enviar default | Não é um contrato Router compatível. Sempre descubra os valores permitidos e passe cost, balanced ou intelligence. |
Faturamento e pool de roteamento
- Todos os modos Auto são cobrados pelo preço de tabela do modelo para o qual cada solicitação é roteada.
- O modelo subjacente pode mudar entre solicitações. Prefira um ID de modelo fixo quando precisar de comparações reproduzíveis.
- Listas de permissão de modelos corporativos definem o pool de roteamento. Bloquear modelos necessários pode desativar o Router.
Para consultar os preços atuais e o pool de roteamento, veja Cherri Code Router e Modos Auto.
Solução de problemas: Router ausente
Se auto-smart não estiver disponível ou um modo de otimização for rejeitado:
- Chame
Cherri Code.models.list(). - Confirme se
auto-smartestá no resultado. - Confirme se
optimize_forinclui o valor desejado (cost,balancedouintelligence). - Confirme se o Router está habilitado para a equipe associada à chave de API.
- Se você fizer parte de várias equipes, confirme se a chave está sendo usada no contexto da equipe pretendida.
- Verifique a política de acesso a modelos da equipe se o Router não estiver disponível ou não puder escolher um modelo subjacente válido.
Substituindo o system prompt
systemPrompt substitui o system prompt built-in do Cherri Code no loop do agente principal pelo seu próprio texto. O modelo perde a identidade de assistente de coding, o protocolo de uso de ferramentas e as orientações de comunicação, então reafirme tudo o que o agente ainda precisar. Os schemas das ferramentas, as regras e as skills continuam sendo carregados, e os subagentes mantêm seus próprios prompts.
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() },});- Apenas agentes locais. Combinar
systemPromptcomcloudlança umConfigurationError. - Não pode estar vazio. Um texto contendo apenas espaços em branco lança um
ConfigurationErroremAgent.create()ouAgent.resume(). - Não é persistido no agente. Passe
systemPromptnovamente emAgent.resume()para mantê-lo nas execuções de acompanhamento. - O acesso é habilitado por conta. Sem ele, o primeiro
send()falha com um erro que menciona--system-prompt.
SDKAgent
O handle retornado por Agent.create() e 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>;}| Membro | Descrição |
|---|---|
agentId | Identificador estável do agente. agent-<uuid> para local, bc-<uuid> para cloud. |
model | Seleção de modelo atual. É atualizada após cada send({ model }) bem-sucedido. Permanece como undefined até ser definido (inclusive para agentes retomados cujo chamador não passou model). |
send | Inicia uma nova execução com o prompt fornecido. Retorna um handle Run. |
close | Inicia o descarte sem aguardar. Fire-and-forget. |
reload | Relê a configuração do sistema de arquivos (hooks, MCP do projeto, subagentes) sem descartar. |
[Symbol.asyncDispose] | Descarte assíncrono. Use com await using para limpeza automática. |
listArtifacts | Lista os arquivos produzidos pelo agente (somente cloud; local retorna uma lista vazia). |
downloadArtifact | Faz download de um arquivo pelo path (somente cloud; local gera uma exceção). |
getUsage | Consulta o uso de tokens faturado e o custo em dólares do agente. |
Agent.prompt()
function Agent.prompt(message: string, options?: AgentOptions): Promise<RunResult>;Conveniência de uso único: cria um agente, envia um único prompt, aguarda a execução terminar e libera os 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() },});Envio de mensagens
Cada agent.send() retorna uma Run. O agente mantém o contexto da conversa entre execuções; uma execução é a unidade de trabalho de um prompt.
Execução
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;}Streaming
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; }}// Mensagem de acompanhamento para o mesmo agente. O estado da conversa da execução// anterior é carregado automaticamente.const run2 = await agent.send("Fix it and add a regression test");await run2.wait();Para enviar imagens junto com texto:
const run = await agent.send({ text: "What's in this screenshot?", images: [{ data: base64Png, mimeType: "image/png" }],});Aguardar sem streaming
const result = await run.wait();console.log(result.status); // "finished" | "error" | "cancelled"console.log(result.result); // texto final do assistente, se disponívelconsole.log(result.error); // { message, code? } quando a execução falharconsole.log(result.model); // ModelSelection resolvido usado nesta execuçãoconsole.log(result.durationMs);console.log(result.usage); // TokenUsage acumulado ou undefined, se não estiver disponívelconsole.log(result.git); // { branches: [{ repoUrl, branch?, prUrl? }] } na nuvemO texto final do assistente está em result.result como uma string. Não há campos text, message, messages ou content para procurar. Se precisar da transcrição de cada etapa, chame run.conversation() para obter uma visualização estruturada 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);Cancelando uma execução
await run.cancel();Cancela a execução. O status muda para "cancelled", o stream em tempo real é abortado, as chamadas de ferramenta em andamento são interrompidas e run.wait() é resolvido com status: "cancelled". A saída parcial (texto do assistente gerado até o momento) permanece no objeto Run.
O cancelamento é compatível com execuções locais e na nuvem em andamento e não tem efeito se a execução já tiver terminado.
Direcionando uma execução em andamento
run.steer(text) injeta uma mensagem no turno que já está em execução, em vez de esperar que ele termine. A promise é resolvida assim que o turno confirma se anexou ou não o texto. complete_delivered significa que o turno recebeu a mensagem, portanto não a reenvie. revert_to_followup significa que o turno não a aceitou, portanto entregue-a com agent.send() após a execução.
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");}- Apenas para execuções locais. Execuções em nuvem e handles locais desanexados expõem
steer, mas sempre resolvemrevert_to_followup. - O steering funciona enquanto um subagente em foreground está em execução. O subagente passa para o background e continua em execução para que o turno do parent possa receber a mensagem.
steeré opcional emRune não é umRunOperation. Verifiquerun.steerantes de chamá-lo, em vez de usarrun.supports().
Consultando o estado da execução
console.log(run.status); // "running" | "finished" | "error" | "cancelled"const stop = run.onDidChangeStatus((status) => { console.log(`status changed to ${status}`);});// Chame `stop()` para remover o listener.// Visualização estruturada por turno da conversa acumulada nesta execuçãoconst turns = await run.conversation();run.conversation() retorna os ConversationTurn[] da execução (um turno de agente com etapas ou um turno de shell com comando e saída). Use-o para renderizar ou persistir o histórico estruturado da execução sem assinar o stream em tempo real.
Uso de tokens
As execuções informam o uso de tokens quando o tempo de execução o disponibiliza. Consulte o total acumulado em run.usage enquanto a execução estiver em andamento ou em result.usage após run.wait(). Ambos contêm um TokenUsage que soma o uso informado em cada turno e são undefined quando nenhum turno o informou (por exemplo, em uma execução cancelada que não concluiu nenhum turno ou em um tempo de execução que não expõe o uso).
interface TokenUsage { inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheWriteTokens: number; totalTokens: number; reasoningTokens?: number;}| Campo | Descrição |
|---|---|
inputTokens | Tokens do prompt enviados ao modelo. |
outputTokens | Tokens gerados pelo modelo. |
cacheReadTokens | Tokens obtidos do cache de prompt. |
cacheWriteTokens | Tokens gravados no cache de prompt. |
totalTokens | inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens. Não inclui reasoningTokens. |
reasoningTokens | Tokens de raciocínio, um subconjunto de outputTokens. Omitidos quando o modelo ou o tempo de execução não os informou. |
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 já está contabilizado em outputTokens, portanto totalTokens não o inclui para evitar contagem duplicada.
Para ver os valores por turno à medida que são transmitidos, trate o evento de stream usage (SDKUsageMessage). Ele é acionado uma vez ao final de cada turno que informou uso e contém o TokenUsage daquele turno. run.usage e result.usage permanecem cumulativos durante toda a execução.
for await (const event of run.stream()) { if (event.type === "usage") { console.log(`turn used ${event.usage.totalTokens} tokens`); }}A contagem de tokens é informada pelo tempo de execução e não diz nada sobre o custo. Para consultar o uso faturado e o custo em dólares das execuções de um agente, chame agent.getUsage().
Correlação de execuções com requestId
Cada agent.send() recebe um UUID gerado pela plataforma, disponibilizado como requestId tanto em Run quanto em RunResult. Use-o para vincular a execução de um script ou do CI aos logs de backend, ao analytics e às threads de suporte, em vez de tentar adivinhar apenas pelo agentId.
const run = await agent.send("Audit the auth middleware");console.log(run.requestId); // por exemplo, "6e0d261c-86a2-4383-89f0-9162c1c10662"const result = await run.wait();logger.info({ requestId: result.requestId }, "run finished");requestId é persistido com a execução e, por isso, é preservado nos stores locais em memória, SQLite e JSONL. Ele também é definido nas execuções na nuvem quando o backend retorna um. Registre-o junto com error.requestId dos erros, para que um único identificador abranja os fluxos de sucesso e falha.
Substituição de modelo por Run
O model passado para agent.send() substitui a seleção do agente para essa execução e permanece ativo: os envios subsequentes sem uma substituição continuam usando o novo modelo. Para voltar ao modelo anterior, passe outra substituição de model ou consulte a seleção atual em agent.model.
const run = await agent.send("Plan the refactor", { model: { id: "composer-2.5", params: [{ id: "fast", value: "true" }] },});console.log(agent.model); // atualizado para a substituição após o envio ser concluído com êxitorun.model e result.model refletem a seleção usada nesta execução específica e são imutáveis após o início da execução.
Variáveis de ambiente por execução
Agentes na nuvem também podem receber variáveis de ambiente para uma única execução. Passe cloud.envVars em agent.send(), e os valores serão injetados no shell do agente apenas nessa execução — quando ela terminar, serão removidos da VM, e a execução seguinte não terá acesso a eles. Esse é o formato adequado para credenciais rotacionadas entre turnos, como um token de deploy de curta duração emitido logo antes de pedir ao agente que o use.
const run = await agent.send("Deploy the preview environment", { cloud: { envVars: { DEPLOY_TOKEN: await mintShortLivedToken(), }, },});Se uma variável com escopo de execução tiver o mesmo nome de uma variável com escopo de agente em cloud.envVars em Agent.create(), o valor com escopo de execução prevalecerá nessa execução. Na execução seguinte, o valor com escopo de agente voltará a ser usado.
As variáveis por execução também funcionam no primeiro envio. O SDK as transmite durante a criação do agente, com escopo limitado à execução inicial, para que não sejam persistidas no agente. Assim como as variáveis com escopo de agente, elas são criptografadas quando armazenadas, e os nomes não podem começar com CURSOR_.
As variáveis de ambiente por execução são exclusivas de agentes na nuvem e não estão disponíveis para agentes executados em repositórios públicos. Para agentes locais, o processo do agente herda seu próprio ambiente; portanto, defina as variáveis no processo antes de chamar send().
Modo de conversa
Passe mode: "plan" ou mode: "agent" para definir se uma execução primeiro explora e planeja ou implementa as alterações diretamente. Consulte o modo Plan para entender como ele funciona no produto.
Defina mode em Agent.create() para configurar a primeira execução. Nas chamadas de acompanhamento a agent.send(), omita mode para manter o modo atual da conversa ou passe mode para alterná-lo somente nessa execução.
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();Transmissão de deltas brutos
run.stream() produz eventos SDKMessage normalizados. Para atualizações de nível mais baixo (texto por token, transmissão de args de chamadas de ferramenta, deltas de raciocínio, atualizações de tarefas aninhadas, limites de etapas), passe os callbacks onDelta e onStep para 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}`); },});Os callbacks são aguardados antes do processamento da próxima atualização, permitindo aplicar contrapressão. InteractionUpdate abrange 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 e alguns deltas de resumo e de saída do shell.
Opções por envio
| Propriedade | Tipo | Descrição |
|---|---|---|
model | ModelSelection | Substituição de modelo por envio. Se omitido, usa agent.model. Persistente: um envio bem-sucedido atualiza agent.model. |
mode | "agent" | "plan" | Substituição do modo de conversa por envio. Se omitido em mensagens de acompanhamento, mantém o modo atual da conversa. |
mcpServers | Record<string, McpServerConfig> | Definições inline de servidores MCP. Substitui integralmente os servidores definidos na criação para esta execução. |
onStep | (args: { step }) => void | Promise<void> | Callback após cada etapa concluída da conversa (texto, raciocínio ou lote de ferramentas). |
onDelta | (args: { update }) => void | Promise<void> | Callback para cada InteractionUpdate bruto. |
idempotencyKey | string | Chave de idempotência opcional gerada pelo cliente para o envio. |
cloud.envVars | Record<string, string> | Apenas para agentes na nuvem. Variáveis de ambiente por execução injetadas nesta execução e removidas quando ela termina. Substitui, pelo nome e apenas nesta execução, cloud.envVars com escopo de agente. |
local.force | boolean | Apenas para agentes locais. O padrão é false. Encerra uma execução ativa travada antes de iniciar esta mensagem. Na nuvem, o servidor retorna 409 agent_busy, portanto não é necessário equivalente. |
local.customTools | Record<string, SDKCustomTool> | Apenas para agentes locais. Ferramentas personalizadas para esta execução. Substitui local.customTools definido na criação do agente para essa execução. |
As próximas três seções trazem referências detalhadas para SDKMessage, InteractionUpdate e ConversationTurn. Leia rapidamente ou pule na primeira leitura; Retomar agentes dá continuidade à narrativa.
Eventos de stream
Eventos de run.stream(). Diferencie-os pelo type. Todos os eventos incluem agent_id e run_id.
type SDKMessage = | SDKSystemMessage | SDKUserMessageEvent | SDKAssistantMessage | SDKThinkingMessage | SDKToolUseMessage | SDKStatusMessage | SDKTaskMessage | { type: "request"; agent_id: string; run_id: string; request_id: string; } | SDKUsageMessage;type | Descrição | Campos principais |
|---|---|---|
"system" | Metadados de inicialização. Emitidos uma vez no início de uma execução. | subtype? ("init"), model?, tools? |
"user" | Eco do prompt do usuário desta execução. | message.content: TextBlock[] |
"assistant" | Saída de texto do modelo. | message.content: (TextBlock | ToolUseBlock)[] |
"thinking" | Conteúdo de raciocínio. | text, thinking_duration_ms? |
"tool_call" | Ciclo de vida da chamada de ferramenta. Emitido no início com args e novamente na conclusão com result. | call_id, name, status, args?, result?, truncated? |
"status" | Transições do ciclo de vida da execução na nuvem. | status, message? |
"task" | Marcos e resumos no nível da tarefa. | status?, text? |
"request" | Aguardando entrada ou aprovação do usuário. | request_id |
"usage" | Uso de tokens por turno, emitido uma vez ao fim do turno quando informado pelo tempo de execução. | usage (TokenUsage) |
Os dados do resultado (texto final, modelo, duração, uso acumulado de tokens e metadados do Git) ficam no objeto Run após a conclusão do stream. Use run.wait() para acessá-los.
O schema de chamada de ferramenta não é estável. Os payloads
argseresultdos eventostool_callrefletem a estrutura interna de cada ferramenta e podem mudar conforme as ferramentas evoluem. Os nomes das ferramentas também podem ser renomeados ou substituídos. Trateargseresultcomounknowne faça o parsing de forma defensiva. O envelope do evento (type,call_id,name,status) é estável.
Tipos de mensagem
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 é emitida duas vezes para a maioria das chamadas de ferramenta: primeiro com status: "running" e args preenchidos e, em seguida, ao concluir, com status: "completed" (ou "error") e result preenchido. truncated indica se o SDK truncou args ou result porque o payload era grande demais.
SDKStatusMessage abrange as transições do ciclo de vida na nuvem. CREATING abrange o provisionamento da VM e a clonagem do repositório; RUNNING indica que o agente está trabalhando; os demais são estados finais.
SDKUsageMessage é emitida uma vez ao final de cada turno que registrou uso de tokens, contendo o TokenUsage desse turno. O total acumulado entre os turnos permanece em run.usage e result.usage. Consulte Uso de tokens.
Atualizações de interação
InteractionUpdate é o tipo delta bruto passado ao callback onDelta em agent.send(). As atualizações são mais detalhadas que os eventos SDKMessage: o texto é transmitido token a token, as chamadas de ferramenta informam o estado parcial à medida que os args são acumulados, e o raciocínio é enviado conforme ocorre.
type InteractionUpdate = | TextDeltaUpdate | ThinkingDeltaUpdate | ThinkingCompletedUpdate | ToolCallStartedUpdate | ToolCallCompletedUpdate | ToolCallDeltaUpdate | PartialToolCallUpdate | TokenDeltaUpdate | StepStartedUpdate | StepCompletedUpdate | TurnEndedUpdate | UserMessageAppendedUpdate | SummaryUpdate | SummaryStartedUpdate | SummaryCompletedUpdate | ShellOutputDeltaUpdate;Tipos de atualização
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 contém um nível de atualizações de interação aninhadas de uma chamada de ferramenta de uma tarefa ou de um subagente. PartialToolCallUpdate é emitido à medida que o modelo transmite argumentos para uma chamada de ferramenta antes de confirmá-la. O mesmo aviso de estabilidade aplicável a SDKToolUseMessage.args também se aplica aqui.
Tipos de conversa
A visualização estruturada por turno de uma execução, retornada por run.conversation() e usada como argumento do 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 é uma união discriminada de todas as ferramentas integradas (shell, edit, read, write, glob, grep, ls, semSearch, mcp, task e outras). Sua estrutura é destinada ao uso interno; consulte a nota de estabilidade na seção Eventos de stream.
Retomar agentes
function Agent.resume(agentId: string, options?: Partial<AgentOptions>): Promise<SDKAgent>;Use Agent.resume() para se reconectar a um agente existente pelo ID. Fluxos comuns: reconectar-se a um agente em nuvem de longa duração iniciado anteriormente ou continuar uma conversa após o reinício do processo local. O tempo de execução é detectado automaticamente pelo prefixo do ID (bc- indica nuvem; qualquer outro indica 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 fica undefined ao retomar, a menos que você passe model novamente. mcpServers inline não são persistidos entre retomadas — geralmente contêm segredos e ficam apenas na memória. Passe-os novamente ao retomar ou use uma configuração de MCP baseada em arquivo (.cursor/mcp.json + local.settingSources) para servidores que precisam permanecer disponíveis.
Inspeção de agentes e execuções
Liste, consulte e recarregue agentes anteriores. Os endpoints de listagem retornam { items, nextCursor? } para paginação baseada em 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; // roteamento local apiKey?: string; // roteamento na nuvem store?: LocalAgentStore;}O tempo de execução é detectado automaticamente pelo prefixo do ID do agente (bc- → cloud; caso contrário, 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 };O getRun na Cloud requer o agentId do agente principal.
Agent.cancelRun()
function Agent.cancelRun(runId: string, options?: GetRunOptions): Promise<void>;Cancela uma execução quando você tem o ID dela, mas não tem um 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;}Retorna as mensagens do usuário e do assistente armazenadas para um agente local.
Agent.getUsage()
Consulte o uso faturado de tokens e o custo em dólares das execuções de um agente. Chame-o em um handle ou estaticamente por ID quando não tiver um. Agentes na nuvem retornam um detalhamento por execução; agentes locais retornam um detalhamento por turno. Passe runId para restringir o resultado a uma entrada: para agentes na nuvem, um ID de execução run-<uuid>; para agentes locais, um ID obtido em um 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 em todas as `runs` cost?: UsageCost; // total em todas as `runs` runs: RunUsage[];}interface RunUsage { runId: string; usage: TokenUsage; cost?: UsageCost;}interface UsageCost { rawCostCents: number; // custo dos tokens do modelo sem desconto; 0 para uso com preço por solicitação chargedCents: number; // valor cobrado, incluindo descontos e a Cherri Code Token Fee}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);}O custo inclui descontos e pode levar um momento para ser calculado após o término de uma execução; cost fica ausente até que isso ocorra. chargedCents é 0 para uso incluído no plano, BYOK e créditos concedidos.
Esta é uma visualização diferente de Uso de tokens: run.usage é a contagem de tokens em tempo real de uma execução, enquanto getUsage() é o registro faturado de todas as execuções do agente.
Ciclo de vida do agente na nuvem
Os agentes na nuvem permanecem no espaço de trabalho da sua equipe até serem arquivados ou excluídos. Agent.list({ runtime: "cloud" }) oculta agentes arquivados por padrão; passe includeArchived: true para vê-los. Filtre por prUrl para encontrar o agente que abriu uma solicitação de pull 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); // exclusão reversível; a transcrição permanece legívelawait Agent.unarchive(agentId); // restaura um agente arquivadoawait Agent.delete(agentId); // permanente; leituras subsequentes retornam 404SDKAgentInfo
A estrutura de metadados retornada por Agent.list() e 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>; });O namespace Cherri Code
Leituras no nível da conta, do catálogo e configuração do SDK para todo o processo. Os métodos de leitura aceitam { apiKey } como opção e, caso contrário, usam CURSOR_API_KEY e depois um login pelo navegador armazenado.
Cherri Code.auth
Login interativo para hosts sem uma chave de API provisionada previamente. Cherri Code.auth.login() abre a página de login do site do Cherri Code em um navegador, aguarda a conclusão e emite uma chave de API de usuário (válida por 90 dias por padrão), armazenada em ~/.cursor/sdk/auth.json. Após o login, Agent.create(), Cherri Code.me() e as demais operações de leitura funcionam sem apiKey nem 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();| Opção | Descrição |
|---|---|
backendUrl | URL base da API. O valor padrão é CURSOR_BACKEND_URL e, em seguida, produção. |
websiteUrl | URL base de login pelo navegador. O valor padrão é CURSOR_WEBSITE_URL e, em seguida, produção. |
openBrowser | true (valor padrão) abre o navegador do sistema quando isso tiver chance de funcionar; false nunca abre o navegador; uma função define um abridor personalizado. Ignorado em sessões SSH ou quando NO_OPEN_BROWSER está definido. |
onLoginUrl | Chamado com a URL de login antes de aguardar, para que o host possa exibi-la. Quando omitido e nenhum navegador é aberto, a URL é gravada em stderr. |
signal | AbortSignal que cancela a espera; nesse caso, login lança um AuthenticationError. |
store | Onde persistir as credenciais. O valor padrão é ~/.cursor/sdk/auth.json; passe null para receber apenas a chave no resultado. |
apiKeyName | Nome de exibição da chave emitida na lista de chaves de API do dashboard. |
apiKeyTtlMs | Validade da chave emitida em milissegundos. O valor padrão é 90 dias. |
Cherri Code.auth.login() retorna
{ apiKey, email?, apiKeyExpiresAtMs }. Use FileCredentialStore ou
InMemoryCredentialStore para fornecer um store personalizado a login(), status()
ou logout().
Ordem de resolução de credenciais em todo o SDK: apiKey explícita, depois CURSOR_API_KEY e, por fim, o login armazenado. O login armazenado não lê credenciais de uma instalação local do app Cursor; ele mantém apenas chaves 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; };}Defina valores padrão para agentes locais aplicáveis a chamadas Agent.* posteriores. Os campos de uma chamada individual substituem esses valores; passe null para limpar um valor padrão anterior.
| Opção | Descrição |
|---|---|
local.store | Store de agente local padrão quando uma chamada não informa local.store. O SDK usa SQLite em disco por meio de node:sqlite; quando esse módulo não está disponível, Agent.create() lança um ConfigurationError, a menos que você configure JsonlLocalAgentStore ou outro store aqui. |
local.useHttp1ForAgent | Força os streams do backend do agente local a usar HTTP/1.1 com SSE em vez de HTTP/2. Útil atrás de proxies ou em stacks de fetch que não oferecem suporte a HTTP/2. O Bun usa HTTP/1.1 por padrão devido a problemas de compatibilidade com HTTP/2 no upstream. |
local.workspaceScanCacheTtlMs | Por quanto tempo o SDK reutiliza uma varredura do espaço de trabalho (regras, skills, AGENTS.md, arquivos ignorados), em milissegundos. O valor padrão é 20 segundos. Aumente-o em um host de longa duração que atende um checkout que só muda durante o deploy; a contrapartida é a atualização dos dados, pois uma regra adicionada após o início do processo pode não ser detectada durante esse período. A variável de ambiente CURSOR_RIPWALK_CACHE_TTL_MS define o mesmo 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;}Use Cherri Code.models.list() para descobrir IDs model válidos e os params de cada modelo antes de chamar Agent.create() ou agent.send(). Os parâmetros são específicos de cada modelo. Exemplos comuns incluem o esforço de raciocínio e optimize_for do Cherri Code Router em auto-smart.
O catálogo é específico da conta e da equipe. O Cherri Code Router só aparece como auto-smart quando o Router está disponível para a equipe associada à chave de API. Consulte 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" },// ],// },// ]Passe os valores dos parâmetros selecionados em model.params. As variants predefinidas já contêm params válidos, então você pode copiá-las para uma seleção 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() },});Melhores práticas
-
Descubra, não fixe no código. Chame
Cherri Code.models.list()na inicialização (ou uma vez por processo) e armazene o resultado em cache. IDs de modelos e formatos de parâmetros podem mudar à medida que novos modelos são lançados. -
Passe os parâmetros explicitamente quando o modelo os exigir. Um modelo cuja matriz
parametersnão está vazia é um modelo parametrizado. Envie os parâmetros desejados; caso contrário, a execução usará o primeiro valor permitido de cada parâmetro, que pode não corresponder ao que você pretende. Para o Cherri Code Router, sempre passeoptimize_forexplicitamente. -
Resolva por capacidade, não por ID. Se quiser "o padrão atual no modo Fast" em vez de um modelo específico, procure-o:
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" }], };Prefira uma seleção explícita do Router (
auto-smart+optimize_for) quando o modelo de destino não estiver disponível. Use{ id: "auto" }como alternativa apenas quando quiser que o servidor selecione Auto sem escolher Custo, Balance ou Inteligência.
Cherri Code.repositories.list()
function Cherri Code.repositories.list(options?: CursorRequestOptions): Promise<SDKRepository[]>;interface SDKRepository { url: string;}Retorna os repositórios do GitHub conectados à equipe do usuário solicitante. Somente na Cloud.
Fontes de configuração em resumo
Servidores MCP, subagentes e hooks são resolvidos a partir de uma combinação de opções inline e configurações em disco. A precedência segue o mesmo padrão nos três casos: inline por envio > inline na criação > arquivos do projeto > arquivos do usuário > configuração da equipe / dashboard.
| Funcionalidade | Opção inline | Arquivo local (projeto) | Arquivo local (usuário) | Nuvem / dashboard | Precedência |
|---|---|---|---|---|---|
| Servidores MCP | mcpServers em Agent.create() e agent.send() | .cursor/mcp.json (condicionado à inclusão de "project" em local.settingSources) | ~/.cursor/mcp.json (condicionado a "user") | Servidores configurados em cursor.com/agents (somente na nuvem) | Enviar > criar > plugins > projeto > usuário (local); Enviar > criar > dashboard (nuvem) |
| Subagentes | agents em Agent.create() | .cursor/agents/*.md (frontmatter: name, description, model?) | n/d | A nuvem usa os mesmos arquivos do projeto quando o agente é executado no repositório clonado | Inline substitui a configuração baseada em arquivo com o mesmo nome |
| Hooks | Nenhuma — somente baseada em arquivo | .cursor/hooks.json (+ scripts) | ~/.cursor/hooks.json | Execuções na nuvem usam hooks do projeto. Nos planos Enterprise, também hooks da equipe e hooks gerenciados corporativamente. | Baseada em arquivo; projeto em camadas com usuário / equipe / corporativo conforme Hooks |
| Fontes de configuração | local.settingSources seleciona quais camadas em disco carregar | .cursor/ | ~/.cursor/ | n/d | A nuvem sempre carrega project / team / plugins e ignora local.settingSources. |
Valores inline são ideais para segredos que nunca devem ser gravados em disco (chaves de API por execução, tokens com escopo de locatário). A configuração baseada em arquivo é ideal para políticas: os hooks, especialmente, delimitam o projeto, não são um controle por execução.
Servidores MCP
Os agentes podem usar servidores MCP de várias fontes. Definições inline em Agent.create() ou agent.send() são a forma mais comum. Também há suporte a configurações baseadas em arquivos e gerenciadas pelo dashboard.
O que é carregado
Agentes locais carregam servidores de até cinco fontes, com precedência da primeira correspondência em caso de nomes conflitantes:
mcpServersemagent.send(). Substitui completamente os servidores definidos na criação para essa execução (não são mesclados).mcpServersemAgent.create(). Usado quando não há substituição por envio.- Servidores de plugins, se
local.settingSourcesincluir"plugins". - Servidores do projeto em
.cursor/mcp.json, selocal.settingSourcesincluir"project". - Servidores do usuário em
~/.cursor/mcp.json, selocal.settingSourcesincluir"user".
Sem local.settingSources, apenas servidores inline são carregados. Se um servidor MCP local exigir login OAuth, o SDK não poderá solicitar que você faça login. Isso só funciona se você já tiver feito login nesse servidor pelo app Cherri Code. Nesse caso, o SDK reutiliza o login salvo.
Agentes na nuvem carregam servidores destas fontes:
mcpServersemagent.send(). Substitui completamente os servidores definidos na criação para essa execução (não são mesclados).mcpServersemAgent.create(). Usado quando não há substituição por envio.- Seus servidores MCP de usuário e de equipe em cursor.com/agents.
Se um servidor inline não incluir auth ou headers e você já tiver autorizado o URL desse servidor em cursor.com/agents, as execuções autenticadas com um token de API pessoal reutilizarão automaticamente esses tokens OAuth. As chaves de API de contas de serviço não podem usar a autenticação do usuário como alternativa, pois não estão associadas a um usuário.
local.settingSources não se aplica a agentes na nuvem.
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(), }, },});Cloud
Agentes na nuvem também podem receber configurações MCP autenticadas inline. Use a autenticação HTTP quando o Cherri Code precisar atuar como proxy de um MCP remoto por meio do backend. Use env com stdio quando o servidor for executado na VM em nuvem e ler credenciais de variáveis de ambiente.
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!, }, }, },});Use headers para chaves de API estáticas ou tokens Bearer — o Cherri Code os encaminha em todas as solicitações. Use auth para servidores protegidos por OAuth. Na nuvem, o Cherri Code executa o fluxo OAuth uma vez no servidor e reutiliza o token entre execuções. Localmente, o SDK não consegue abrir um navegador para fazer login; ele apenas reutiliza tokens que você já obteve ao fazer login pelo app Cherri Code.
- Os
headerse aauthHTTP são tratados pelo backend do Cherri Code. Campos sensíveis são ocultados e não chegam à VM. - Os valores de
envdo Stdio são passados para a VM porque o servidor é executado nela. Trate-os como qualquer outro segredo de tempo de execução. - O OAuth para servidores MCP configurados em cursor.com/agents permanece por usuário, mesmo para servidores em nível de equipe.
Consulte MCP para conferir o formato completo da configuração e recursos do Cloud Agent para saber mais sobre o comportamento específico da nuvem.
Subagentes
Defina subagentes nomeados que o agente principal cria usando a ferramenta Agent. Passe-os diretamente:
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.", }, },});Subagentes commitados no repositório em .cursor/agents/*.md (com frontmatter name, description e model opcional) também são reconhecidos. Definições inline substituem definições baseadas em arquivos com o mesmo nome.
Subagentes aninhados
Os subagentes podem criar seus próprios subagentes, respeitando um limite de aninhamento. Quando um subagente usa a ferramenta Agent, o SDK fornece a ele o mesmo executor de subagentes do agente pai, permitindo que um agente pai delegue a um subagente que, por sua vez, delega a outros. Todos os níveis têm acesso ao mesmo conjunto de subagentes nomeados e ferramentas personalizadas. O agente de nível superior e seus subagentes diretos podem iniciar subagentes, mas um subagente iniciado por outro subagente não pode iniciar outros.
Subagentes em background
Quando o agente executa um subagente em background, o resultado do subagente retorna ao parent como um turno de acompanhamento na mesma execução, em vez de ser descartado quando o turno do parent termina. O run.stream() continua emitindo events ao longo desses turnos, e o run.wait() resolve depois deles, com o texto do último turno como result. Apenas para agentes local.
Restrição do conjunto de ferramentas
tools define a lista de permissão das ferramentas integradas oferecidas ao modelo; disallowedTools remove ferramentas e mantém as demais, incluindo ferramentas adicionadas à plataforma após o lançamento da sua versão do SDK. Por enquanto, ambos estão disponíveis apenas para agentes locais e não persistem no agente: passe-os novamente em Agent.resume() para manter a restrição em execuções de acompanhamento.
// Agente somente leitura: apenas estas ferramentas estão disponíveis.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() },});// Tudo, exceto o acesso ao 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(padrão) oferece o conjunto de ferramentas padrão do modelo selecionado;tools: []não oferece ferramentas integradas, portanto o modelo só pode responder com texto.- Ambos os campos aceitam a união
ToolName: nomes públicos ("read","edit","task","webSearch", ...), os grupos de recursos"shell"e"mcp"e nomes brutos de ferramentas proto. Nomes desconhecidos geram umConfigurationErroremAgent.create()/Agent.resume(). - A negação prevalece: uma ferramenta precisa estar em
tools(quando definido) e não estar emdisallowedToolspara ser disponibilizada. - Desabilitar
"mcp"também remove ferramentas personalizadas. Desabilitar"task"impede subagentes; caso contrário, os subagentes mantêm seus próprios conjuntos de ferramentas selecionados.
Ferramentas personalizadas
As ferramentas personalizadas permitem expor suas próprias funções ao agente sem precisar configurar um servidor MCP separado. Passe-as em local.customTools, e o SDK as registra como um servidor MCP chamado custom-user-tools. O agente as descobre e chama pelo mesmo caminho de MCP usado por qualquer outro servidor. As regras de negação e os limites de sandbox ainda se aplicam, mas as ferramentas personalizadas dispensam aprovação interativa. Portanto, execuções em sandbox e com auto-review as chamam sem solicitar confirmação. As ferramentas personalizadas também ficam disponíveis para subagentes (inclusive os aninhados).
As ferramentas personalizadas estão disponíveis apenas para agentes locais. Agentes na nuvem ignoram local.customTools na criação e geram um ConfigurationError quando você o passa em 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());Defina ferramentas personalizadas uma vez em Agent.create() para aplicá-las a todas as execuções ou passe local.customTools em uma chamada específica de agent.send() para substituí-las nessa execução.
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." }] }; }, }, }, },});Definição da ferramenta
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;}| Campo | Descrição |
|---|---|
description | Exibida ao modelo para que ele saiba quando chamar a ferramenta. O valor padrão é uma string vazia. |
inputSchema | JSON Schema dos argumentos. O valor padrão é um objeto aberto que aceita quaisquer propriedades. |
outputSchema | JSON Schema do resultado estruturado da ferramenta, anunciado ao modelo como o Tool.outputSchema do MCP. Os resultados não são validados contra ele. |
annotations | Anotações de MCP tool (title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint) repassadas ao modelo. São apenas dicas descritivas; nada no SDK as impõe. |
execute | Seu callback. Recebe os args analisados e um context com o toolCallId. É executado no seu processo e pode acessar tudo o que seu código acessa. |
Resultados da ferramenta
execute pode retornar uma string simples, qualquer valor JSON ou um envelope estruturado. A chave do map é o nome da ferramenta chamada pelo 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 };- Retorne uma string para saída de texto simples.
- Retorne qualquer valor JSON para enviá-lo como texto; objetos também preenchem
structuredContent. - Retorne o envelope para ter controle total: combine texto e
contentde imagem em base64, definaisError: truepara relatar uma falha ou anexestructuredContentpara o modelo analisar. Exceções lançadas porexecutetambém são relatadas ao agente como erros de ferramenta.
Hooks
Os hooks são definidos exclusivamente por arquivos. Não há callback de hook programático. Os hooks definem uma política de projeto, não uma configuração por execução.
- Local: Adicione
.cursor/hooks.jsonao repositório passado comolocal.cwdou adicione~/.cursor/hooks.jsonpara hooks de nível de usuário. - Nuvem: Faça commit de
.cursor/hooks.jsone dos respectivos scripts no repositório passado emcloud.repos. Agentes em nuvem criados pelo SDK carregam automaticamente os hooks do projeto. Em planos Enterprise, eles também executam hooks da equipe e hooks gerenciados pela empresa.
Consulte Hooks para ver o formato de configuração e suporte a hooks do Cloud Agents para entender o comportamento na nuvem.
Opções de sandbox
Por padrão, os agentes locais são executados com local.sandboxOptions.enabled: false. O agente pode ler e gravar no diretório de trabalho, executar comandos de shell e acessar a rede sem restrições. Não há um fluxo de aprovação com intervenção humana em execuções sem interface gráfica do SDK, portanto, um sandbox habilitado por padrão bloquearia silenciosamente chamadas de ferramenta legítimas ou exigiria um callback incompatível com scripts.
Quando você habilita o sandbox, o SDK restringe todas as chamadas de ferramenta de shell e os processos iniciados pelo shell:
- Sistema de arquivos. As gravações são limitadas ao diretório de trabalho (
local.cwd), a diretórios temporários e aos caminhos que você permitir emsandbox.json. As leituras não são restritas ao espaço de trabalho. - Shell. Os comandos são executados em um sandbox da plataforma (
bubblewrapno Linux,seatbeltno macOS e o auxiliar incluído@cursor/sdk-<os>-<arch>). Operações privilegiadas são negadas. - Rede. O tráfego de rede de saída é bloqueado por padrão. Para permitir hosts específicos, adicione um
.cursor/sandbox.jsonao espaço de trabalho com a lista de hosts permitidos. Se existir, o SDK também lê a mesma política por usuário em~/.cursor/sandbox.json.
const agent = await Agent.create({ apiKey: process.env.CURSOR_API_KEY!, model: { id: "composer-2.5" }, local: { cwd: process.cwd(), sandboxOptions: { enabled: true }, },});Se o sandboxing não for compatível com o host (Linux antigo sem bubblewrap ou binário auxiliar ausente), o SDK gera um ConfigurationError com uma mensagem que informa qual dependência está ausente. Desabilite sandboxOptions.enabled ou execute no modo em nuvem para contornar o problema.
As execuções em nuvem sempre ocorrem em uma VM isolada, portanto sandboxOptions não se aplica.
Auto-review
Por padrão, um agente local executa todas as chamadas de ferramenta sem restrições, pois execuções sem interface gráfica não têm uma pessoa para aprová-las. Defina local.autoReview: true para encaminhar as chamadas de ferramenta locais pelo Auto-review, que usa o mesmo classificador do IDE para permitir ou bloquear chamadas de Shell, MCP e Fetch com base na segurança e no grau de correspondência de cada chamada com a intenção da execução.
const agent = await Agent.create({ apiKey: process.env.CURSOR_API_KEY!, model: { id: "composer-2.5" }, local: { cwd: process.cwd(), autoReview: true, },});O Auto-review exige que o classificador esteja habilitado no backend conectado. Quando ele não está disponível, as execuções voltam ao comportamento padrão. Como não há aprovação interativa em uma execução headless, uma chamada bloqueada pelo classificador é negada em vez de escalonada, e o agente recebe o motivo do bloqueio e pode tentar outra abordagem. Oriente o classificador com um bloco autoRun em permissions.json no espaço de trabalho, da mesma forma que no IDE. Consulte permissions.json para ver o formato.
O Auto-review é exclusivo para agentes locais. As execuções em nuvem já são executadas em uma VM isolada. O classificador é uma conveniência de melhor esforço, não um limite de segurança; combine-o com sandboxOptions ou uma lista de permissão para um controle rigoroso.
Artefatos
Liste e baixe arquivos do espaço de trabalho do 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);O suporte a artefatos depende do ambiente de execução. Atualmente, os agentes SDK locais não retornam artefatos e geram um erro ao chamar downloadArtifact.
Gerenciamento de recursos
Sempre libere os agentes ao terminar de usá-los. O padrão mais adequado é await using:
await using agent = await Agent.create({ /* ... */ });// liberado automaticamente ao sair do blocoPara liberar explicitamente:
await agent[Symbol.asyncDispose]();agent.close() é a forma documentada de iniciar a liberação de recursos sem aguardar. Symbol.asyncDispose funciona (await using é baseado nele), mas close() é o método indicado em código que não usa a sintaxe await using. agent.reload() recarrega alterações na configuração do sistema de arquivos (hooks, MCP do projeto, subagentes) sem liberar os recursos.
Ciclo de vida do agente
Pré-aqueça um espaço de trabalho local
Resolver um espaço de trabalho local (regras, habilidades, servidores MCP, arquivos ignorados) é a parte mais lenta do primeiro turno de um agente local e, em um repositório grande, pode ser responsável pela maior parte do tempo. Por padrão, esse custo ocorre na primeira chamada a send(). Um host que sabe onde seus agentes serão executados pode antecipá-lo com 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"] },});// O primeiro send() neste espaço de trabalho é iniciado imediatamente.await release(); // ao encerrarPasse as mesmas AgentOptions que seus agentes usarão; o pré-aquecimento só ajuda envios cujas opções de espaço de trabalho sejam compatíveis. Chame a função de liberação retornada quando o host for encerrado.
Reconecte-se a um agente existente
Agent.resume(agentId) retorna um novo handle para um agente já existente. O tempo de execução é detectado automaticamente pelo prefixo do ID (bc- é cloud; qualquer outro é local), e o estado da conversa é carregado da cloud (cloud) ou do store local de checkpoints (local). Assim, você pode continuar o trabalho após um reinício do processo ou permitir que outro worker retome um agente iniciado por outro processo.
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();Se a execução já estava em andamento quando você se reconectou, Agent.getRun(runId, { runtime: "cloud", agentId }) (ou o equivalente local) retorna um Run em que você pode usar stream(), wait() ou cancel().
Contexto da conversa
Agentes locais persistem o estado da conversa em um store de checkpoints. Por padrão, ele usa SQLite em disco no seu diretório home; substitua-o por JSONL ou um backend personalizado com local.store. Cada chamada de agent.send() carrega o checkpoint mais recente desse agente e o envia ao modelo, para que as mensagens de acompanhamento tenham acesso ao mesmo contexto com que a execução anterior terminou. O store sobrevive a reinícios do processo, o que significa que Agent.resume(agentId) em um processo totalmente novo retoma de onde o anterior parou.
Agentes na nuvem persistem o estado no servidor. Reconectar-se de qualquer lugar retorna a mesma conversa.
Algumas situações que parecem perda de contexto, mas não são:
- Um novo
Agent.create()sempre inicia um agente do zero com um novoagentId. Para continuar uma conversa existente, captureagent.agentIdna primeira chamada e useAgent.resume(agentId)depois. Agent.prompt()cria, executa e libera recursos de uma só vez. Não há um segundo turno; esse é o contrato.mcpServersinline não são persistidos entre chamadas deAgent.resume()porque geralmente contêm segredos. Passe-os novamente ao retomar ou use uma configuração MCP baseada em arquivo.
Padrão dispatcher
Um dispatcher gerencia um pool de agentes e atribui trabalho a eles conforme ele chega. A estrutura é simples: mantenha um mapa de agentId para SDKAgent de longa duração, encaminhe os prompts recebidos com base em uma chave (usuário, repositório, ticket) e chame Agent.resume() a partir do disco se um reinício do processo tiver apagado o mapa em memória.
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();}Os streams SSE na Cloud mantêm os eventos anteriores por um período após o início da execução. Assim, um dispatcher que transmite para muitos assinantes pode chamar run.stream() para cada assinante sem perder eventos anteriores. Para execuções na Cloud muito longas, os dispatchers geralmente distribuem chamadas a run.wait() e permitem que os assinantes façam polling de run.conversation() caso precisem da transcrição estruturada.
Stores de agentes locais
Os agentes locais persistem metadados do agente, checkpoints de conversa, execuções e eventos de execução em disco para que as mensagens de acompanhamento e Agent.resume() sobrevivam a reinícios do processo. Por padrão, o SDK usa SQLite em disco por meio de node:sqlite. Quando esse módulo não está disponível, Agent.create() lança um ConfigurationError em vez de recorrer a uma alternativa. Você pode substituir o backend usando local.store.
O SDK oferece dois backends e permite que você use o seu próprio:
| Store | Importação | Quando usar |
|---|---|---|
SqliteLocalAgentStore | @cursor/sdk/sqlite | SQLite em disco na raiz de estado do espaço de trabalho. |
JsonlLocalAgentStore | @cursor/sdk | Arquivos JSON portáteis delimitados por nova linha (NDJSON) em um diretório à sua escolha. Fáceis de inspecionar, copiar e comparar. |
LocalAgentStore personalizado | Seu código | Persista em qualquer lugar: em memória, Redis, Postgres ou um banco de dados hospedado. Implemente a interface ou componha substores. |
Os agentes na nuvem persistem no servidor, portanto local.store se aplica apenas a agentes locais.
Store JSONL
JsonlLocalAgentStore grava quatro arquivos NDJSON (agents.ndjson, runs.ndjson, run_events.ndjson, checkpoints.ndjson) no diretório especificado. Crie uma instância e atribua-a 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 },});Passe a mesma instância de store para Agent.resume() e as APIs locais de listagem e obtenção (Agent.list, Agent.get, Agent.listRuns, Agent.getRun) para que leiam os mesmos dados.
Defina um padrão para todo o processo
Para não precisar passar um store em cada chamada, defina um padrão uma única vez com Cherri Code.configure(). O local.store informado em cada chamada ainda tem prioridade.
import { Cherri Code, JsonlLocalAgentStore } from "@cursor/sdk";Cherri Code.configure({ local: { store: new JsonlLocalAgentStore("/var/lib/cursor-agents") } });// As chamadas subsequentes usam o store configurado, a menos que forneçam um próprio.const agent = await Agent.create({ apiKey: process.env.CURSOR_API_KEY!, model: { id: "composer-2.5" }, local: { cwd: process.cwd() },});Passe store: null para Cherri Code.configure({ local: { store: null } }) para remover uma configuração padrão anterior e voltar à seleção padrão de store local do SDK.
Stores personalizados
Para persistir dados em outro lugar (em um Postgres compartilhado, Redis ou um mapa em memória para testes), implemente LocalAgentStore. Ele é composto por quatro substores, cada um com uma pequena interface CRUD chamada pelo SDK:
interface LocalAgentStore { readonly agents: LocalAgentStoreAgents; // linhas de metadados de agentes readonly checkpoints: LocalAgentStoreCheckpoints; // blobs de conversa endereçados por conteúdo readonly runs: LocalAgentStoreRuns; // linhas de execuções readonly runEvents: LocalAgentStoreRunEvents; // log somente de acréscimo de eventos de execução}Implemente a interface diretamente ou crie cada substore separadamente e combine-os com composeLocalAgentStore:
import { composeLocalAgentStore } from "@cursor/sdk";const store = composeLocalAgentStore({ agents: myAgentsTable, checkpoints: myCheckpointBlobs, runs: myRunsTable, runEvents: myRunEventLog,});Os substores espelham as tabelas SQLite padrão: agents armazena uma linha por agente (com um ponteiro compacto latestCheckpoint.rootBlobId), checkpoints armazena os blobs de conversa endereçados por conteúdo referenciados por esses ponteiros, runs armazena uma linha por execução e runEvents é o log de stream somente de acréscimo. Os substores de catálogo são paginados com um cursor / nextCursor opaco; o log de eventos de execução é retomado com um afterOffset / nextOffset exclusivo. Consulte os tipos exportados LocalAgentStore, LocalAgentDocument, LocalAgentRunDocument e relacionados para conhecer as estruturas exatas.
Referência de configuração
AgentOptions
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
model | ModelSelection | Obrigatório antes do primeiro send() local; em nuvem, usa o padrão resolvido pelo servidor | Modelo a usar. Consulte ModelSelection. |
apiKey | string | variável de ambiente CURSOR_API_KEY | Chave de API do usuário ou chave da conta de serviço. Chaves de Admin da equipe ainda não são compatíveis. |
name | string | Gerado automaticamente | Nome legível do agente, retornado como name em Agent.list() / Agent.get(). |
local | LocalAgentOptions | Configuração do agente local. Consulte LocalAgentOptions. | |
cloud | CloudAgentOptions | Configuração do agente em nuvem. | |
mcpServers | Record<string, McpServerConfig> | Definições inline de servidores MCP. | |
agents | Record<string, AgentDefinition> | Definições de subagentes. | |
tools | ToolName[] | Conjunto de ferramentas padrão | Restrinja o conjunto de ferramentas: apenas as ferramentas integradas listadas são disponibilizadas ao modelo. [] significa que não há ferramentas integradas. Apenas para agentes locais. |
disallowedTools | ToolName[] | Remova ferramentas do conjunto de ferramentas; todas as outras permanecem disponíveis. Deny prevalece quando combinado com tools. Apenas para agentes locais. | |
systemPrompt | string | Prompt integrado do Cherri Code | Substitua o prompt de sistema do loop do agente principal. Não pode ser vazio. Apenas para agentes locais e não é persistido entre chamadas de Agent.resume(). |
agentId | string | Gerado automaticamente | ID persistente do agente. Informe-o para manter um ID estável entre invocações. |
idempotencyKey | string | Gerado automaticamente em nuvem | Chave de idempotência opcional gerada pelo cliente. |
mode | "agent" | "plan" | "agent" | Modo de conversa inicial da primeira execução do agente. Consulte Modo de conversa. |
LocalAgentOptions
Configuração para agentes locais, passada como local em Agent.create(). Também exportada como um tipo independente para Partial<LocalAgentOptions>.
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
cwd | string | Diretório de trabalho principal para o shell padrão e o escopo do store de agentes. | |
dirs | string[] | Pastas adicionais do espaço de trabalho para configurações com múltiplas raízes. Mescladas com cwd (sem duplicatas) para que regras, skills e o contexto do espaço de trabalho sejam carregados de cada caminho. | |
settingSources | SettingSource[] | Camadas de configuração a serem carregadas: "project", "user", "team", "mdm", "plugins" ou "all". | |
sandboxOptions | { enabled: boolean } | { enabled: false } | Configuração do Sandbox. |
autoReview | boolean | false | Encaminha chamadas de ferramenta locais pelo Auto-review. |
customTools | Record<string, SDKCustomTool> | Ferramentas personalizadas expostas como o servidor MCP custom-user-tools. | |
store | LocalAgentStore | store padrão do SDK | Persistência de suporte do store de agentes locais. |
enableAgentRetries | boolean | true | Habilita novas tentativas automáticas em caso de falhas de transporte ou interrupções durante execuções de agentes locais. Defina como false para exibir erros de transporte na primeira falha. |
CloudAgentOptions
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
env | { type: "cloud"; name?: string } | { type: "pool"; name?: string } | { type: "machine"; name?: string } | { type: "cloud" } | Destino do ambiente de execução. cloud usa VMs hospedadas pelo Cursor; defina name para usar um ambiente hospedado pelo Cherri Code salvo. pool e machine direcionam para workers auto-hospedados que você executa. Omita repos e mantenha o valor padrão de env para um agente sem repositório e com espaço de trabalho vazio. Ambientes hospedados pelo Cherri Code com nome e repos explícitos são mutuamente exclusivos. |
repos | Array<{ url: string; startingRef?: string; prUrl?: string }> | Repositórios a serem clonados na VM. Passe uma entrada para um agente de repositório único ou até 20 para um agente de vários repositórios. Omita ou passe [] para um agente sem repositório. Mutuamente exclusivo com um env.name para um ambiente hospedado pelo Cherri Code com nome. Passe prUrl para associar o agente a uma PR existente. | |
workOnCurrentBranch | boolean | false | Faça push dos commits para a branch existente em vez de criar uma nova. |
autoCreatePR | boolean | false | Abra uma PR quando a execução terminar. |
openAsCursorGithubApp | boolean | true para chaves de conta de serviço, false para chaves de usuário | Abra PRs como o Cherri Code GitHub App em vez de usar o proprietário da chave de API. O valor resolvido é retornado ao criar, obter e listar. |
skipReviewerRequest | boolean | false | Não solicite que o usuário que fez a chamada seja revisor da PR. |
envVars | Record<string, string> | Variáveis de ambiente com escopo de sessão para agentes em nuvem. | |
metadata | Record<string, string> | Tags de string definidas pelo chamador e persistidas no agente em nuvem. Consulte Metadados do agente. |
AgentDefinition
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
description | string | obrigatório | Quando usar este subagente. É exibida ao agente pai para que ele saiba quando criá-lo. |
prompt | string | obrigatório | Prompt de sistema do subagente. |
model | ModelSelection | "inherit" | "inherit" | Substituição de modelo. Passe "inherit" para usar a seleção do pai. |
mcpServers | Array<string | Record<string, McpServerConfig>> | Aceito por compatibilidade futura. Referências em string são ignoradas e configs inline lançam um ConfigurationError; os subagentes herdam os servidores MCP do pai. |
ModelSelection
interface ModelSelection { id: string; params?: ModelParameterValue[];}interface ModelParameterValue { id: string; value: string;}id é o identificador do modelo (por exemplo, "composer-2.5" ou "auto-smart"). params contém parâmetros específicos de cada modelo, como o esforço de raciocínio ou optimize_for do Router. Use Cherri Code.models.list() para encontrar IDs válidos, definições de parâmetros e variantes predefinidas para sua conta. Consulte Cherri Code Router para ver o contrato de seleção do Router.
McpServerConfig
type McpServerConfig = // stdio | { type?: "stdio"; command: string; args?: string[]; env?: Record<string, string>; cwd?: string; // somente no modo Local; a Cloud rejeita este campo } // HTTP / SSE | { type?: "http" | "sse"; url: string; headers?: Record<string, string>; // transmitidos sem alterações; Authorization funciona aqui auth?: { CLIENT_ID: string; CLIENT_SECRET?: string; scopes?: string[]; }; };Para servidores HTTP executados na nuvem, os headers e a auth são tratados pelo backend do Cherri Code. Os campos confidenciais são ocultados antes de chegarem à VM. Para servidores stdio na nuvem, os valores de env são passados para a VM (trate-os como qualquer outro segredo de tempo de execução).
SDKUserMessage
interface SDKUserMessage { text: string; images?: SDKImage[];}A forma estruturada do argumento message de agent.send(). Use-a para enviar imagens junto com texto.
SDKImage
type SDKImage = | { url: string; dimension?: SDKImageDimension } | { data: string; mimeType: string; dimension?: SDKImageDimension };interface SDKImageDimension { width: number; height: number;}Forneça uma url remota ou data em base64 com um mimeType.
SettingSource
type SettingSource = | "project" | "user" | "team" | "mdm" | "plugins" | "all";Controla quais camadas de configuração armazenadas em disco um agente local carrega. Agentes na nuvem sempre carregam project / team / plugins e ignoram este campo.
| Valor | Origem |
|---|---|
"project" | .cursor/ no espaço de trabalho |
"user" | ~/.cursor/ |
"team" | Configurações da equipe sincronizadas do dashboard |
"mdm" | Configurações corporativas gerenciadas por MDM |
"plugins" | Configurações fornecidas por plugins |
"all" | Abreviação para todas as opções acima |
ListResult
interface ListResult<T> { items: T[]; nextCursor?: string;}Retornado por Agent.list() e Agent.listRuns(). nextCursor não é retornado quando não há mais páginas.
Erros
Todos os erros do SDK herdam de CursorSdkError (reexportado como CursorAgentError para manter a compatibilidade retroativa). Use isRetryable para orientar a lógica de novas tentativas e code / status / requestId para diagnóstico.
class CursorSdkError extends Error { readonly isRetryable: boolean; readonly code?: string; // código estável do SDK/backend readonly status?: number; // status HTTP, se disponível readonly cause?: unknown; // erro subjacente encapsulado readonly endpoint?: string; readonly requestId?: string; readonly operation?: string; // operação do SDK que gerou o erro}| Classe de erro | Mensagem típica | Causa provável | Correção recomendada |
|---|---|---|---|
AuthenticationError | "Chave de API inválida" | CURSOR_API_KEY ausente ou incorreta, token expirado ou chave desativada por um admin. | Gere uma nova chave em API Keys (usuário) ou Configurações da equipe (conta de serviço). Confirme que a chave tem permissão para a operação. |
RateLimitError | "Limite de taxa excedido" ou "Limite de uso excedido" | Limite de pico ou teto de uso mensal. | Tente novamente com espera exponencial (o SDK informa isRetryable: true para casos transitórios). Para o teto mensal, aumente o limite de uso do plano. |
ConfigurationError | "Nome de modelo inválido", "Chave de API não compatível", "Arquivo não compatível" | model.id inválido, ausência de params obrigatórios, arquivo não compatível em uma chamada de ferramenta ou uma política de admin bloqueando a solicitação. | Chame Cherri Code.models.list() para confirmar o ID e os params. Verifique se os caminhos do repositório / arquivo existem. |
AgentBusyError | "O agente está ocupado" | Envio de uma mensagem de acompanhamento enquanto o mesmo agente em nuvem já tem uma execução nos estados CREATING ou RUNNING. | Aguarde a execução ativa terminar, cancele-a ou consulte Agent.listRuns() antes de enviar novamente. |
IntegrationNotConnectedError | "A integração do [provedor] não está conectada" | Criação de um agente em nuvem para um repositório cujo provedor de SCM não está conectado à sua equipe do Cherri Code. | Abra error.helpUrl para reconectar o provedor e tente novamente. |
NetworkError | "Serviço indisponível", "Tempo esgotado" | Problema transitório no backend, partição de rede ou prazo excedido. | Tente novamente com espera progressiva. Inspecione error.requestId se precisar abrir um ticket de suporte. |
UnsupportedRunOperationError | "A operação "stream" não é compatível com este tempo de execução" | Chamada de um método Run que o tempo de execução atual não consegue atender (por exemplo, streaming em uma execução local recuperada novamente que já terminou). | Verifique primeiro com run.supports(operation) / run.unsupportedReason(operation). |
AgentNotFoundError | "Agente não encontrado" | O agente solicitado não existe ou não está visível no espaço de trabalho local resolvido. | Verifique o ID do agente, cwd e local.store. |
UnknownAgentError | Mensagem definida pelo servidor | Erro não classificado de backend ou tempo de execução. | Inspecione error.code e error.cause para obter detalhes sobre a causa. |
Alguns erros incluem um link de resolução em um clique. O mais comum é
IntegrationNotConnectedError, mas outros tipos de erro podem adicionar helpUrl ao
longo do tempo. Ao capturar um erro, registre error.helpUrl, se presente, e exiba-o
ao usuário.
IntegrationNotConnectedError
class IntegrationNotConnectedError extends ConfigurationError { readonly provider: string; // por exemplo, "github", "gitlab", "azure-devops" readonly helpUrl: string; // link do dashboard para reconectar}A mensagem de erro padrão não inclui helpUrl, portanto, registre-a explicitamente:
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 é false para agent_busy. Tentar novamente imediatamente continuará falhando até que a execução ativa atinja um status final ou seja cancelada. Outras respostas 409, como agent_archived, geram ConfigurationError.
Aguarde a execução ativa terminar, cancele-a com run.cancel() ou consulte Agent.listRuns() antes de enviar novamente:
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;}Agentes locais não retornam agent_busy. Use send({ local: { force: true } }) para encerrar uma execução local travada antes de iniciar outra.
UnsupportedRunOperationError
class UnsupportedRunOperationError extends ConfigurationError { readonly operation: RunOperation;}Lançado quando uma operação Run não está disponível no tempo de execução atual. Use run.supports(operation) e run.unsupportedReason(operation) para verificar antes de fazer a chamada.
Limitações conhecidas
mcpServersinline não são persistidos entre chamadas aAgent.resume(). Informe-os novamente ao retomar, se necessário.- Ferramentas personalizadas (
local.customTools), Auto-review (local.autoReview), stores personalizados (local.store), restrições de conjunto de ferramentas (tools,disallowedTools) esystemPromptestão disponíveis apenas para agentes locais. Agentes na nuvem rejeitamlocal.customToolsemsend()e persistem no servidor. tools,disallowedToolsesystemPromptnão são persistidos no agente. Informe-os novamente emAgent.resume()para mantê-los.- O download de artefatos não está implementado para agentes locais (
agent.listArtifacts()retorna uma lista vazia eagent.downloadArtifact()gera uma exceção). local.settingSources(e os caminhos de MCP / subagente baseados em arquivo que ele controla) não se aplica a agentes na nuvem. A nuvem sempre carregaproject/team/plugins.- Hooks estão disponíveis apenas por arquivo (
.cursor/hooks.json). Não há callbacks programáticos. - O SDK não detecta automaticamente credenciais de uma instalação local do app Cherri Code. Defina
CURSOR_API_KEY(ou informeapiKey) explicitamente ou gere uma chave comCherri Code.auth.login(). - O modo local exige Node.js 22.13 ou posterior e suporte da plataforma ao sandbox-helper. O store padrão precisa de
node:sqlite; quando ele não está disponível,Agent.create()gera umConfigurationErroraté que você configureJsonlLocalAgentStoreou outro store.