Setting up the Cursor TypeScript SDK: Agent.prompt, Agent.create, and Agent.resume Patterns
The Cursor TypeScript SDK (@cursor/sdk) provides three canonical patterns—Agent.prompt for one-shot scripts, Agent.create for multi-turn interactions, and Agent.resume for reconnecting to persisted agents—enabling developers to embed Cursor agents into everything from CI pipelines to background jobs.
The @cursor/sdk package in the cursor/plugins repository offers programmatic control over Cursor agents through these distinct execution models. Each pattern targets specific operational requirements, from ephemeral automation tasks to durable conversations that survive process restarts.
The Three Execution Patterns
Agent.prompt for One-Shot Tasks
Agent.prompt(...) creates a temporary agent, executes a single prompt, and automatically disposes of resources. This pattern is ideal for CI steps, GitHub Actions, or simple CLI commands where you need a result and immediate exit without managing lifecycle cleanup.
According to cursor-sdk/skills/cursor-sdk/SKILL.md (lines 55-66), this method accepts a prompt string and options, returning a RunResult directly:
import { Agent } from "@cursor/sdk";
(async () => {
const result = await Agent.prompt(
"Refactor src/utils.ts for readability",
{
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: { cwd: process.cwd() },
},
);
console.log("Status:", result.status);
console.log("Output:", result.result);
})();
Agent.create for Durable Conversations
Agent.create(...) returns a persistent Agent object that maintains conversation state across multiple turns. Unlike Agent.prompt, this pattern requires manual resource cleanup via await agent[Symbol.asyncDispose]() to prevent leaks.
The implementation in orchestrate/skills/orchestrate/scripts/cli/task.ts (around line 170) demonstrates this pattern in production use. The agent supports real-time streaming via run.stream() and final resolution via run.wait():
import { Agent } from "@cursor/sdk";
(async () => {
const agent = Agent.create({
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: { cwd: process.cwd() },
});
try {
// First turn with streaming
const run1 = await agent.send("Find the bug in src/auth.ts");
for await (const ev of run1.stream()) {
if (ev.type === "assistant") {
for (const blk of ev.message.content) {
if (blk.type === "text") process.stdout.write(blk.text);
}
}
}
const result1 = await run1.wait();
console.log("First turn status:", result1.status);
// Follow-up turn preserves conversation context
const run2 = await agent.send("Now write a regression test for the bug");
await run2.wait();
} finally {
// Prevent resource leaks
await agent[Symbol.asyncDispose]();
}
})();
Agent.resume for Process Resumption
Agent.resume(...) reconnects to a previously persisted agent using its ID. This pattern supports cron jobs, webhooks, or CLI restarts where the original process no longer exists. Note that MCP servers are not persisted and must be passed again in the options.
As documented in cursor-sdk/skills/cursor-sdk/SKILL.md (lines 102-112):
import { Agent } from "@cursor/sdk";
(async () => {
// previousAgentId is persisted somewhere (DB, file, env, etc.)
const previousAgentId = "agent-1234abcd";
const agent = Agent.resume(previousAgentId, {
apiKey: process.env.CURSOR_API_KEY!,
model: { id: "composer-2" },
local: { cwd: process.cwd() },
});
const run = await agent.send("Also update the changelog for the latest release");
const final = await run.wait();
console.log("Resume run status:", final.status);
})();
The file orchestrate/skills/orchestrate/scripts/core/agent-manager.ts (lines 89-95) demonstrates the lazy import pattern used when loading the SDK for these operations.
Runtime Configuration and Architecture
All three patterns share a common options shape but differ in runtime behavior. The SDK examines the supplied configuration to determine whether to execute locally (on the caller's machine) or in the cloud (Cursor-hosted VM):
local: { cwd: ... }: Pins execution to the current working directorycloud: { repos: [...] }: Forces cloud execution with specific repository access- Omitting both: Defaults to local execution silently
See cursor-sdk/skills/cursor-sdk/references/runtime-choice.md for the complete decision matrix.
Run Lifecycle and Streaming Control
A Run represents a single prompt execution within an agent session. Each run provides:
run.stream(): Yields events (assistant,tool_call, etc.) for live output processingrun.wait(): Resolves to the finalRunResultcontaining status, output, and errorsrun.supports(op): Guards optional operations likerun.cancel()orrun.conversation(), which may be unavailable on certain runtimes (e.g., paused cloud runs)
Always call wait() after streaming to release internal watchers and determine final completion status.
Error Handling and Resource Management
The SDK distinguishes between two error domains:
- Startup failures (
CursorAgentError): Thrown before any run starts (e.g., missing API key, invalid configuration). These are catchable at theAgent.createorAgent.promptlevel. - Runtime failures: Indicated by
result.status === "error"afterrun.wait()completes—the agent started but encountered issues like tool failures or model errors.
As shown in cursor-sdk/skills/cursor-sdk/SKILL.md (lines 124-142), handle both domains separately:
import { Agent, CursorAgentError } from "@cursor/sdk";
(async () => {
let agent;
try {
agent = Agent.create({ apiKey: process.env.CURSOR_API_KEY! });
const run = await agent.send("Summarize the repo");
const res = await run.wait();
if (res.status === "error") {
console.error("Run failed:", res.error?.message);
// Inspect res.transcript, tool outputs, etc.
} else {
console.log("Summary:", res.result);
}
} catch (err) {
if (err instanceof CursorAgentError) {
console.error("Startup error:", err.message, "Retryable:", err.isRetryable);
} else {
throw err;
}
} finally {
if (agent) await agent[Symbol.asyncDispose]();
}
})();
Common Pitfalls and Best Practices
Based on the "Top Five Traps" documented in cursor-sdk/skills/cursor-sdk/SKILL.md and references/error-handling.md:
- Always specify runtime config: Explicitly pass
localorcloudto avoid silent local fallbacks - Never forget disposal: Wrap
Agent.createandAgent.resumeintry ... finallyblocks, or useawait usingsyntax if your TypeScript target supports it - Distinguish error types: Catch
CursorAgentErrorfor startup issues; inspectresult.statusfor runtime problems - Stream with wait: Always call
run.wait()after streaming to release internal resources - Check capability support: Use
run.supports(op)before callingrun.cancel()or similar optional methods that may be unavailable on paused cloud runs
Summary
Agent.prompthandles one-shot tasks with automatic cleanup—perfect for CI/CD pipelines and simple scriptsAgent.createenables stateful, multi-turn conversations but requires manual disposal viaSymbol.asyncDisposeAgent.resumereconnects to persisted agents by ID, supporting durable workflows across process restarts- Runtime choice between local and cloud execution depends on the
localorcloudoptions passed to any pattern - Error handling requires catching
CursorAgentErrorfor startup failures and checkingresult.statusfor runtime issues - Resource management demands explicit disposal for created/resumed agents to prevent file handle and socket leaks
Frequently Asked Questions
What is the difference between Agent.prompt and Agent.create?
Agent.prompt creates a temporary agent instance that automatically disposes after returning the result, making it ideal for one-shot tasks. Agent.create returns a persistent agent that maintains conversation context across multiple send() calls but requires manual cleanup via await agent[Symbol.asyncDispose](). Use Agent.prompt for scripts and Agent.create for interactive or multi-step workflows.
How do I prevent resource leaks when using the Cursor SDK?
Always wrap Agent.create or Agent.resume calls in a try ... finally block to ensure await agent[Symbol.asyncDispose]() runs. Alternatively, use the await using syntax if your TypeScript configuration supports it. Forgetting disposal leaks file handles, child processes, and network sockets, as noted in cursor-sdk/skills/cursor-sdk/SKILL.md.
Can I resume an agent after my process restarts?
Yes. Persist the agentId returned when creating an agent, then use Agent.resume(agentId, options) to reconnect. Note that MCP servers and runtime configuration must be passed again during resume, as they are not persisted with the agent state. This pattern supports cron jobs, webhooks, and CLI tools that need to continue conversations across restarts.
How do I handle errors correctly in the Cursor TypeScript SDK?
Distinguish between startup errors (CursorAgentError) thrown during agent initialization, and runtime errors indicated by result.status === "error" after run.wait() returns. Catch CursorAgentError for configuration issues like missing API keys. For runtime errors, inspect the result.error and result.transcript properties to diagnose tool failures or model issues.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →