Skip to main content

Command Palette

Search for a command to run...

SDK

Cursor TypeScript SDK

Пакет @cursor/sdk позволяет вызывать Agent Cursor из собственного кода. Того же Agent, который работает в Cursor IDE, CLI и веб-приложении, теперь можно запускать из скриптов TypeScript. Для начала запустите навык /sdk в Cursor.

Обзор

SDK предоставляет единый интерфейс для локальных и облачных сред выполнения. Вы пишете один и тот же код независимо от того, где работает агент.

Среда выполненияЧто делаетКогда использовать
ЛокальнаяЗапускает цикл агента непосредственно в вашем процессе Node. Файлы берутся с диска.Скрипты для разработки и проверки CI в рабочем дереве.
Облачная (размещаемая Cursor)Запускает агента в изолированной ВМ с клонированным репозиторием. ВМ управляются Cursor.Когда у вызывающей стороны нет репозитория, нужно запустить много агентов параллельно или выполнение должно продолжаться после отключения вызывающей стороны.

Среда выполнения определяется ключом, который вы передаёте в Agent.create() (local или cloud). В обоих случаях используйте один и тот же CURSOR_API_KEY.

Документацию по REST API см. в разделе API облачных агентов. Для других языков см. SDK Bridge.

Аутентификация

Перед созданием агента задайте CURSOR_API_KEY (или передайте apiKey). Для интерактивных хостов без заранее предоставленного ключа Cursor.auth.login() выпускает и сохраняет его через вход в браузере.

SDK поддерживает пользовательские API-ключи и API-ключи сервисных аккаунтов как для локальных, так и для облачных запусков. API-ключи администратора команды пока не поддерживаются.

export CURSOR_API_KEY="your-key"

Использование и оплата

К запускам SDK применяются те же правила ценообразования, пулов запросов и режима конфиденциальности, что и к запускам из IDE и Cloud Agents. Расходы отображаются на дашборде использования команды с тегом SDK.

Расходы по API-ключам сервисных аккаунтов относятся на команду, которой принадлежит сервисный аккаунт. Расходы по пользовательским API-ключам относятся на тариф соответствующего пользователя.

Чтобы получить количество токенов для отдельного запуска в коде, см. Использование токенов. Чтобы получить данные об оплачиваемом использовании и стоимости в долларах для запусков агента, см. Agent.getUsage().

Основные понятия

ПонятиеОписание
AgentПостоянный контейнер, содержащий состояние диалога, конфигурацию рабочего пространства и настройки. Сохраняется между несколькими промптами.
RunОдин отправленный промпт. Имеет собственные поток, статус, результат и возможность отмены.
SDKMessageНормализованные события потока, генерируемые во время запуска. Единая структура во всех средах выполнения.

Установка

npm install @cursor/sdk

Имя пакета начинается с @. Пакета cursor/sdk без пространства имён в npm не существует.

Поддерживаемые среды выполнения

Для SDK требуется Node.js 22.13 или новее. В пакет включены отдельные для каждой платформы бинарные файлы @cursor/sdk-<os>-<arch> для песочницы и ripgrep, поэтому он в первую очередь предназначен для Node.

При импорте @cursor/sdk стек локального агента не загружается сразу. Локальный executor загружается при первом локальном acquire, поэтому при работе только в облачном режиме или с типами не возникает затрат на локальный импорт. Первый локальный агент в процессе выполняет однократный импорт, после чего модуль остаётся в кэше.

@cursor/sdk публикует самодостаточные файлы .d.ts, поэтому типы разрешаются без подключения неопубликованных пакетов workspace. После обновления повторно запустите проверку типов. Типы потока, такие как TurnEndedUpdate, разрешаются в конкретные типы вместо any.

Однофайловые бандлы и скомпилированные исполняемые файлы

Стандартная сборка загружает некоторые части лениво во время выполнения. Однофайловые бандлеры не могут обработать такие загрузки, поэтому скомпилированное приложение завершается ошибкой при первом вызове Agent.create() с сообщением вроде Cannot find module './986.js'. SDK также поставляется в виде плоской однофайловой сборки с тем же публичным API. Она помещает всё в один файл, поэтому бандлер сразу встраивает весь SDK.

В Bun @cursor/sdk разрешается в плоскую сборку автоматически. Импортируйте его как обычно и компилируйте:

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

Для других однофайловых бандлеров, таких как esbuild, или чтобы явно зафиксировать плоскую сборку, импортируйте бандл-точки входа:

Точка входаСодержимое
@cursor/sdk/bundledВсё, что экспортирует @cursor/sdk.
@cursor/sdk/bundled/sqliteSqliteLocalAgentStore, как в @cursor/sdk/sqlite.

Несколько важных моментов:

  • Плоская сборка работает в Bun, включая исполняемые файлы, созданные с помощью bun build --compile. Она также загружается в Node, но хранилище SQLite там недоступно, поэтому настройте JsonlLocalAgentStore через local.store, иначе Agent.create() вызовет ConfigurationError. Продолжайте импортировать @cursor/sdk в Node-приложениях, которые не поставляются одним файлом.
  • zod, @bufbuild/protobuf и пакеты @connectrpc/* разрешаются из вашей установки. Они поставляются вместе с @cursor/sdk, а бандлер встраивает одну общую копию, поэтому схемы Zod, которые вы передаёте в пользовательские инструменты, продолжают работать.
  • Нативные исполняемые файлы не могут находиться внутри JavaScript-бандла. Песочница и встроенный ripgrep поставляются в пакетах @cursor/sdk-<os>-<arch> для соответствующих платформ. Разместите node_modules/@cursor/sdk-<os>-<arch>/ рядом со скомпилированным исполняемым файлом, и SDK найдёт его там. Без него поиск переключается на rg из PATH, а включение sandboxOptions вызывает ConfigurationError.

Типы для бандл-точек входа разрешаются так же, как для @cursor/sdk. Изменения конфигурации TypeScript не требуются.

Быстрый старт

Самый быстрый способ начать: запустить локальный агент в текущем рабочем дереве и получать события по мере их поступления. Настройка Cloud описана ниже, в разделе Создание Agent'ов.

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);}

Каждое событие представляет собой дискриминированный SDKMessage. В разделе «Стриминг» показано, как извлекать текст ассистента, обрабатывать вызовы инструментов и освобождать ресурсы с помощью await using. Для одноразового промпта (создать, запустить, освободить ресурсы) см. Agent.prompt().

Создание Agent'ов

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

Agent.create() проверяет параметры и сразу возвращает дескриптор. Передайте local или cloud, чтобы выбрать среду выполнения.

// Локальный агентconst agent = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  local: { cwd: "/path/to/repo" },});// Облачный агент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" }],    autoCreatePR: true,  },});

agent.agentId присваивается сразу. Локальные агенты получают идентификатор agent-<uuid>, а облачные — bc-<uuid>.

Облачные агенты без репозитория

Облачные агенты могут работать на пустой ВМ без репозитория. Передайте cloud с пустым списком repos или не указывайте repos вовсе. Если не указать cloud, будет выбрана локальная среда выполнения.

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);

Для вашего аккаунта или команды должны быть включены агенты без репозитория. API-ключи с областью действия в пределах репозитория не позволяют их создавать; используйте неограниченный ключ сервисного аккаунта или пользовательский API-ключ.

Переменные среды сессии

Для облачных агентов передавайте cloud.envVars, если при запуске нужны краткосрочные учетные данные или другие значения, доступные только этому агенту.

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!,    },  },});

Эти значения шифруются при хранении, передаются в оболочку облачного агента и удаляются вместе с ним. envVars нельзя использовать с agentId, заданным вызывающей стороной; не указывайте agentId и получайте сгенерированный сервером идентификатор из agent.agentId после первого вызова send(). Имена переменных не могут начинаться с CURSOR_.

Для значений, которые должны существовать только в рамках одного запуска, передавайте их в agent.send(). См. Переменные среды для одного запуска.

Метаданные агента

Добавляйте собственные строковые теги облачному агенту с помощью cloud.metadata. Теги сохраняются вместе с агентом и возвращаются в SDKAgentInfo.metadata методами Agent.get() и Agent.list(). Эти теги не относятся к API метаданных агента в VM, которое предоставляет изнутри 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",    },  },});

Параметры модели

Используйте model.params, чтобы передавать параметры конкретной модели, например уровень усилий рассуждения. Идентификаторы и значения параметров различаются в зависимости от модели. Используйте Cursor.models.list(), чтобы узнать, какие параметры и предустановленные варианты доступны для вашего аккаунта.

В устаревших тарифах на основе запросов Cursor автоматически включает Max Mode, если этого требует выбранная модель.

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() },});

Cursor Router

Cursor Router выбирает модель для каждого запроса в режиме Auto. В SDK Router представлен моделью auto-smart с параметром optimize_for. Он доступен в Teams и Enterprise. Чтобы auto-smart появилась в каталоге, администратор Enterprise должен включить Router для команды.

Cursor SDK — это SDK для агентов, а не автономный API для инференса моделей или чат-комплишенов. Router выбирает модели для запусков агентов Cursor, которые могут анализировать рабочее пространство, вызывать инструменты, выполнять команды и редактировать файлы. В настоящее время Cursor не документирует низкоуровневый endpoint Router для произвольных вызовов моделей.

Выберите Cost, Balance или Intelligence

Передайте auto-smart и явно задайте optimize_for:

Метка продуктаЗначение SDK
Costcost
Balancebalanced
Возможностьintelligence

В текстах продукта используйте Balance. balanced используйте только как передаваемое значение 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);

Всегда передавайте optimize_for. Не пропускайте этот параметр и не отправляйте устаревшее значение default; поддерживаемый контракт предполагает обнаружение через каталог.

Поиск Router в каталоге моделей

Cursor.models.list() возвращает модели, определения параметров и предустановленные варианты, доступные текущему аккаунту и команде API-ключа. Cursor Router отображается как auto-smart, если Router доступен. Администраторы команды могут отключить Router или ограничить доступные участникам режимы оптимизации.

Перед жёстким заданием выбора используйте каталог как источник достоверной информации:

import { Cursor, type ModelSelection } from "@cursor/sdk";const models = await Cursor.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(    "Cursor 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 }],};

Переключение режимов для отдельных запусков

Переопределите модель в agent.send(), чтобы изменить режим Router для конкретного запуска:

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

Переопределения модели для отдельного запуска сохраняются. Последующие отправки без переопределения продолжают использовать выбранную модель. См. Переопределение модели для отдельного запуска.

Идентификаторы моделей: auto-smart, auto и default

ВыборЗначение
auto-smart с optimize_forCursor Router. Используйте, если хотите выбрать Cost, Balance или возможность.
{ id: "auto" }Выбираемый сервером резервный режим Auto, если конкретная модель отсутствует в каталоге. Если нужен явный режим Router, используйте auto-smart.
Без optimize_for или при передаче defaultНеподдерживаемый контракт Router. Всегда получайте список допустимых значений и передавайте cost, balanced или intelligence.

Оплата и пул маршрутизации

  • Все режимы Auto тарифицируются по прейскурантной цене модели, на которую направляется каждый запрос.
  • Базовая модель может меняться от запроса к запросу. Для воспроизводимых сравнений используйте фиксированный идентификатор модели.
  • Enterprise allowlist определяют пул маршрутизации. Блокировка необходимых моделей может отключить Router.

Актуальные ставки и пул маршрутизации см. в разделах Cursor Router и режимы Auto.

Устранение неполадок: Router отсутствует

Если auto-smart отсутствует или режим оптимизации отклонён:

  1. Вызовите Cursor.models.list().
  2. Убедитесь, что auto-smart присутствует в результатах.
  3. Убедитесь, что optimize_for включает нужное значение (cost, balanced или intelligence).
  4. Убедитесь, что Router включён для команды, связанной с API-ключом.
  5. Если вы состоите в нескольких командах, убедитесь, что ключ используется в контексте нужной команды.
  6. Проверьте политику доступа команды к моделям, если Router недоступен или не может выбрать допустимую базовую модель.

Замена системного промпта

systemPrompt заменяет встроенный системный промпт Cursor для основного цикла агента на ваш собственный текст. Модель теряет роль ассистента по программированию, протокол использования инструментов и правила общения, поэтому заново опишите всё, что по-прежнему нужно агенту. Схемы инструментов, правила и навыки при этом загружаются как обычно, а у субагентов остаются их собственные промпты.

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() },});
  • Только локальные агенты. Сочетание systemPrompt с cloud приводит к ConfigurationError.
  • Значение не должно быть пустым. Текст из одних пробелов приводит к ConfigurationError в Agent.create() или Agent.resume().
  • Не сохраняется в агенте. Передайте systemPrompt повторно в Agent.resume(), чтобы он применялся к последующим запускам.
  • Доступ включается отдельно для каждого аккаунта. Без него первый вызов send() завершится ошибкой с упоминанием --system-prompt.

SDKAgent

Объект-дескриптор, возвращаемый методами Agent.create() и 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>;}
УчастникОписание
agentIdПостоянный идентификатор Agent. agent-<uuid> для локального режима, bc-<uuid> для облачного.
modelТекущий выбор модели. Обновляется после каждого успешного send({ model }). undefined, пока его значение не будет задано (в том числе у возобновлённых Agent, чья вызывающая сторона не передала model).
sendЗапускает новый запуск с указанным промптом. Возвращает дескриптор Run.
closeЗапускает освобождение ресурсов без ожидания. Fire-and-forget.
reloadПовторно считывает конфигурацию файловой системы (хуки, MCP проекта, субагенты) без освобождения ресурсов.
[Symbol.asyncDispose]Асинхронное освобождение ресурсов. Используйте с await using для автоматической очистки.
listArtifactsВыводит список файлов, созданных Agent (только облачный; local возвращает пустой список).
downloadArtifactСкачивает файл по path (только облачный; local вызывает исключение).
getUsageПолучает данные о тарифицируемом использовании токенов и стоимости в долларах для Agent. Для локальных Agent доступ к данным об использовании нужно включить отдельно для каждого аккаунта; см. Agent.getUsage().

Agent.prompt()

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

Удобный одноразовый вызов: создаёт Agent, отправляет один промпт, ждёт завершения запуска и освобождает ресурсы.

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() },});

Отправка сообщений

Каждый вызов agent.send() возвращает Run. Agent сохраняет контекст диалога между запусками; запуск — единица работы для одного промпта.

Запуск

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;}

Стриминг

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;  }}// Продолжите работу с тем же Agent. Состояние диалога из предыдущего// запуска загружается автоматически.const run2 = await agent.send("Fix it and add a regression test");await run2.wait();

Чтобы отправить изображения вместе с текстом:

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

Ожидание без стриминга

const result = await run.wait();console.log(result.status);      // "finished" | "error" | "cancelled"console.log(result.result);      // итоговый текст ассистента, если он естьconsole.log(result.error);       // { message, code? }, если запуск завершился с ошибкойconsole.log(result.model);       // определённый ModelSelection, использованный для этого запускаconsole.log(result.durationMs);console.log(result.usage);       // суммарное использование токенов или undefined, если данные недоступныconsole.log(result.git);         // { branches: [{ repoUrl, branch?, prUrl? }] } в облаке

Итоговый текст ассистента доступен в result.result в виде строки. Не нужно искать его в полях text, message, messages или content. Если вместо этого нужна расшифровка по шагам, вызовите run.conversation(), чтобы получить структурированное представление 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);

Отмена выполнения

await run.cancel();

Отменяет запуск. Статус меняется на "cancelled", поток событий прерывается, выполняющиеся вызовы инструментов останавливаются, а run.wait() завершается со status: "cancelled". Частичные выходные данные (текст assistant, записанный к этому моменту) сохраняются в объекте Run.

Отмена поддерживается для выполняющихся локальных и облачных запусков и не выполняет никаких действий, если запуск уже завершён.

Управление запуском на лету

run.steer(text) вставляет сообщение в уже выполняющийся шаг, не дожидаясь его завершения. Промис разрешается, как только шаг подтвердит, добавлен ли текст. complete_delivered означает, что шаг получил сообщение — повторно его отправлять не нужно. revert_to_followup означает, что шаг его не принял, поэтому отправьте сообщение через agent.send() после завершения запуска.

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");}
  • Только для локальных запусков. Облачные запуски и отсоединённые локальные handle предоставляют steer, но всегда разрешают revert_to_followup.
  • Steering работает, пока выполняется foreground субагент. Субагент переходит в background и продолжает работу, чтобы parent шаг мог принять message.
  • steer — необязательный метод у Run, и он не является RunOperation. Перед вызовом проверяйте run.steer, а не run.supports().

Получение состояния Run

console.log(run.status);  // "running" | "finished" | "error" | "cancelled"const stop = run.onDidChangeStatus((status) => {  console.log(`status changed to ${status}`);});// Вызовите `stop()`, чтобы удалить слушателя.// Структурированное пошаговое представление диалога, накопленного за этот запускconst turns = await run.conversation();

run.conversation() возвращает ConversationTurn[] запуска's (шаг агента с шагами или шаг оболочки с командой и выходными данными). Используйте его для отображения или сохранения структурированной истории запуска без подписки на поток в реальном времени.

Использование токенов

Если среда выполнения предоставляет эти данные, Run сообщает об использовании токенов. Пока Run выполняется, считывайте накопленное значение из run.usage, а после run.wait() — из result.usage. В обоих случаях доступен объект TokenUsage с суммарным использованием по всем шагам, сообщившим об использовании; если ни один шаг этого не сделал, оба значения равны undefined (например, если отменённый Run не завершил ни одного шага или среда выполнения не предоставляет данные об использовании).

interface TokenUsage {  inputTokens: number;  outputTokens: number;  cacheReadTokens: number;  cacheWriteTokens: number;  totalTokens: number;  reasoningTokens?: number;}
ПолеОписание
inputTokensТокены промпта, отправленные модели.
outputTokensТокены, сгенерированные моделью.
cacheReadTokensТокены, полученные из кэша промпта.
cacheWriteTokensТокены, записанные в кэш промпта.
totalTokensinputTokens + outputTokens + cacheReadTokens + cacheWriteTokens. Не включает reasoningTokens.
reasoningTokensТокены рассуждений — подмножество outputTokens. Не указываются, если модель или среда выполнения не сообщает о них.
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 уже учитываются в outputTokens, поэтому totalTokens их не включает, чтобы избежать двойного подсчёта.

Чтобы получать показатели по каждому шагу по мере поступления, обрабатывайте событие потока usage (SDKUsageMessage). Оно срабатывает один раз в конце каждого шага, для которого сообщается использование токенов, и содержит TokenUsage этого шага. run.usage и result.usage остаются накопительными в рамках запуска.

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

Количество токенов сообщает среда выполнения; они ничего не говорят о стоимости. Чтобы узнать тарифицируемое использование и стоимость запусков Agent в долларах, вызовите agent.getUsage().

Сопоставление запусков по requestId

Каждому вызову agent.send() платформа присваивает UUID, доступный как requestId в Run и RunResult. Используйте его, чтобы сопоставлять запуск скрипта или CI с логами бэкенда, аналитикой и тредами поддержки, а не полагаться только на agentId.

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

requestId сохраняется вместе с запуском и передаётся через локальные хранилища в памяти, SQLite и JSONL, а также задаётся для облачных запусков, когда backend его возвращает. Логируйте его вместе с error.requestId из ошибок, чтобы один идентификатор использовался как при успешном выполнении, так и при ошибках.

Переопределение модели для одного запуска

Модель model, переданная в agent.send(), переопределяет выбранную Agent модель для этого запуска и затем сохраняется: в последующих отправках без переопределения будет использоваться новая модель. Чтобы вернуться к прежней модели, передайте другое значение model или получите текущий выбор из agent.model.

const run = await agent.send("Plan the refactor", {  model: { id: "composer-2.5", params: [{ id: "fast", value: "true" }] },});console.log(agent.model);  // после успешной отправки принимает значение из override

run.model и result.model отражают модель, выбранную для данного запуска, и не изменяются после его начала.

Переменные среды для одного запуска

Облачным агентам также можно передавать переменные среды для одного запуска. Передайте cloud.envVars в agent.send(), и значения будут доступны в оболочке агента только во время этого запуска — после его завершения они удаляются из VM, и в следующем запуске их уже не будет. Это подходит для учётных данных, которые меняются между шагами, например для краткосрочного токена развёртывания, выпускаемого непосредственно перед тем, как попросить агента его использовать.

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

Если переменная в рамках запуска имеет то же имя, что и переменная в рамках агента из cloud.envVars в Agent.create(), для этого запуска приоритет имеет значение переменной запуска, а при следующем запуске снова используется значение переменной агента.

Переменные для отдельного запуска работают и при первой отправке. SDK передаёт их при создании агента и привязывает к начальному запуску, поэтому они не сохраняются в агенте. Как и переменные в рамках агента, они шифруются при хранении, а их имена не могут начинаться с CURSOR_.

Переменные среды для отдельного запуска доступны только для облачных агентов и недоступны агентам, работающим с общедоступными репозиториями. Для локальных агентов процесс агента наследует ваше окружение, поэтому задайте переменные в процессе перед вызовом send().

Режим диалога

Передайте mode: "plan" или mode: "agent", чтобы указать, должен ли запуск сначала изучить задачу и составить план или сразу внести изменения. О том, как работает режим планирования в продукте, см. Режим планирования.

Задайте mode в Agent.create(), чтобы установить режим для первого запуска. В последующих вызовах agent.send() не указывайте mode, чтобы сохранить текущий режим диалога, либо передайте mode, чтобы переключить режим только для этого запуска.

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();

Стриминг необработанных дельт

run.stream() возвращает нормализованные события SDKMessage. Чтобы получать обновления более низкого уровня (текст по токенам, потоковую передачу аргументов вызова инструмента, дельты размышлений, обновления вложенных задач, границы шагов), передайте в send() колбэки onDelta и onStep:

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}`);  },});

Перед обработкой следующего обновления выполняется ожидание колбэков, поэтому можно применять обратное давление. InteractionUpdate включает 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 и несколько дельт сводки и выходных данных shell.

Параметры для каждой отправки

СвойствоТипОписание
modelModelSelectionПереопределение модели для данной отправки. Если не указано, используется agent.model. Сохраняется: успешная отправка обновляет agent.model.
mode"agent" | "plan"Переопределение режима диалога для данной отправки. Если не указано в последующих сообщениях, сохраняется текущий режим диалога.
mcpServersRecord<string, McpServerConfig>Определения серверов MCP, заданные inline. Полностью заменяют серверы, заданные при создании, для этого запуска.
onStep(args: { step }) => void | Promise<void>Колбэк после каждого завершённого шага диалога (текста, размышления или пакета инструментов).
onDelta(args: { update }) => void | Promise<void>Колбэк для каждого необработанного InteractionUpdate.
idempotencyKeystringНеобязательный ключ идемпотентности, созданный клиентом для отправки.
cloud.envVarsRecord<string, string>Только для облачных агентов. Переменные среды для каждого запуска, добавляемые для этого запуска и удаляемые после его завершения. Переопределяют cloud.envVars, привязанные к агенту, по имени только для этого запуска.
local.forcebooleanТолько для локальных агентов. По умолчанию false. Завершает зависший активный запуск перед отправкой этого сообщения. В Cloud сервер возвращает 409 agent_busy, поэтому эквивалент не нужен.
local.customToolsRecord<string, SDKCustomTool>Только для локальных агентов. Пользовательские инструменты для этого запуска. Заменяют local.customTools, заданные при создании агента, для данного запуска.

В следующих трёх разделах приведена подробная справка по SDKMessage, InteractionUpdate и ConversationTurn. При первом чтении их можно бегло просмотреть или пропустить; Возобновление работы агентов продолжает изложение.

События потока

События, поступающие из run.stream(). Различаются по type. Все события включают agent_id и run_id.

type SDKMessage =  | SDKSystemMessage  | SDKUserMessageEvent  | SDKAssistantMessage  | SDKThinkingMessage  | SDKToolUseMessage  | SDKStatusMessage  | SDKTaskMessage  | {      type: "request";      agent_id: string;      run_id: string;      request_id: string;    }  | SDKUsageMessage;
typeОписаниеКлючевые поля
"system"Метаданные инициализации. Отправляются один раз в начале запуска.subtype? ("init"), model?, tools?
"user"Копия пользовательского промпта для этого запуска.message.content: TextBlock[]
"assistant"Текстовые выходные данные модели.message.content: (TextBlock | ToolUseBlock)[]
"thinking"Содержимое рассуждений.text, thinking_duration_ms?
"tool_call"Жизненный цикл вызова инструмента. Отправляется в начале с args, затем после завершения с result.call_id, name, status, args?, result?, truncated?
"status"Переходы жизненного цикла облачного запуска.status, message?
"task"Вехи и сводки на уровне задачи.status?, text?
"request"Ожидание ввода или одобрения от пользователя.request_id
"usage"Использование токенов для каждого хода; отправляется один раз в конце хода, если среда выполнения сообщила об этом.usage (TokenUsage)

Данные результата (итоговый текст, модель, длительность, суммарное использование токенов, метаданные Git) доступны в объекте Run после завершения потока. Чтобы прочитать их, используйте run.wait().

Схема вызова инструмента не является стабильной. Полезные нагрузки args и result в событиях tool_call соответствуют внутренней структуре каждого инструмента и могут меняться по мере развития инструментов. Имена инструментов также могут меняться или заменяться. Считайте args и result значениями типа unknown и выполняйте их разбор с учётом возможных изменений. Обёртка события (type, call_id, name, status) стабильна.

Типы сообщений

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 отправляется дважды для большинства вызовов инструментов: сначала со status: "running" и заполненным args, затем по завершении — со status: "completed" (или "error") и заполненным result. truncated указывает, усёк ли SDK args или result из-за слишком большого payload.

SDKStatusMessage описывает переходы жизненного цикла на стороне Cloud. CREATING включает подготовку VM и клонирование repo; RUNNING означает, что Agent выполняет работу; остальные статусы являются конечными.

SDKUsageMessage отправляется один раз в конце каждого шага, для которого было зафиксировано использование токенов, и содержит TokenUsage этого шага. Накопленный итог по всем шагам хранится в run.usage и result.usage. См. Использование токенов.

Обновления взаимодействия

InteractionUpdate — это тип необработанной дельты, передаваемой в колбэк onDelta при вызове agent.send(). Эти обновления детальнее событий SDKMessage: текст поступает токен за токеном, вызовы инструментов передают частичное состояние по мере накопления args, рассуждения поступают в реальном времени.

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

Типы обновлений

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 содержит один уровень вложенных обновлений взаимодействия из вызова инструмента задачи или субагента. PartialToolCallUpdate генерируется, когда модель передаёт аргументы в вызов инструмента до его подтверждения. Здесь действует то же предупреждение о стабильности, что и для SDKToolUseMessage.args.

Типы диалогов

Структурированное представление выполнения по шагам, возвращаемое run.conversation() и используемое в аргументе функции обратного вызова 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 — дискриминируемое объединение всех встроенных инструментов (shell, edit, read, write, glob, grep, ls, semSearch, mcp, task и других). Его структура предназначена для внутреннего использования; см. примечание о стабильности в разделе «События потока».

Возобновление работы агентов

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

Используйте Agent.resume(), чтобы повторно подключиться к существующему агенту по ID. Распространённые сценарии: повторное подключение к ранее запущенному облачному агенту, работающему длительное время, или продолжение диалога после перезапуска локального процесса. Среда выполнения определяется автоматически по префиксу ID (bc- — облачная, всё остальное — локальная).

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 имеет значение undefined, если не передать model снова. Inline mcpServers не сохраняются между возобновлениями — они часто содержат секреты и хранятся только в памяти. Передавайте их снова при возобновлении или используйте файловую конфигурацию MCP (.cursor/mcp.json + local.settingSources) для серверов, которые должны сохраняться.

Просмотр агентов и запусков

Просматривайте список, получайте данные и перезагружайте ранее созданных агентов. Конечные точки списка возвращают { items, nextCursor? } для пагинации на основе курсора.

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;       // локальная маршрутизация  apiKey?: string;    // маршрутизация в облаке  store?: LocalAgentStore;}

Среда выполнения автоматически определяется по префиксу идентификатора агента (bc- → облачная, иначе локальная).

Agent.listRuns()

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

Agent.getRun()

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

Cloud getRun требует родительский agentId.

Agent.cancelRun()

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

Отменяет запуск, если известен его идентификатор, но нет объекта 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;}

Возвращает сохранённые сообщения пользователя и ассистента локального агента.

Agent.getUsage()

Возвращает оплачиваемое использование токенов и стоимость запусков Agent в долларах. Вызывайте метод для handle или статически по ID, если handle отсутствует. Облачные агенты возвращают разбивку по запускам, локальные — по шагам. Передайте runId, чтобы ограничить результат одной записью: для облачных агентов — ID запуска вида run-<uuid>, для локальных Agent — ID из предыдущего getUsage().runs[].runId.

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;   // суммарно по всем `runs`  cost?: UsageCost;    // суммарно по всем `runs`  runs: RunUsage[];}interface RunUsage {  runId: string;  usage: TokenUsage;  cost?: UsageCost;}interface UsageCost {  rawCostCents: number;   // стоимость токенов модели без скидок; 0 при тарификации за запрос  chargedCents: number;   // списанная сумма с учётом скидок и платы за токены Cursor}
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);}

Стоимость включает скидки и может окончательно сформироваться не сразу после завершения запуска; cost отсутствует, пока это не произойдёт. chargedCents равен 0 для использования, включённого в тариф, BYOK (Bring Your Own Key) и использования кредитных грантов.

Это отличается от использования токенов: run.usage — текущее количество токенов в одном запуске, а getUsage() — учётная запись начислений по всем запускам Agent.

Жизненный цикл облачных агентов

Облачные агенты остаются в рабочем пространстве вашей команды, пока вы не архивируете или не удалите их. Agent.list({ runtime: "cloud" }) по умолчанию не показывает архивированных агентов; чтобы увидеть их, передайте includeArchived: true. Отфильтруйте по prUrl, чтобы найти агента, открывшего конкретный pull request.

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);     // мягкое удаление; transcript остаётся доступным для чтенияawait Agent.unarchive(agentId);   // восстановить архивированный Agentawait Agent.delete(agentId);      // безвозвратно; последующие запросы на чтение вернут 404

SDKAgentInfo

Структура метаданных, возвращаемая методами Agent.list() и 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>;    });

Пространство имён Cursor

Получение данных уровня аккаунта, чтение каталога и конфигурация SDK для всего процесса. Методы чтения принимают необязательный параметр { apiKey }; иначе используется CURSOR_API_KEY, а затем сохранённый вход через браузер.

Cursor.auth

Интерактивный вход для хостов без предварительно настроенного API-ключа. Cursor.auth.login() открывает в браузере страницу входа на сайте Cursor, ожидает завершения входа и выпускает пользовательский API-ключ (по умолчанию сроком на 90 дней), который хранится в ~/.cursor/sdk/auth.json. После входа Agent.create(), Cursor.me() и другие операции чтения работают без apiKey или CURSOR_API_KEY.

import { Cursor } from "@cursor/sdk";await Cursor.auth.login();const status = await Cursor.auth.status();// { status: "logged-in", backendUrl, email?, apiKeyExpiresAtMs? }// | { status: "logged-out" }await Cursor.auth.logout();
ПараметрОписание
backendUrlБазовый URL API. По умолчанию используется CURSOR_BACKEND_URL, затем продакшен.
websiteUrlБазовый URL для входа через браузер. По умолчанию используется CURSOR_WEBSITE_URL, затем продакшен.
openBrowsertrue (по умолчанию) открывает системный браузер, если это, скорее всего, сработает; false никогда не открывает браузер; функция — пользовательский обработчик открытия. Не используется в SSH-сессиях или при заданной NO_OPEN_BROWSER.
onLoginUrlВызывается с URL входа перед ожиданием, чтобы хост мог его отобразить. Если параметр не указан и браузер не открыт, URL записывается в stderr.
signalAbortSignal, отменяющий ожидание; в этом случае login вызывает AuthenticationError.
storeМесто хранения учётных данных. По умолчанию — ~/.cursor/sdk/auth.json; передайте null, чтобы получить ключ только в результате.
apiKeyNameОтображаемое имя выпущенного ключа в списке API-ключей дашборда.
apiKeyTtlMsСрок действия выпущенного ключа в миллисекундах. По умолчанию — 90 дней.

Cursor.auth.login() возвращает { apiKey, email?, apiKeyExpiresAtMs }. Используйте FileCredentialStore или InMemoryCredentialStore, чтобы передать пользовательский store в login(), status() или logout().

Порядок определения учётных данных во всём SDK: явный apiKey, затем CURSOR_API_KEY, затем сохранённые данные входа. Сохранённые данные входа не считывают учётные данные из локальной установки приложения Cursor; в них хранятся только ключи, выпущенные Cursor.auth.login().

Cursor.configure()

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

Задайте значения по умолчанию для локальных агентов, применяемые к последующим вызовам Agent.*. Поля, указанные в отдельном вызове, переопределяют эти значения; передайте null, чтобы сбросить ранее заданное значение по умолчанию.

ПараметрОписание
local.storeХранилище локальных агентов по умолчанию, если в вызове не указан local.store. SDK использует SQLite на диске через node:sqlite; если этот модуль недоступен, Agent.create() выбрасывает ConfigurationError — если только вы не укажете здесь JsonlLocalAgentStore или другое хранилище.
local.useHttp1ForAgentПринудительно использовать HTTP/1.1 с SSE вместо HTTP/2 для потоков бэкенда локальных агентов. Полезно при работе через прокси или со стеками fetch, не поддерживающими HTTP/2.

Bun по умолчанию использует HTTP/1.1 из-за проблем совместимости с HTTP/2 в вышестоящих компонентах.
local.workspaceScanCacheTtlMsКак долго SDK повторно использует результаты сканирования рабочего пространства (правила, навыки, AGENTS.md, файлы игнорирования), в миллисекундах. По умолчанию — 20 секунд. Увеличьте значение для долгоживущего хоста, обслуживающего рабочую копию, которая меняется только при развёртывании; компромисс — менее актуальные данные, поскольку правило, добавленное после запуска процесса, может оставаться незамеченным в течение этого времени. Переменная среды CURSOR_RIPWALK_CACHE_TTL_MS задаёт то же значение.
import { Cursor, JsonlLocalAgentStore } from "@cursor/sdk";Cursor.configure({  local: {    store: new JsonlLocalAgentStore("/var/lib/cursor-agents"),    useHttp1ForAgent: true,  },});

Cursor.me()

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

Cursor.models.list()

function Cursor.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;}

Используйте Cursor.models.list(), чтобы узнать допустимые идентификаторы model и параметры params для каждой модели перед вызовом Agent.create() или agent.send(). Параметры зависят от модели. Среди распространённых примеров — уровень усилий рассуждения и optimize_for Cursor Router для auto-smart.

Каталог зависит от аккаунта и команды. Cursor Router отображается как auto-smart, только если Router доступен для команды, связанной с API-ключом. См. Cursor Router.

const models = await Cursor.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" },//     ],//   },// ]

Передавайте выбранные значения параметров через model.params. Предустановленные variants уже содержат допустимые params, поэтому их можно скопировать в параметры выбора модели.

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() },});

Рекомендации по лучшим практикам

  • Получайте данные, а не задавайте их жёстко. Вызывайте Cursor.models.list() при запуске (или один раз для каждого процесса) и кэшируйте результат. Идентификаторы моделей и наборы параметров могут меняться с выходом новых моделей.

  • Явно передавайте параметры, если модель их ожидает. Модель с непустым массивом parameters — параметризуемая. Передавайте нужные параметры; иначе при запуске будет использоваться первое допустимое значение каждого параметра, которое может не соответствовать вашим намерениям. Для Cursor Router всегда явно передавайте optimize_for.

  • Выбирайте по возможностям, а не по идентификатору. Если вам нужна «текущая модель по умолчанию в быстром режиме», а не конкретная модель, найдите её так:

    const models = await Cursor.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" }],    };

    Если целевая модель недоступна, предпочитайте явный выбор Router (auto-smart + optimize_for). Используйте { id: "auto" } только если хотите, чтобы сервер выбрал Auto без указания Cost, Balance или Intelligence.

Cursor.repositories.list()

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

Возвращает репозитории GitHub, подключённые к команде вызывающего пользователя. Только для Cloud.

Краткий обзор источников конфигурации

MCP‑серверы, субагенты и хуки получают конфигурацию из сочетания inline-параметров и файлов конфигурации на диске. Приоритет одинаков для всех трёх: inline при отправке > inline при создании > файлы проекта > файлы пользователя > конфигурация команды / дашборда.

ФункцияInline-параметрЛокальный файл (проект)Локальный файл (пользователь)Облако / дашбордПриоритет
MCP‑серверыmcpServers в Agent.create() и agent.send().cursor/mcp.json (если local.settingSources включает "project")~/.cursor/mcp.json (если включён "user")Серверы, настроенные на cursor.com/agents (только в облаке)Отправка > создание > плагины > проект > пользователь (локально); Отправка > создание > дашборд (в облаке)
Субагентыagents в Agent.create().cursor/agents/*.md (фронтматтер: name, description, model?)н/дВ облаке используются те же файлы проекта, когда агент работает с клонированным репозиториемInline-параметры переопределяют файловые конфигурации с тем же именем
ХукиНет — только файловые.cursor/hooks.json (+ скрипты)~/.cursor/hooks.jsonОблачные запуски используют хуки проекта. На тарифах Enterprise также используются хуки команды и хуки, управляемые на уровне Enterprise.Файловые; конфигурация проекта дополняется пользовательской / командной / Enterprise согласно Hooks
Источники настроекlocal.settingSources выбирает, какие слои конфигурации на диске загружать.cursor/~/.cursor/н/дОблако всегда загружает project / team / plugins и игнорирует local.settingSources.

Inline-значения подходят для секретов, которые не должны попадать на диск (API-ключи для отдельных запусков, токены с областью действия тенанта). Файловая конфигурация подходит для политик: хуки, в частности, задаются на уровне проекта, а не для отдельного запуска.

MCP‑серверы

Агенты могут подключать MCP‑серверы из нескольких источников. Чаще всего используются встроенные определения в Agent.create() или agent.send(). Также поддерживаются конфигурации в файлах и конфигурации, управляемые через дашборд.

Что загружается

Локальные агенты загружают серверы не более чем из пяти источников. При совпадении имён используется первый найденный:

  1. mcpServers в agent.send(). Полностью заменяет серверы, заданные при создании, для этого запуска (без объединения).
  2. mcpServers в Agent.create(). Используется, если для отдельной отправки не задано переопределение.
  3. Серверы плагинов, если local.settingSources включает "plugins".
  4. Серверы проекта из .cursor/mcp.json, если local.settingSources включает "project".
  5. Пользовательские серверы из ~/.cursor/mcp.json, если local.settingSources включает "user".

Без local.settingSources загружаются только встроенные серверы. Если локальному MCP-серверу требуется вход через OAuth, SDK не сможет предложить войти в систему. Это работает, только если вы уже вошли в этот сервер через приложение Cursor: в таком случае SDK использует сохранённые данные для входа.

Облачные агенты загружают серверы из следующих источников:

  1. mcpServers в agent.send(). Полностью заменяет серверы, заданные при создании, для этого запуска (без объединения).
  2. mcpServers в Agent.create(). Используется, если для отдельной отправки не задано переопределение.
  3. Ваши пользовательские и командные MCP‑серверы с cursor.com/agents.

Если встроенный сервер не включает auth или headers, а вы ранее авторизовали URL этого сервера на cursor.com/agents, запуски, аутентифицированные личным API-токеном, автоматически используют те же OAuth-токены. API-ключи сервисных аккаунтов не могут использовать пользовательскую аутентификацию как резервный вариант, поскольку не связаны с пользователем.

local.settingSources не применяется к облачным агентам.

Локальный

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

Облачные агенты также могут получать аутентифицированные конфигурации MCP напрямую. Используйте HTTP-аутентификацию, если Cursor должен проксировать удалённый MCP через бэкенд. Используйте env в stdio, если сервер работает в облачной VM и получает учётные данные из переменных среды.

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!,      },    },  },});

Используйте headers для статических API-ключей или Bearer-токенов — Cursor передаёт их при каждом запросе. Используйте auth для серверов, защищённых OAuth. В облаке Cursor один раз выполняет поток OAuth на стороне сервера и повторно использует токен между запусками. Локально SDK не может открыть браузер для входа в аккаунт; он лишь повторно использует токены, уже полученные при входе через приложение Cursor.

  • HTTP headers и auth обрабатываются бэкендом Cursor. Конфиденциальные поля скрываются и не передаются в VM.
  • Значения Stdio env передаются в VM, поскольку сервер запускается там. Обращайтесь с ними как с любыми другими секретами среды выполнения.
  • OAuth для MCP-серверов, настроенных на cursor.com/agents, остаётся отдельным для каждого пользователя, даже для серверов уровня команды.

Полный формат конфигурации см. в разделе MCP, а сведения о поведении в облаке — в разделе возможности Cloud Agent.

Субагенты

Определяйте именованных субагентов, которых основной агент запускает с помощью инструмента Agent. Передавайте их inline:

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.",    },  },});

Также учитываются субагенты, сохранённые в репозитории по пути .cursor/agents/*.md (с фронтматтером name, description и необязательным model). Встроенные определения имеют приоритет над файловыми определениями с тем же именем.

Вложенные субагенты

Субагенты могут запускать собственных субагентов с ограничением глубины вложенности. Когда субагент использует инструмент Agent, SDK передаёт ему тот же исполнитель субагентов, что и родительскому агенту, поэтому родительский агент может делегировать задачу субагенту, который затем делегирует её дальше. На каждом уровне доступен один и тот же набор именованных субагентов и пользовательских инструментов. Агент верхнего уровня и его прямые субагенты могут запускать субагентов, но субагент, запущенный другим субагентом, не может запускать новых субагентов.

Фоновые субагенты

Когда agent запускает субагента в фоне, результат субагента возвращается родительскому агенту как дополнительный шаг в рамках того же run, а не отбрасывается по завершении родительского шага. run.stream() продолжает выдавать события в течение этих шагов, а run.wait() завершается после них, возвращая текст последнего шага в result. Только для локальных агентов.

Ограничение набора инструментов

tools задаёт allowlist встроенных инструментов, доступных модели; disallowedTools удаляет указанные инструменты, оставляя остальные, включая добавленные на платформу после выпуска вашей версии SDK. Пока оба параметра доступны только для локальных агентов и не сохраняются в Agent: передавайте их снова в Agent.resume(), чтобы ограничение действовало и при последующих запусках.

// Агент только для чтения: ему доступны только эти инструменты.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() },});// Всё, кроме доступа к командной оболочке.const noShell = await Agent.create({  apiKey: process.env.CURSOR_API_KEY!,  model: { id: "composer-2.5" },  disallowedTools: ["shell"],  local: { cwd: process.cwd() },});
  • tools: undefined (по умолчанию) предоставляет стандартный набор инструментов для выбранной модели; tools: [] не предоставляет встроенных инструментов, поэтому модель может отвечать только текстом.
  • Оба поля принимают объединение ToolName: публичные имена ("read", "edit", "task", "webSearch", ...), группы возможностей "shell" и "mcp", а также необработанные имена proto-инструментов. Неизвестные имена вызывают ConfigurationError в Agent.create() / Agent.resume().
  • Запрет имеет приоритет: чтобы инструмент был доступен, он должен присутствовать в tools (если задан) и отсутствовать в disallowedTools.
  • Запрет "mcp" также отключает пользовательские инструменты. Запрет "task" запрещает использование субагентов; в противном случае субагенты сохраняют собственные тщательно подобранные наборы инструментов.

Пользовательские инструменты

Пользовательские инструменты позволяют предоставить агенту собственные функции без развёртывания отдельного сервера MCP. Передайте их в local.customTools, и SDK зарегистрирует их как сервер MCP с именем custom-user-tools. Агент обнаруживает и вызывает их через тот же MCP-механизм, что и на любом другом сервере. Правила запрета и ограничения песочницы по-прежнему действуют, но для пользовательских инструментов не требуется интерактивное одобрение, поэтому при запусках в песочнице и с автоматическим ревью они вызываются без запроса. Пользовательские инструменты также доступны субагентам, включая вложенных.

Пользовательские инструменты доступны только для локальных агентов. Облачные агенты игнорируют local.customTools при создании и вызывают ConfigurationError, если передать его в 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());

Задайте пользовательские инструменты один раз в Agent.create(), чтобы использовать их во всех запусках, или передайте local.customTools в отдельный вызов agent.send(), чтобы заменить их только для этого запуска.

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." }] };        },      },    },  },});

Определение инструмента

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;  sessionId?: string;}
ПолеОписание
descriptionПоказывается модели, чтобы она знала, когда вызывать инструмент. По умолчанию — пустая строка.
inputSchemaJSON Schema для аргументов. По умолчанию — открытый object, допускающий любые свойства.
outputSchemaJSON Schema для структурированного результата инструмента, сообщаемая модели как MCP Tool.outputSchema. Результаты по ней не проверяются.
annotationsАннотации MCP инструмента (title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint), передаваемые модели. Это лишь описательные подсказки; SDK никак их не применяет.
executeВаш callback. Получает разобранные args и context с toolCallId и sessionId локальной сессии, которая вызвала инструмент. У каждого субагента свой sessionId. Выполняется в вашем process, поэтому имеет доступ ко всему, что доступно вашему коду.

Результаты работы инструментов

execute может возвращать простую строку, любое значение JSON или структурированную обёртку. Ключом в map служит имя инструмента, вызываемого моделью.

type SDKCustomToolResult =  | string  | SDKJsonValue  | {      content: SDKCustomToolContent[];      isError?: boolean;      structuredContent?: Record<string, SDKJsonValue>;    };type SDKCustomToolContent =  | { type: "text"; text: string }  | { type: "image"; data: string; mimeType?: string };
  • Возвращайте строку для текстового вывода.
  • Возвращайте любое значение JSON, чтобы отправить его как текст; объекты также заполняют structuredContent.
  • Возвращайте обёртку для полного контроля: сочетайте текст и base64-изображение в content, задайте isError: true, чтобы сообщить об ошибке, или прикрепите structuredContent, чтобы модель могла его обработать. Исключение, выброшенное из execute, также передаётся Agent как ошибка инструмента.

Хуки

Хуки поддерживаются только в файлах. Программные callback-функции для хуков не предусмотрены. Хуки определяют границы политик проекта, а не настраиваются для каждого отдельного запуска.

  • Локально: Добавьте .cursor/hooks.json в репозиторий, переданный в local.cwd, или ~/.cursor/hooks.json для хуков уровня пользователя.
  • Облако: Закоммитьте .cursor/hooks.json и связанные с ним скрипты в репозиторий, переданный в cloud.repos. Облачные агенты, созданные через SDK, автоматически загружают хуки проекта. На тарифах Enterprise они также запускают хуки команды и хуки, управляемые на уровне Enterprise.

См. Хуки для информации о формате конфигурации и Поддержка хуков Cloud Agents — о поведении в облаке.

Параметры песочницы

По умолчанию локальные агенты запускаются с local.sandboxOptions.enabled: false. Агент может без ограничений читать и записывать файлы в рабочем каталоге, выполнять shell-команды и обращаться к сети. В запусках SDK без графического интерфейса нет процесса одобрения с участием человека, поэтому песочница по умолчанию либо молча блокировала бы допустимые вызовы инструментов, либо требовала бы callback, не подходящий для скрипта.

При включении песочницы SDK ограничивает каждый вызов shell-инструмента и каждый процесс, запущенный из shell:

  • Файловая система. Запись ограничена рабочим каталогом (local.cwd), временными каталогами и путями, которые вы разрешили в sandbox.json. Чтение не ограничено рабочим пространством.
  • Shell. Команды выполняются в песочнице платформы (bubblewrap в Linux, seatbelt в macOS, встроенный помощник @cursor/sdk-<os>-<arch>). Привилегированные операции запрещены.
  • Сеть. Исходящие сетевые подключения по умолчанию запрещены. Чтобы разрешить доступ к определённым хостам, добавьте в рабочее пространство файл .cursor/sandbox.json со списком разрешённых хостов. Если файл существует, SDK также читает ту же политику для пользователя из ~/.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 },  },});

Если песочница не поддерживается на хосте (устаревшая версия Linux без bubblewrap или отсутствует вспомогательный бинарный файл), SDK выбрасывает ConfigurationError с указанием отсутствующей зависимости. Отключите sandboxOptions.enabled или запустите в облачном режиме, чтобы устранить проблему.

Облачные запуски всегда выполняются в изолированной VM, поэтому sandboxOptions к ним не применяется.

Авто-ревью

По умолчанию локальный Agent выполняет все вызовы инструментов без ограничений, поскольку при запусках без графического интерфейса некому их одобрять. Задайте local.autoReview: true, чтобы вместо этого направлять вызовы локальных инструментов через Автоматическое ревью — тот же классификатор, который IDE использует для разрешения или блокировки вызовов Shell, MCP и Fetch с учётом безопасности и соответствия каждого вызова цели запуска.

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

Для автоматического ревью на подключённом бэкенде должен быть включён классификатор; если он недоступен, запуски возвращаются к поведению по умолчанию. Поскольку в headless-запуске нет интерактивного одобрения, вызов, заблокированный классификатором, отклоняется, а не передаётся на рассмотрение; агент получает причину блокировки и может попробовать другой подход. Настройте классификатор с помощью блока autoRun в файле permissions.json рабочего пространства — так же, как в IDE. Формат см. в разделе permissions.json.

Автоматическое ревью доступно только для локальных агентов. Облачные запуски уже выполняются в изолированной VM. Классификатор — это вспомогательная функция, работающая по мере возможностей, а не граница безопасности; для строгого контроля используйте его вместе с sandboxOptions или allowlist.

Артефакты

Просматривайте и скачивайте файлы из рабочего пространства агента.

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);

Поддержка артефактов зависит от среды выполнения. Локальные агенты SDK пока не возвращают артефакты, а downloadArtifact вызывает ошибку.

Управление ресурсами

Всегда освобождайте агентов после завершения работы с ними. Самый удобный способ — await using:

await using agent = await Agent.create({ /* ... */ });// ресурсы автоматически освобождаются при выходе из блока

