# @cursor/february

TypeScript SDK for Cursor agents (private alpha — codenamed).

The primary API is the static `Agent` namespace. It creates durable agents, starts runs, streams normalized run events, waits for completion, resumes existing agents, and inspects persisted agent state.

## Quick Start

```typescript
import { Agent } from "@cursor/february/agent";

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

try {
  const run = await agent.send("Refactor the utils module", {
    onDelta: ({ update }) => {
      console.log("raw update", update.type);
    },
    onStep: ({ step }) => {
      console.log("completed step", step.type);
    },
  });

  for await (const event of run.stream()) {
    if (event.type === "assistant") {
      for (const block of event.message.content) {
        if (block.type === "text") {
          process.stdout.write(block.text);
        }
      }
    }
  }

  const result = await run.wait();
  console.log(result.status);
} finally {
  await agent[Symbol.asyncDispose]();
}
```

## Creating Agents

`Agent.create(options)` returns an `SDKAgent` immediately. Runtime initialization is deferred until the first `send`.

```typescript
const localAgent = Agent.create({
  apiKey: process.env.CURSOR_API_KEY!,
  model: { id: "composer-2" },
  local: { cwd: "/path/to/repo" },
});

const cloudAgent = Agent.create({
  apiKey: process.env.CURSOR_API_KEY!,
  model: { id: "composer-2" },
  cloud: {
    repos: [
      {
        url: "https://github.com/owner/repo",
        startingRef: "main",
      },
    ],
  },
});
```

### Agent Options

- `model`: Model selection for runs. Required for local agents. Optional for cloud — omit to use the server-resolved default. Structure is `{ id, params? }`; see `Cursor.models.list()` for valid selections.
- `apiKey`: Cursor API key. Required for cloud agents and real local execution.
- `local.cwd`: Working directory for local agents. May be a string or string array.
- `cloud.repos`: GitHub repository configuration for cloud agents.
- `agentId`: Optional durable agent ID. If omitted, the SDK generates one.
- `settingSources`: Ambient Cursor settings layers to load for local agents.
- `mcpServers`: Inline MCP server definitions available to local runs.
- `platform`: Advanced local persistence/workspace configuration.

## Sending Messages

Use `agent.send(message, options)` to create a follow-up run.

```typescript
const run = await agent.send(
  {
    text: "Explain this repository",
    images: [
      {
        data: base64Png,
        mimeType: "image/png",
      },
    ],
  },
  {
    model: { id: "composer-2" },
    force: false,
  }
);
```

### Send Options

- `model`: Per-run model override for local agents. Cloud follow-up model overrides are unsupported in v1.
- `force`: Expire an active persisted run before starting this message. Use this as an explicit recovery path after a crashed local process.
- `onDelta`: Callback for raw `InteractionUpdate` deltas as the executor emits them.
- `onStep`: Callback when a conversation step finishes after batching text, thinking, or tool updates.

`onDelta` and `onStep` are awaited before the update continues through the run pipeline, so callback backpressure is preserved.

## Runs

A `Run` represents one durable agent turn.

```typescript
const run = await agent.send("Update the README");

console.log(run.id);
console.log(run.agentId);
console.log(run.status);

const result = await run.wait();
```

### Run Operations

- `run.stream()`: Async generator of normalized `SDKMessage` events.
- `run.wait()`: Resolves to a terminal `RunResult`.
- `run.cancel()`: Cancels a supported running run.
- `run.conversation()`: Returns accumulated `ConversationTurn[]` for this run.
- `run.supports(operation)`: Checks whether `stream`, `wait`, `cancel`, or `conversation` is supported.
- `run.unsupportedReason(operation)`: Explains why an operation is unavailable.
- `run.onDidChangeStatus(listener)`: Subscribes to run status changes.

`run.cancel()` is supported for both local and cloud runs. For cloud runs it calls the server's cancel endpoint and reconciles local status from the authoritative server response.

## Streaming Events

`run.stream()` emits `SDKMessage` events. This is the stable public stream surface shared by local and cloud runs.

```typescript
for await (const event of run.stream()) {
  switch (event.type) {
    case "user":
      console.log(event.message.content.map(block => block.text).join(""));
      break;
    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(event.call_id, event.name, event.status);
      if (event.args) {
        console.log("args", event.args);
      }
      if (event.result) {
        console.log("result", event.result);
      }
      break;
    case "status":
      console.log(event.status);
      break;
    case "task":
      console.log(event.text);
      break;
  }
}
```

### SDKMessage Types

- `SDKUserMessageEvent`: User prompt content for the run.
- `SDKAssistantMessage`: Assistant text and tool-use content blocks.
- `SDKThinkingMessage`: Thinking text.
- `SDKToolUseMessage`: Tool call metadata with `call_id`, `name`, `status`, and optional `args`, `result`, and `truncated` payload fields.
- `SDKStatusMessage`: Run lifecycle status such as `RUNNING`, `FINISHED`, or `ERROR`.
- `SDKTaskMessage`: Task-level status or summary text.
- `SDKSystemMessage`: System/init metadata when available.

Tool payload fields are emitted when the runtime provides them. Cloud V1 stream payloads use the public stream shape from the API; local runs forward the normalized local tool call arguments and results from `InteractionUpdate.toolCall`.

## Conversation State

`run.conversation()` returns old-style accumulated `ConversationTurn[]` for the run.

```typescript
const run = await agent.send("Inspect package scripts");
await run.wait();

const turns = await run.conversation();
```

Live local runs can accumulate tool steps from raw executor deltas. Persisted replay reconstructs conversation from `SDKMessage` events, so replayed conversations can include tool-call steps when the persisted stream contains tool payload data.

## Raw Delta Callbacks

`onDelta` exposes the lower-level `InteractionUpdate` stream.

```typescript
const run = await agent.send("Run tests", {
  onDelta: ({ update }) => {
    switch (update.type) {
      case "text-delta":
        process.stdout.write(update.text);
        break;
      case "tool-call-started":
        console.log("tool started", update.toolCall.type);
        break;
      case "tool-call-completed":
        console.log("tool completed", update.toolCall);
        break;
    }
  },
});
```

The callback surface is useful for local UI integrations that need detailed tool payloads. The normalized `run.stream()` surface is the cross-runtime public stream.

## Resuming and Inspecting Agents

Durable agents can be resumed later by ID.

```typescript
const resumed = Agent.resume(agent.agentId, {
  apiKey: process.env.CURSOR_API_KEY!,
  model: { id: "composer-2" },
  local: { cwd: process.cwd() },
});

const run = await resumed.send("What changed last time?");
await run.wait();
```

You can also inspect persisted agent and run state:

```typescript
const agents = await Agent.list({ runtime: "local", cwd: process.cwd() });
const runs = await Agent.listRuns(agent.agentId, {
  runtime: "local",
  cwd: process.cwd(),
});
const run = await Agent.getRun(runs.items[0]!.id, {
  runtime: "local",
  cwd: process.cwd(),
});
const messages = await Agent.messages.list(agent.agentId, {
  runtime: "local",
  cwd: process.cwd(),
});
```

`Agent.messages.list(...)` reads persisted conversation/checkpoint messages. It is complementary to `run.conversation()`, which is scoped to one run.

## Managing Cloud Agents

Cloud agents support lifecycle operations beyond create/resume/list:

```typescript
const info = await Agent.get("bc-abc123", { apiKey });
await Agent.archive("bc-abc123", { apiKey });
await Agent.unarchive("bc-abc123", { apiKey });
await Agent.delete("bc-abc123", { apiKey });
```

`Agent.get` returns the same `SDKAgentInfo` shape as `Agent.list`. `archive`, `unarchive`, and `delete` return nothing; a missing agent throws.

`apiKey` falls back to `process.env.CURSOR_API_KEY` if omitted.

## Account and Platform

The `Cursor` namespace exposes account-level queries that aren't tied to a specific agent.

```typescript
import { Cursor } from "@cursor/february/agent";

const me = await Cursor.me({ apiKey });
const models = await Cursor.models.list({ apiKey });
const repos = await Cursor.repositories.list({ apiKey });
```

- `Cursor.me()` returns the authenticated user's `apiKeyName`, `userEmail`, and `createdAt`.
- `Cursor.models.list()` returns the model IDs available to the caller.
- `Cursor.repositories.list()` returns GitHub repositories the caller has connected to Cursor, for use as `cloud.repos` entries on `Agent.create`.

All three are cloud-only and require an API key (param or `CURSOR_API_KEY` env var).

## Local Settings and MCP

Store-backed local agents do not load ambient Cursor settings unless you opt in. Use `settingSources` to choose which settings layers can affect MCP servers, hooks, skills, rules, subagents, team policy, MDM hooks, and plugins.

```typescript
const agent = Agent.create({
  apiKey: process.env.CURSOR_API_KEY!,
  model: { id: "composer-2" },
  local: { cwd: process.cwd() },
  settingSources: ["project", "plugins"],
  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(),
    },
  },
});
```

`settingSources: ["all"]` loads every ambient source. Inline `mcpServers` are explicit API input, so they are available even when `settingSources` is omitted or `[]`. Inline MCP server definitions are not persisted for `Agent.resume(...)`; pass them again when resuming if the run needs those servers.

Call `agent.reload()` after local hook, MCP, or settings files change and you want the underlying local executor to pick them up.

## Artifacts

The `SDKAgent` interface includes artifact helper methods:

```typescript
const artifacts = await agent.listArtifacts();
```

Artifact support is still runtime-dependent. Local SDK agents currently return no artifacts and throw for `downloadArtifact`.

## Resource Management

Dispose agents when you are done with them.

```typescript
await agent[Symbol.asyncDispose]();
```

`agent.close()` starts best-effort disposal without awaiting it. Prefer `[Symbol.asyncDispose]()` in scripts and tests.

## Static API Reference

- `Agent.create(options): SDKAgent`
- `Agent.resume(agentId, options?): SDKAgent`
- `Agent.prompt(message, options?): Promise<RunResult>`
- `Agent.list(options?): Promise<ListResult<SDKAgentInfo>>`
- `Agent.listRuns(agentId, options?): Promise<ListResult<Run>>`
- `Agent.getRun(runId, options?): Promise<Run>`
- `Agent.messages.list(agentId, options?): Promise<AgentMessage[]>`

## Known Limitations

- Persisted conversation replay can only reconstruct information present in the public `SDKMessage` stream.
- Inline MCP server definitions are not persisted across `Agent.resume(...)`.
- Artifact download support is not implemented for local SDK agents yet.

