Cursor TypeScript SDK
Пакет @cursor/sdk позволяет вызывать Agent Cursor из собственного кода. Того же Agent, который работает в Cursor IDE, CLI и веб-приложении, теперь можно запускать из скриптов TypeScript. Для начала запустите навык /sdk в Cursor.
Полные примеры доступны в Cursor Cookbook: быстрый старт SDK, инструмент для прототипирования приложений, канбан-доска для облачных агентов и CLI для coding-agent. Это хорошие отправные точки для ботов CI с автоисправлением, воркеров для триажа багов, ревью кода, встроенных агентов в продуктах и оркестраторов.
Обзор
SDK предоставляет единый интерфейс для локальных и облачных сред выполнения. Вы пишете один и тот же код независимо от того, где работает агент.
| Среда выполнения | Что делает | Когда использовать |
|---|---|---|
| Локальная | Запускает цикл агента непосредственно в вашем процессе Node. Файлы берутся с диска. | Скрипты для разработки и проверки CI в рабочем дереве. |
| Облачная (размещаемая Cursor) | Запускает агента в изолированной ВМ с клонированным репозиторием. ВМ управляются Cursor. | Когда у вызывающей стороны нет репозитория, нужно запустить много агентов параллельно или выполнение должно продолжаться после отключения вызывающей стороны. |
«Локальная» указывает, где выполняются цикл агента и доступ к файловой системе, а не где работает модель. В обоих режимах весь инференс выполняется через размещённые 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-ключи администратора команды пока не поддерживаются.
- Пользовательский API-ключ в Cursor Dashboard → API Keys
- 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/sqlite | SqliteLocalAgentStore, как в @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().
Локальный агент по умолчанию выполняет вызовы инструментов (shell, edit, write и т. д.) без
запроса на одобрение; в headless-режиме нет запроса, требующего участия человека. Чтобы
контролировать вызовы инструментов, настройте хуки (например, beforeShellExecution или
preToolUse) или запустите с local.sandboxOptions.enabled: true.
Создание 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>.
Облачные агенты, запущенные через SDK, не отображаются в списке агентов по умолчанию. Чтобы просмотреть их в Cursor Web или окне Cursor, нажмите Фильтр > Источник > SDK.
Облачные агенты без репозитория
Облачные агенты могут работать на пустой ВМ без репозитория. Передайте 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, если этого требует выбранная модель.
Composer 2 больше не поддерживается. Запросы SDK, в которых по-прежнему передаются composer-2 или
composer-2-fast, перенаправляются на Composer 2.5 при аутентификации, поэтому существующие
скрипты продолжают работать. Если вы использовали вариант composer-2-fast, убедитесь,
что быстрый режим по-прежнему соответствует вашим ожиданиям.
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 |
|---|---|
| Cost | cost |
| Balance | balanced |
| Возможность | 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_for | Cursor 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 отсутствует или режим оптимизации отклонён:
- Вызовите
Cursor.models.list(). - Убедитесь, что
auto-smartприсутствует в результатах. - Убедитесь, что
optimize_forвключает нужное значение (cost,balancedилиintelligence). - Убедитесь, что Router включён для команды, связанной с API-ключом.
- Если вы состоите в нескольких командах, убедитесь, что ключ используется в контексте нужной команды.
- Проверьте политику доступа команды к моделям, если 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 | Токены, записанные в кэш промпта. |
totalTokens | inputTokens + 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); // после успешной отправки принимает значение из overriderun.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.
Параметры для каждой отправки
| Свойство | Тип | Описание |
|---|---|---|
model | ModelSelection | Переопределение модели для данной отправки. Если не указано, используется agent.model. Сохраняется: успешная отправка обновляет agent.model. |
mode | "agent" | "plan" | Переопределение режима диалога для данной отправки. Если не указано в последующих сообщениях, сохраняется текущий режим диалога. |
mcpServers | Record<string, McpServerConfig> | Определения серверов MCP, заданные inline. Полностью заменяют серверы, заданные при создании, для этого запуска. |
onStep | (args: { step }) => void | Promise<void> | Колбэк после каждого завершённого шага диалога (текста, размышления или пакета инструментов). |
onDelta | (args: { update }) => void | Promise<void> | Колбэк для каждого необработанного InteractionUpdate. |
idempotencyKey | string | Необязательный ключ идемпотентности, созданный клиентом для отправки. |
cloud.envVars | Record<string, string> | Только для облачных агентов. Переменные среды для каждого запуска, добавляемые для этого запуска и удаляемые после его завершения. Переопределяют cloud.envVars, привязанные к агенту, по имени только для этого запуска. |
local.force | boolean | Только для локальных агентов. По умолчанию false. Завершает зависший активный запуск перед отправкой этого сообщения. В Cloud сервер возвращает 409 agent_busy, поэтому эквивалент не нужен. |
local.customTools | Record<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 внедряется постепенно и включается для каждого аккаунта отдельно.
Пока он не включён для аккаунта вашего API-ключа, getUsage() для локального Agent
возвращает 403 feature_unavailable, и SDK выбрасывает исключение
UnknownAgentError с error.code === "feature_unavailable". На облачных
агентов это ограничение не распространяется. А пока получайте количество токенов по каждому запуску из
run.usage или result.usage (см. Использование токенов), а оплачиваемую
стоимость — на дашборде использования по
тегу SDK.
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); // безвозвратно; последующие запросы на чтение вернут 404SDKAgentInfo
Структура метаданных, возвращаемая методами 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, затем продакшен. |
openBrowser | true (по умолчанию) открывает системный браузер, если это, скорее всего, сработает; false никогда не открывает браузер; функция — пользовательский обработчик открытия. Не используется в SSH-сессиях или при заданной NO_OPEN_BROWSER. |
onLoginUrl | Вызывается с URL входа перед ожиданием, чтобы хост мог его отобразить. Если параметр не указан и браузер не открыт, URL записывается в stderr. |
signal | AbortSignal, отменяющий ожидание; в этом случае 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(). Также поддерживаются конфигурации в файлах и конфигурации, управляемые через дашборд.
Что загружается
Локальные агенты загружают серверы не более чем из пяти источников. При совпадении имён используется первый найденный:
mcpServersвagent.send(). Полностью заменяет серверы, заданные при создании, для этого запуска (без объединения).mcpServersвAgent.create(). Используется, если для отдельной отправки не задано переопределение.- Серверы плагинов, если
local.settingSourcesвключает"plugins". - Серверы проекта из
.cursor/mcp.json, еслиlocal.settingSourcesвключает"project". - Пользовательские серверы из
~/.cursor/mcp.json, еслиlocal.settingSourcesвключает"user".
Без local.settingSources загружаются только встроенные серверы. Если локальному MCP-серверу требуется вход через OAuth, SDK не сможет предложить войти в систему. Это работает, только если вы уже вошли в этот сервер через приложение Cursor: в таком случае SDK использует сохранённые данные для входа.
Облачные агенты загружают серверы из следующих источников:
mcpServersвagent.send(). Полностью заменяет серверы, заданные при создании, для этого запуска (без объединения).mcpServersвAgent.create(). Используется, если для отдельной отправки не задано переопределение.- Ваши пользовательские и командные 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 | Показывается модели, чтобы она знала, когда вызывать инструмент. По умолчанию — пустая строка. |
inputSchema | JSON Schema для аргументов. По умолчанию — открытый object, допускающий любые свойства. |
outputSchema | JSON 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/sqlite | SQLite на диске в корневом каталоге состояния рабочей области. |
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
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
model | ModelSelection | Обязательно до первого локального send(); для облачного используется определённое сервером значение по умолчанию | Используемая модель. См. ModelSelection. |
apiKey | string | переменная окружения CURSOR_API_KEY | Пользовательский API-ключ или ключ сервисного аккаунта. Ключи администратора команды пока не поддерживаются. |
name | string | Генерируется автоматически | Понятное человеку имя агента, возвращаемое как name в Agent.list() / Agent.get(). |
local | LocalAgentOptions | Конфигурация локального агента. См. LocalAgentOptions. | |
cloud | CloudAgentOptions | Конфигурация облачного агента. | |
mcpServers | Record<string, McpServerConfig> | Встроенные определения серверов MCP. | |
agents | Record<string, AgentDefinition> | Определения субагентов. | |
tools | ToolName[] | Набор инструментов по умолчанию | Ограничение набора инструментов: модели доступны только перечисленные встроенные инструменты. [] означает отсутствие встроенных инструментов. Только для локальных агентов. |
disallowedTools | ToolName[] | Удаляет инструменты из набора инструментов; всё остальное остаётся доступным. При совместном использовании с tools запрет имеет приоритет. Только для локальных агентов. | |
systemPrompt | string | Встроенный промпт Cursor | Замена системного промпта для основного agent loop. Не может быть пустым. Только для локальных агентов и не сохраняется при Agent.resume(). |
agentId | string | Генерируется автоматически | Постоянный идентификатор агента. Передайте его, чтобы сохранить стабильный ID между вызовами. |
idempotencyKey | string | Автоматически генерируется для облачного агента | Необязательный ключ идемпотентности, сгенерированный клиентом. |
mode | "agent" | "plan" | "agent" | Начальный режим диалога при первом запуске агента. См. Режим диалога. |
LocalAgentOptions
Конфигурация локальных агентов, передаваемая как local в Agent.create(). Также экспортируется как отдельный тип для Partial<LocalAgentOptions>.
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
cwd | string | Основной рабочий каталог для оболочки по умолчанию и определения области действия хранилища агента. | |
dirs | string[] | Дополнительные папки рабочего пространства для конфигураций с несколькими корневыми каталогами. Объединяются с cwd (без дубликатов), чтобы правила, навыки и контекст рабочего пространства загружались из каждого пути. | |
settingSources | SettingSource[] | Загружаемые уровни настроек: "project", "user", "team", "mdm", "plugins" или "all". | |
sandboxOptions | { enabled: boolean } | { enabled: false } | Конфигурация песочницы. |
autoReview | boolean | false | Направлять локальные вызовы инструментов через Auto-review. |
customTools | Record<string, SDKCustomTool> | Пользовательские инструменты, предоставляемые через сервер MCP custom-user-tools. | |
store | LocalAgentStore | Хранилище SDK по умолчанию | Хранилище для постоянного хранения данных локального агента. |
enableAgentRetries | boolean | true | Включить автоматические повторы при сбоях транспорта и зависаниях запусков локального агента. Установите 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 взаимоисключающие. |
repos | Array<{ url: string; startingRef?: string; prUrl?: string }> | Репозитории для клонирования в ВМ. Передайте одну запись для Agent с одним репозиторием или до 20 записей — для Agent с несколькими репозиториями. Для self-hosted целей допускается только одна запись, если env.name не указывает на пул для любого репозитория. Не указывайте или передайте [] для Agent без репозитория. Взаимоисключается с именованным env.name для инфраструктуры Cursor. Передайте prUrl, чтобы прикрепить Agent к существующему PR. | |
workOnCurrentBranch | boolean | false | Отправлять коммиты в существующую ветку вместо новой. |
autoCreatePR | boolean | false | Открывать PR после завершения запуска. |
openAsCursorGithubApp | boolean | true для ключей сервисных аккаунтов, false для ключей пользователей | Открывать PR от имени Cursor GitHub App, а не владельца API-ключа. Определённое значение возвращается при создании, получении и выводе списка. |
skipReviewerRequest | boolean | false | Не запрашивать назначение вызвавшего пользователя ревьюером PR. |
envVars | Record<string, string> | Переменные среды в рамках сессии для облачных агентов. | |
metadata | Record<string, string> | Строковые теги, принадлежащие вызывающей стороне и сохраняемые в облачном агенте. См. Метаданные Agent. |
AgentDefinition
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
description | string | обязательно | Указывает, когда использовать этот субагент. Отображается родительскому агенту, чтобы он знал, когда его запускать. |
prompt | string | обязательно | Системный промпт для субагента. |
model | ModelSelection | "inherit" | "inherit" | Переопределение модели. Передайте "inherit", чтобы использовать выбор родительского агента. |
mcpServers | Array<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, но со временем helpUrl может появиться и у других типов
ошибок. При перехвате ошибки записывайте в журнал error.helpUrl, если он есть, и показывайте его
пользователю.
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или другое хранилище.