Чтобы явно освободить ресурс:

await agent[Symbol.asyncDispose]();

agent.close() — документированный способ начать освобождение ресурсов без ожидания. Symbol.asyncDispose работает (на нём основан await using), но в коде, где не используется синтаксис await using, следует вызывать close(). agent.reload() применяет изменения конфигурации файловой системы (хуки, MCP проекта, субагенты) без освобождения ресурсов.

Жизненный цикл Agent

Предварительная подготовка локальной рабочей области

Определение содержимого локальной рабочей области (правил, навыков, MCP‑серверов, файлов игнорирования) — самая медленная часть первого шага локального агента, и в большом репозитории она может занимать большую часть времени. По умолчанию эти затраты возникают при первом вызове send(). Хост, который знает, где будут запускаться его агенты, может выполнить это заранее с помощью 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"] },});// Первый вызов send() для этого рабочего пространства начинается сразу.await release(); // при завершении работы

Передайте те же AgentOptions, которые будут использовать ваши агенты; предварительный прогрев помогает только при отправках с совпадающими параметрами рабочей области. Вызовите возвращённую функцию release при остановке хоста.

Повторное подключение к существующему Agent

Agent.resume(agentId) возвращает новый дескриптор уже существующего Agent. Среда выполнения автоматически определяется по префиксу ID (bc- — Cloud, всё остальное — Local), а состояние диалога загружается из Cloud (Cloud) или локального хранилища контрольных точек (Local). Это позволяет продолжить работу после перезапуска процесса или передать Agent другому воркеру, если он был запущен другим процессом.

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();

Если запуск уже выполнялся в момент повторного подключения, Agent.getRun(runId, { runtime: "cloud", agentId }) (или локальный эквивалент) возвращает Run, для которого можно вызвать stream(), wait() или cancel().

Контекст диалога

Локальные агенты сохраняют состояние диалога в хранилище контрольных точек. По умолчанию используется SQLite на диске в домашнем каталоге; его можно заменить на JSONL или пользовательский бэкенд с помощью local.store. Каждый вызов agent.send() загружает последнюю контрольную точку этого агента и передаёт её модели, поэтому последующие обращения получают тот же контекст, на котором завершился предыдущий запуск. Хранилище сохраняется после перезапуска процесса, поэтому Agent.resume(agentId), вызванный из совершенно нового процесса, продолжает работу с того места, где остановился предыдущий.

Облачные агенты сохраняют состояние на сервере. Повторное подключение из любого места возвращает тот же диалог.

Несколько ситуаций, которые выглядят как потеря контекста, но не являются ею:

  • Каждый новый Agent.create() создаёт нового агента с новым agentId. Чтобы продолжить существующий диалог, сохраните agent.agentId из первого вызова и позже используйте Agent.resume(agentId).
  • Agent.prompt() за один вызов создаёт агента, запускает его и освобождает ресурсы. Второго шага нет — таков контракт.
  • Inline mcpServers не сохраняются между вызовами Agent.resume(), поскольку часто содержат секреты. Передайте их снова при возобновлении или используйте файловую конфигурацию MCP.

Шаблон диспетчера

Диспетчер управляет пулом агентов и распределяет между ними поступающие задачи. Всё устроено просто: храните сопоставление agentId с долгоживущим SDKAgent, направляйте входящие промпты по ключу (пользователь, репозиторий, тикет) и восстанавливайте агента с диска с помощью Agent.resume(), если перезапуск процесса очистил сопоставление в памяти.

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();}

SSE-потоки Cloud сохраняют накопленные события некоторое время после начала запуска, поэтому диспетчер, транслирующий поток нескольким подписчикам, может вызывать run.stream() для каждого из них, не теряя ранние события. Для особо длительных запусков в Cloud диспетчеры обычно распределяют вызовы run.wait(), а подписчики при необходимости могут опрашивать run.conversation(), чтобы получить структурированную расшифровку диалога.

Хранилища локальных агентов

Локальные агенты сохраняют на диске метаданные агентов, контрольные точки диалогов, запуски и события запусков, чтобы последующие запросы и Agent.resume() сохранялись после перезапуска процесса. По умолчанию SDK использует SQLite на диске через node:sqlite. Если этот модуль недоступен, Agent.create() выбрасывает ConfigurationError, а не переключается на резервный вариант. Другой бэкенд можно подключить через local.store.

SDK включает два бэкенда и позволяет использовать собственный:

ХранилищеИмпортКогда использовать
SqliteLocalAgentStore@cursor/sdk/sqliteSQLite на диске в корневом каталоге состояния рабочей области.
JsonlLocalAgentStore@cursor/sdkПереносимые JSON-файлы с разделителями строк (NDJSON) в выбранном вами каталоге. Их легко просматривать, копировать и сравнивать.
Пользовательский LocalAgentStoreВаш кодСохраняйте данные где угодно: в памяти, Redis, Postgres или размещённой базе данных. Реализуйте интерфейс или компонуйте подхранилища.

Облачные агенты сохраняют данные на сервере, поэтому local.store применяется только к локальным агентам.

Хранилище JSONL

JsonlLocalAgentStore записывает четыре файла NDJSON (agents.ndjson, runs.ndjson, run_events.ndjson, checkpoints.ndjson) в переданный каталог. Создайте экземпляр и передайте его в 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 },});

Передавайте один и тот же экземпляр хранилища в Agent.resume() и локальные API получения списков и данных (Agent.list, Agent.get, Agent.listRuns, Agent.getRun), чтобы они работали с одними и теми же данными.

Задать хранилище по умолчанию для всего процесса

Чтобы не передавать хранилище в каждом вызове, один раз задайте значение по умолчанию с помощью Cursor.configure(). При передаче local.store в отдельном вызове оно имеет приоритет.

import { Cursor, JsonlLocalAgentStore } from "@cursor/sdk";Cursor.configure({ local: { 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: null в Cursor.configure({ local: { store: null } }), чтобы сбросить ранее заданное значение по умолчанию и снова использовать локальное хранилище SDK по умолчанию.

Пользовательские хранилища

Чтобы сохранять данные в другом месте (например, в общем Postgres, Redis или карте в памяти для тестов), реализуйте LocalAgentStore. Он состоит из четырёх подхранилищ — небольших CRUD-интерфейсов, которые вызывает SDK:

interface LocalAgentStore {  readonly agents: LocalAgentStoreAgents;         // строки метаданных агента  readonly checkpoints: LocalAgentStoreCheckpoints; // двоичные объекты диалога, адресуемые по содержимому  readonly runs: LocalAgentStoreRuns;             // строки запусков  readonly runEvents: LocalAgentStoreRunEvents;   // журнал событий запусков только с добавлением записей}

Реализуйте интерфейс напрямую или создайте каждое подхранилище отдельно, а затем объедините их с помощью composeLocalAgentStore:

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

Подхранилища соответствуют стандартным таблицам SQLite: agents содержит по одной строке для каждого агента (с компактной ссылкой latestCheckpoint.rootBlobId), checkpoints — адресуемые по содержимому блобы диалогов, на которые указывают эти ссылки, runs — по одной строке для каждого запуска, а runEvents — потоковый журнал, доступный только для добавления. Подхранилища каталогов поддерживают разбиение на страницы с непрозрачными cursor / nextCursor; журнал событий запуска возобновляется с исключающими afterOffset / nextOffset. Точную структуру см. в экспортируемых LocalAgentStore, LocalAgentDocument, LocalAgentRunDocument и связанных типах.

Справочник по конфигурации

AgentOptions

СвойствоТипПо умолчаниюОписание
modelModelSelectionОбязательно до первого локального send(); для облачного используется определённое сервером значение по умолчаниюИспользуемая модель. См. ModelSelection.
apiKeystringпеременная окружения CURSOR_API_KEYПользовательский API-ключ или ключ сервисного аккаунта. Ключи администратора команды пока не поддерживаются.
namestringГенерируется автоматическиПонятное человеку имя агента, возвращаемое как name в Agent.list() / Agent.get().
localLocalAgentOptionsКонфигурация локального агента. См. LocalAgentOptions.
cloudCloudAgentOptionsКонфигурация облачного агента.
mcpServersRecord<string, McpServerConfig>Встроенные определения серверов MCP.
agentsRecord<string, AgentDefinition>Определения субагентов.
toolsToolName[]Набор инструментов по умолчаниюОграничение набора инструментов: модели доступны только перечисленные встроенные инструменты. [] означает отсутствие встроенных инструментов. Только для локальных агентов.
disallowedToolsToolName[]Удаляет инструменты из набора инструментов; всё остальное остаётся доступным. При совместном использовании с tools запрет имеет приоритет. Только для локальных агентов.
systemPromptstringВстроенный промпт CursorЗамена системного промпта для основного agent loop. Не может быть пустым. Только для локальных агентов и не сохраняется при Agent.resume().
agentIdstringГенерируется автоматическиПостоянный идентификатор агента. Передайте его, чтобы сохранить стабильный ID между вызовами.
idempotencyKeystringАвтоматически генерируется для облачного агентаНеобязательный ключ идемпотентности, сгенерированный клиентом.
mode"agent" | "plan""agent"Начальный режим диалога при первом запуске агента. См. Режим диалога.

LocalAgentOptions

Конфигурация локальных агентов, передаваемая как local в Agent.create(). Также экспортируется как отдельный тип для Partial<LocalAgentOptions>.

СвойствоТипПо умолчаниюОписание
cwdstringОсновной рабочий каталог для оболочки по умолчанию и определения области действия хранилища агента.
dirsstring[]Дополнительные папки рабочего пространства для конфигураций с несколькими корневыми каталогами. Объединяются с cwd (без дубликатов), чтобы правила, навыки и контекст рабочего пространства загружались из каждого пути.
settingSourcesSettingSource[]Загружаемые уровни настроек: "project", "user", "team", "mdm", "plugins" или "all".
sandboxOptions{ enabled: boolean }{ enabled: false }Конфигурация песочницы.
autoReviewbooleanfalseНаправлять локальные вызовы инструментов через Auto-review.
customToolsRecord<string, SDKCustomTool>Пользовательские инструменты, предоставляемые через сервер MCP custom-user-tools.
storeLocalAgentStoreХранилище SDK по умолчаниюХранилище для постоянного хранения данных локального агента.
enableAgentRetriesbooleantrueВключить автоматические повторы при сбоях транспорта и зависаниях запусков локального агента. Установите false, чтобы ошибки транспорта отображались при первой ошибке.

CloudAgentOptions

СвойствоТипПо умолчаниюОписание
env{ type: "cloud"; name?: string } | { type: "pool"; name?: string } | { type: "machine"; name?: string }{ type: "cloud" }Целевая инфраструктура выполнения. cloud использует ВМ, размещённые в Cursor; задайте name, чтобы использовать сохранённую инфраструктуру Cursor. pool и machine направляют задачи на self-hosted воркеры, которыми вы управляете. Для Agent без репозиториев с пустым рабочим пространством не указывайте repos и оставьте значение env по умолчанию. Именованные инфраструктуры Cursor и явное указание repos взаимоисключающие.
reposArray<{ url: string; startingRef?: string; prUrl?: string }>Репозитории для клонирования в ВМ. Передайте одну запись для Agent с одним репозиторием или до 20 записей — для Agent с несколькими репозиториями. Для self-hosted целей допускается только одна запись, если env.name не указывает на пул для любого репозитория. Не указывайте или передайте [] для Agent без репозитория. Взаимоисключается с именованным env.name для инфраструктуры Cursor. Передайте prUrl, чтобы прикрепить Agent к существующему PR.
workOnCurrentBranchbooleanfalseОтправлять коммиты в существующую ветку вместо новой.
autoCreatePRbooleanfalseОткрывать PR после завершения запуска.
openAsCursorGithubAppbooleantrue для ключей сервисных аккаунтов, false для ключей пользователейОткрывать PR от имени Cursor GitHub App, а не владельца API-ключа. Определённое значение возвращается при создании, получении и выводе списка.
skipReviewerRequestbooleanfalseНе запрашивать назначение вызвавшего пользователя ревьюером PR.
envVarsRecord<string, string>Переменные среды в рамках сессии для облачных агентов.
metadataRecord<string, string>Строковые теги, принадлежащие вызывающей стороне и сохраняемые в облачном агенте. См. Метаданные Agent.

AgentDefinition

СвойствоТипПо умолчаниюОписание
descriptionstringобязательноУказывает, когда использовать этот субагент. Отображается родительскому агенту, чтобы он знал, когда его запускать.
promptstringобязательноСистемный промпт для субагента.
modelModelSelection | "inherit""inherit"Переопределение модели. Передайте "inherit", чтобы использовать выбор родительского агента.
mcpServersArray<string | Record<string, McpServerConfig>>Принимается для совместимости на будущее. Строковые ссылки игнорируются, а встроенные конфигурации вызывают ConfigurationError; субагенты наследуют MCP‑серверы родительского агента.

ModelSelection

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

id — идентификатор модели (например, "composer-2.5" или "auto-smart"). params содержит параметры конкретной модели, такие как уровень усилий рассуждений или optimize_for Router'а. Используйте Cursor.models.list(), чтобы узнать допустимые идентификаторы, определения параметров и предустановленные варианты для вашего аккаунта. См. Cursor Router для описания механизма выбора Router.

McpServerConfig

type McpServerConfig =  // stdio  | {      type?: "stdio";      command: string;      args?: string[];      env?: Record<string, string>;      cwd?: string;       // только в локальном режиме; в облачном режиме это поле не поддерживается    }  // HTTP / SSE  | {      type?: "http" | "sse";      url: string;      headers?: Record<string, string>;   // передаются без изменений; здесь можно использовать Authorization      auth?: {        CLIENT_ID: string;        CLIENT_SECRET?: string;        scopes?: string[];      };    };

Для HTTP-серверов, работающих в облаке, headers и auth обрабатываются бэкендом Cursor. Конфиденциальные поля скрываются до того, как VM получит к ним доступ. Для stdio-серверов в облаке значения env передаются в VM (обращайтесь с ними как с любым секретом среды выполнения).

SDKUserMessage

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

Структурированная форма аргумента сообщения метода agent.send(). Используйте её для отправки изображений вместе с текстом.

SDKImage

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

Передайте либо удалённый url, либо данные в формате base64 в data вместе с mimeType.

SettingSource

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

Определяет, какие слои настроек на диске загружает локальный агент. Облачные агенты всегда загружают project / team / plugins и игнорируют это поле.

ЗначениеИсточник
"project".cursor/ в рабочем пространстве
"user"~/.cursor/
"team"Настройки команды, синхронизированные с дашбордом
"mdm"Корпоративные настройки под управлением MDM
"plugins"Настройки, предоставляемые плагинами
"all"Сокращённая запись для всех перечисленных выше

ListResult

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

Возвращается методами Agent.list() и Agent.listRuns(). nextCursor отсутствует, если больше нет страниц.

Ошибки

Все ошибки SDK наследуются от CursorSdkError (для обратной совместимости повторно экспортируемого как CursorAgentError). Используйте isRetryable для логики повторных попыток, а code / status / requestId — для диагностики.

class CursorSdkError extends Error {  readonly isRetryable: boolean;  readonly code?: string;       // постоянный код SDK / бэкенда  readonly status?: number;     // статус HTTP, если доступен  readonly cause?: unknown;     // исходная ошибка, обёрнутая в текущую  readonly endpoint?: string;  readonly requestId?: string;  readonly operation?: string;  // операция SDK, вызвавшая ошибку}
Класс ошибкиТипичное сообщениеВероятная причинаРекомендуемое исправление
AuthenticationError"Недопустимый API-ключ"CURSOR_API_KEY отсутствует или неверен, срок действия токена истёк либо администратор отключил ключ.Создайте новый ключ в разделе API Keys (пользователь) или Team settings (сервисный аккаунт). Убедитесь, что у ключа есть право доступа для этой операции.
RateLimitError"Превышен лимит запросов" или "Превышен лимит использования"Лимит всплеска запросов или месячный лимит использования.Повторите попытку с экспоненциально увеличивающейся задержкой (для временных случаев SDK сообщает isRetryable: true). Для месячного лимита увеличьте лимит использования плана.
ConfigurationError"Неверное имя модели", "API-ключ не поддерживается", "Файл не поддерживается"Недопустимый model.id, отсутствуют обязательные params, неподдерживаемый файл в вызове инструмента или политика администратора блокирует запрос.Вызовите Cursor.models.list(), чтобы проверить id и params. Убедитесь, что репозиторий и пути к файлам существуют.
AgentBusyError"Agent занят"Отправка follow-up, когда у того же облачного агента уже есть запуск в состоянии CREATING или RUNNING.Дождитесь завершения активного запуска, отмените его или перед повторной отправкой опросите Agent.listRuns().
IntegrationNotConnectedError"Интеграция [provider] не подключена"Создание облачного агента для репозитория, чей SCM provider не подключён к вашей команде Cursor.Откройте error.helpUrl, чтобы повторно подключить provider, затем повторите попытку.
NetworkError"Сервис недоступен", "Время ожидания истекло"Временная проблема на бэкенде, разделение сети или превышение дедлайна.Повторите попытку с увеличивающейся задержкой. Если нужно обратиться в поддержку, проверьте error.requestId.
UnsupportedRunOperationError"Операция "stream" не поддерживается в этой среде выполнения"Вызов метода Run, который не поддерживается в текущей среде выполнения (например, стриминг для повторно полученного локального запуска, который уже завершился).Сначала проверьте run.supports(operation) / run.unsupportedReason(operation).
AgentNotFoundError"Agent не найден"Запрошенный агент не существует или не виден в определённой локальной рабочей области.Проверьте ID агента, cwd и local.store.
UnknownAgentErrorСообщение, определённое серверомНеклассифицированная ошибка бэкенда или среды выполнения.Проверьте error.code и error.cause, чтобы узнать подробности.

IntegrationNotConnectedError

class IntegrationNotConnectedError extends ConfigurationError {  readonly provider: string;   // e.g. "github", "gitlab", "azure-devops"  readonly helpUrl: string;    // ссылка на дашборд для повторного подключения}

Стандартное сообщение об ошибке не содержит helpUrl, поэтому явно выведите его в журнал:

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 для agent_busy. Повторные попытки будут сразу завершаться ошибкой, пока активный запуск не завершится или вы не отмените его. Другие ответы 409, например agent_archived, вместо этого приводят к ConfigurationError.

Дождитесь завершения активного запуска, отмените его с помощью run.cancel() или опрашивайте Agent.listRuns() перед повторной отправкой:

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;}

Локальные агенты не возвращают agent_busy. Используйте send({ local: { force: true } }), чтобы принудительно завершить зависший локальный запуск перед началом нового.

UnsupportedRunOperationError

class UnsupportedRunOperationError extends ConfigurationError {  readonly operation: RunOperation;}

Возникает, когда операция Run недоступна в текущей среде выполнения. Перед вызовом проверьте это с помощью run.supports(operation) и run.unsupportedReason(operation).

Известные ограничения

  • Inline mcpServers не сохраняются при вызове Agent.resume(). При необходимости передайте их повторно.
  • Пользовательские инструменты (local.customTools), Auto-review (local.autoReview), пользовательские хранилища (local.store), ограничения набора инструментов (tools, disallowedTools) и systemPrompt доступны только для локальных агентов. Облачные агенты отклоняют local.customTools при вызове send() и сохраняют данные на сервере.
  • tools, disallowedTools и systemPrompt не сохраняются в агенте. Чтобы сохранить их, передайте их повторно в Agent.resume().
  • Скачивание артефактов не реализовано для локальных агентов (agent.listArtifacts() возвращает пустой список, а agent.downloadArtifact() вызывает исключение).
  • local.settingSources (и зависящие от него пути к файлам MCP / субагентов) не применяется к облачным агентам. Облачные агенты всегда загружают project / team / plugins.
  • Хуки поддерживаются только в файлах (.cursor/hooks.json). Программные обратные вызовы не поддерживаются.
  • SDK не обнаруживает автоматически учётные данные из локальной установки приложения Cursor. Явно задайте CURSOR_API_KEY (или передайте apiKey) либо создайте ключ с помощью Cursor.auth.login().
  • Для локального режима требуется Node.js 22.13 или новее, а также поддержка sandbox-helper на платформе. Хранилищу по умолчанию нужен node:sqlite; если он недоступен, Agent.create() вызывает ConfigurationError, пока вы не настроите JsonlLocalAgentStore или другое хранилище.