---
title: Cursor Provider
---

# Cursor Provider

## What it is

Omniscio can spawn Claude-Code-style sessions backed by the **Cursor CLI** (`cursor-agent`, powered by Cursor's **Composer** model) as an alternative provider, alongside `claude` (the default), `codex`, `gemini`, `antigravity`, the Anthropic-compatible `deepseek` / `kimi`, `glm`, `minimax`, `hermes`, `pi`, `opencode`, and `openclaw`. Cursor (by Anysphere) is a local CLI coding agent with its own model and its own API key — so a Cursor session is "a different agent and a different model," not Claude in a different harness.

### What the user sees

A Cursor session looks almost identical in the sidebar and main pane to a Claude session — same status dots, same streaming bubbles, same Ctrl+Enter to send. The differences:

- **Cursor icon** (indigo, no text label) in the session header next to the title — like every non-default provider. Codex, Gemini, Anti-Gravity, DeepSeek, Kimi, GLM, MiniMax, Hermes, Pi, and OpenCode all show their own icon too; only Claude (the default) and the internal-only engines show none.
- **Token-by-token streaming.** Omniscio passes `--stream-partial-output` so `cursor-agent --output-format stream-json` streams per-token text deltas (like every other engine); the reply appears incrementally, with one final push replacing the bubble with the full text when the turn completes. Without the flag cursor buffered each assistant message and emitted it whole — no live streaming, and a long no-tool turn (nothing emitted until the whole answer landed) could trip Omniscio's 120s no-first-output watchdog and be **falsely killed** mid-work. Cursor also re-emits a full-text "consolidation" copy of each segment at its end; the translator dedups those so the streamed answer isn't doubled (see contract streaming-is-incremental-with-snapshots-deduped).
- **Tool-use auto-approval is ON.** Cursor runs its tools (file writes, shell commands) without per-call prompts (Omniscio launches it with `--force --trust` — `--force` auto-approves commands, `--trust` clears Cursor's workspace-trust gate so a headless session can run at all). The "Allow Cursor sessions" toggle gates whether you can spawn it at all; once spawned it does not ask before acting.
- **Estimated per-session cost (≈).** Cursor's CLI reports token counts but no **dollar** figure (it bills by subscription/credits), so Omniscio captures the tokens and shows an **≈ estimated** cost, priced by the model you picked. For Cursor's **Auto** router (and its own `composer` model) the underlying model isn't known, so those turns show tokens with **no** dollar — never a made-up number. The estimate shows in **Stats → Spend → by engine**; your authoritative spend still lives in Cursor's own dashboard.

### No virtual project

Cursor has **no dedicated `__cursor__` sidebar entry** (unlike `__codex__` / `__gemini__` / `__antigravity__`), and therefore no sentinel bypass of the "Allow Cursor sessions" toggle — the toggle always applies. You spawn Cursor inside any real project. (Cursor IS a per-project default and IS switch-to-able, unlike OpenCode — those are the surfaces that make it "first-class.")

### Comparison with other providers

| Concern             | Cursor                                | OpenCode                                | Codex                                 |
| ------------------- | ------------------------------------- | --------------------------------------- | ------------------------------------- |
| Auth                | Cursor API key (required)             | Anthropic API key (`ANTHROPIC_API_KEY`) | `codex login` (ChatGPT) or OpenAI key |
| Underlying model    | Cursor Composer                       | Claude (Sonnet) via your key            | OpenAI Codex models                   |
| Process model       | Per-turn one-shot (`cursor-agent -p`) | Persistent shared server (`opencode serve`) | Long-lived JSON-RPC app-server        |
| Multi-turn resume   | Native `--resume <id>`                | Session id, multiplexed by the shared server | Persistent process                    |
| Cost emitted        | **≈ estimated** (token-priced)        | Yes — authoritative, to `api_cost_log`  | Yes                                   |
| Readiness gates     | 3 (toggle + binary + key)             | 3 (toggle + binary + key)               | 2 (toggle + binary; key optional)     |
| Per-project default | **Yes**                               | No (`pickable: false`, excluded from radios) | Yes                                   |
| Virtual project     | No                                    | No                                      | Yes (`__codex__`)                     |
| Session-header icon | **Indigo Cursor**                     | OpenCode (orange)                       | Codex (monochrome)                    |

## Where to find it

Cursor is off by default, so the first stop is the accounts area of Settings: turn on the option
that reveals the alternative AI providers and the Cursor panel appears, holding the allow-sessions
switch, the install status and the key field. From there a Cursor session starts like any other —
as a project's default provider, from the new-session split button, or from the provider switcher
on a fresh session.

## How it behaves

### How to enable

> **Master toggle required first.** Cursor is an alternative provider — by default Omniscio ships as a Claude-only product, and the entire Cursor setup section is hidden in Settings → Accounts. Flip **Settings → Accounts → Show alternative AI providers** to ON (off by default) and the **Cursor (CLI provider)** panel appears. With the master off, the per-launch provider switcher hides Cursor — it effectively does not exist in the UI even if every gate below is configured. Your "Allow Cursor sessions" toggle and saved key are preserved across master-toggle flips.

Three readiness gates must pass before Cursor is offered (`getProviderSpawnReadiness`, in order) — the toggle, the binary, and a Cursor **API key**:

> **The toggle leads and gates the panel UI (I9).** The Cursor panel shows ONLY the **Allow Cursor sessions** toggle until you turn it on; the CLI-install status card and the API-key field appear once it's enabled — so a key can't be pasted into a switched-off feature (the exact trap behind "I added a key but Cursor isn't available"). When off, a one-line hint stands in and still carries the Settings-search anchors for the key/binary so deep-links land; an already-saved key is preserved across toggling.

1. **Allow Cursor sessions** toggle — off by default. Same security stance as the other auto-approving providers: enable only because you intend to use it. Lives in the Cursor panel as **Allow Cursor sessions in any project**, and it's the first thing you see (revealing the steps below).
2. **Cursor CLI binary** — the `cursor-agent` binary must be on your PATH. Omniscio **detects** it on PATH but does not install it. Install it from Cursor's official instructions — macOS/Linux `curl https://cursor.com/install -fsS | bash`; **native Windows** `irm 'https://cursor.com/install?win32=true' | iex` (Cursor added a native Windows build in 2026 — the older "Windows needs WSL" guidance is obsolete). The Windows build lands in `%LOCALAPPDATA%\cursor-agent\` as a PowerShell→Node launcher that is slow to start; Omniscio's detector waits up to 30s for it and falls back to "present on disk = installed," so a real-but-slow Cursor is no longer misreported as missing. When found, the panel shows **Cursor CLI: Installed (version X)**. When **not found**, it shows an **Install Cursor CLI** button — which opens Cursor's official instructions at `https://cursor.com/docs/cli/installation` (note `cursor.com/install` itself is the raw install script, not a readable page) — plus a **Recheck** button that re-probes the binary in place so you need not close and reopen Settings. If Recheck still says "not found" right after you installed it, restart Omniscio so the running process picks up your updated PATH.
3. **Cursor API key (required).** Paste a key from your **Cursor dashboard** (Cursor's own key, **not** an Anthropic key — the panel's **Get your Cursor API key** button opens the dashboard) into the Cursor panel — stored encrypted (`safeStorage` + `enc:` prefix), trimmed on save, and injected as `CURSOR_API_KEY` on each spawn (never on argv). A key is **required**: `cursor-agent login` (the browser sign-in) does NOT authenticate the _headless_ way Omniscio runs Cursor (`cursor-agent -p --output-format stream-json`) — it works once, then fails "Authentication required" while `cursor-agent status` still shows logged-in; per Cursor's own docs, headless/automation auth IS the API key. With no key the picker shows **Add key** and won't spawn (a keyless turn would die silently with "no result event"). Omniscio strips any stray ambient `CURSOR_API_KEY` so it can't silently bill an account. See [provider-registry-contract.md](../../.claude/memory/contracts/provider-registry-contract.md) (`readiness-is-a-registry-driven-loop`/`opt-in-spawn-toggle-is-registry-data`) — Cursor is deliberately carved out of [cli-login-auth-contract.md](../../.claude/memory/contracts/cli-login-auth-contract.md).

Once the toggle + binary are green and a Cursor API key is saved, you launch a Cursor session three ways:

- **Per-project default** — the Edit-Project "Default provider" radios and the "+ New Session" split-button include Cursor (it's in `PROVIDER_ORDER`). New sessions in that project spawn Cursor automatically.
- **Per-launch override** — on a fresh (zero-message) session, the **Change-provider button** in the new session's main panel (the launch-config pickers on a fresh session) lets you pick Cursor before sending the first message. Non-ready providers appear disabled with a "Set up… / Install…" suffix that deep-links into the relevant Settings panel.
- **Programmatically** — anything that creates a session with `provider: 'cursor'` (recipes, the CLI control server, agent-driven sessions). The same readiness gates apply on the backend.

### Choosing the model

A fresh Cursor session shows a **Model** picker in the launch-config pickers, the same as Claude / Codex / Gemini. It offers a **curated 14-model list** — `auto`, Cursor's own Composer 2.5, then the latest of each headline family `cursor-agent` proxies (Claude Fable 5.1, Opus 5, Sonnet 5, Gemini 3.8 Flash, Gemini 3.1 Pro, Meta Muse Spark 1.3, GPT‑5.6 Sol / Terra / Luna, Grok 4.7, Grok 4.6 and Grok 4.5). Pick one and Omniscio spawns `cursor-agent --model <id>`; pick **Use default** (the null option) and the flag is omitted so `cursor-agent` uses its own default (`composer-2.5-fast`). The list is registry-driven (`MODELS_BY_PROVIDER.cursor` in `provider-models.ts`), and its ids come from Cursor's own per-model docs, which publish the settable id in an explicit **Model ID** field at `cursor.com/docs/models/<model>` — the `cursor-agent --list-models` route that used to verify this list now requires an account credential, so the docs are the authority. Cursor's CLI reports roughly 140 ids in total, so the curated set keeps the latest of every family; add more there as needed. There is **no separate Thinking/effort chip** — the model picker _is_ the effort picker, because Cursor bakes the tier into the id (a `-fast` variant exists per model and is deliberately not offered here, since it bills at double the base rate). A Cursor session never inherits your Claude default model. See [start-a-new-session.md](start-a-new-session.md) and [provider-registry-contract.md](../../.claude/memory/contracts/provider-registry-contract.md).

### Error states and fixes

Three gap codes (the API key IS a readiness gate):

| Gap              | What it means                              | Click-to-fix lands you at…                         |
| ---------------- | ------------------------------------------ | -------------------------------------------------- |
| `toggle-off`     | "Allow Cursor sessions" is OFF in Settings | Settings → Accounts → Allow Cursor sessions toggle |
| `binary-missing` | `cursor-agent` CLI not found on PATH       | Settings → Accounts → Cursor CLI binary panel      |
| `key-missing`    | No Cursor API key saved                    | Settings → Accounts → Cursor API key field         |

There is no virtual-project escape hatch, so all three gaps apply to every Cursor spawn.

**Turn-failure messages are cause-specific** (each runner throw is a typed `ProviderTurnError` carrying its own humanized line; the shared core + inline one-shot catch surface it). This matters because the failure modes are genuinely different:

| Failure                                                                                                                                                                                                          | What the user sees                                                                                                                                                                                                                                            |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Cursor team policy refuses headless run-everything** (a RARER Cursor account/org setting disables "Run Everything" or headless CLI use — distinct from the common hook jam, which hook isolation now prevents) | One honest **Omniscio notice**: writes/shell were denied because Cursor fell back to approval-required (allowlist) mode; re-enable "Run Everything" / headless CLI in your Cursor account/team settings — Omniscio can't override a server-side Cursor policy |
| Key absent at turn time (e.g. a CLI/deep-link spawn that skips the picker)                                                                                                                                       | "Cursor needs an API key… add one in Settings → Accounts → Cursor" — the **one** case where that advice is correct                                                                                                                                            |
| cursor-agent exits cleanly after abandoning `stream-json` and writing only plain text                                                                                                                            | Omniscio salvages the reply and shows it as the answer                                                                                                                                                                                                        |
| cursor-agent exits cleanly with **no salvageable answer and no `result` event** (for example partial/mixed NDJSON with no terminal result, or empty stdout)                                                      | "Cursor's CLI ran but returned no response… usually a temporary Cursor auth or quota issue — try again"                                                                                                                                                       |
| Spawn fail / binary missing                                                                                                                                                                                      | its own honest line                                                                                                                                                                                                                                           |
| Any other/unexpected throw                                                                                                                                                                                       | a generic "Cursor couldn't complete this turn — try again" fallback                                                                                                                                                                                           |

The generic fallback **no longer asserts a missing key** — the old single message blamed every failure (including a clean exit with a key present) on a missing key, sending users to re-add a key they already had. The raw cause (now including a bounded **stdout** tail on the no-result path, previously discarded) goes to the log only. A remaining "no result event" now means Omniscio saw neither a terminal `result` nor the narrow pure-plain-text fallback, so the turn is still treated as a real Cursor-side failure. See [cli-login-auth-contract.md](../../.claude/memory/contracts/cli-login-auth-contract.md) — `gemini-injects-key-only-when-set`.

**Team-policy block detection (a separate, RARER account-side case).** NOTE: the COMMON "Cursor can't write" cause — cursor jamming on the user's Claude hooks — is now PREVENTED by **hook isolation** (see _Claude-hook isolation_ above), NOT this detector; the agent's old self-diagnosis of "a PowerShell hook" was in fact _correct_ (a hook WAS jamming it), now fixed at the source. Separately and more rarely, Cursor accounts/orgs can disable "Run Everything" (or headless CLI use) as a server-side policy. When that happens, cursor-agent **refuses Omniscio's `--force`** and falls back to **approval-required (allowlist) mode** — the turn still "completes" but every file write / shell / git call is denied. Omniscio now scans cursor-agent's own policy markers (`cursor-policy-detect.ts`, a pure scanner: _"…disabled the 'Run Everything' option"_, _"…disabled headless Cursor CLI usage"_, _"Falling back to allowlist mode"_, _"Blocked by team policy"_) on **stderr only** (the markers are cursor's `Error:`/`warn.` diagnostics — stdout is the stream-json channel carrying the agent's _narration_, so scanning it would false-positive a notice onto a healthy turn whenever the agent merely _talks_ about allowlists), latched **during streaming** (cursor prints the marker early, and the bounded stderr tail would rotate it out on a long turn), and surfaces **one honest notice** (a text line appended to the turn — no separate state) naming the real cause and the one fix only the user can do: re-enable the setting in their **Cursor account/team settings**. Omniscio **cannot** override a server-side Cursor policy (a local `~/.cursor/cli-config.json` `approvalMode` change hits the same gate). This is distinct from the hook jam, which hook isolation now prevents. The notice never leaks raw cursor stderr. See [provider-registry-contract.md](../../.claude/memory/contracts/provider-registry-contract.md) a-team-policy-block-is-surfaced-never-silent + [cursor-claude-hook-isolation-contract.md](../../.claude/memory/contracts/cursor-claude-hook-isolation-contract.md).

How a turn is actually run — the harness under the hood and the validated stream format — continues on [Cursor Provider (part 2)](cursor-provider-part-2.md).
## For agents

### Files

- [src/main/services/engines/cursor-session-manager.ts](../../src/main/services/engines/cursor-session-manager.ts) — multi-turn lifecycle, estimated-cost tracking, interrupt orchestration (singleton `cursorSessionManager`)
- [src/main/process/cursor-turn-runner.ts](../../src/main/process/cursor-turn-runner.ts) — per-turn one-shot spawn; AbortSignal → `forceKillChild`; result/session-id capture
- [src/main/process/cursor-stream-translator.ts](../../src/main/process/cursor-stream-translator.ts) — pure stream-json NDJSON → `CursorEvent` (text / tool / result); **first place to look on output drift, and the place to reconcile against a real capture**
- [src/main/services/engines/cursor-binary-resolver.ts](../../src/main/services/engines/cursor-binary-resolver.ts) — locate `cursor-agent` on PATH (cached)
- [src/main/services/engines/cursor-credential-store.ts](../../src/main/services/engines/cursor-credential-store.ts) — encrypted `cursorApiKey` get/set/has/clear
- [src/main/services/provider-setup/provider-readiness.ts](../../src/main/services/provider-setup/provider-readiness.ts) — 3-gate readiness (toggle + binary + key); single source of truth for ChangeProviderButton + spawn guard
- [src/main/ipc/openai-handlers.ts](../../src/main/ipc/openai-handlers.ts) — `CURSOR_GET_STATUS` binary-status handler + `PROVIDER_LIST_AVAILABLE` entry
- [src/main/db/migrations/20260530173929-allow-cursor-provider.ts](../../src/main/db/migrations/20260530173929-allow-cursor-provider.ts) — ledger migration (provider trigger accepts `'cursor'`)
- [src/main/services/session/session-service.ts](../../src/main/services/session/session-service.ts) + [src/main/ipc/session-handlers.ts](../../src/main/ipc/session-handlers.ts) — routing to `cursorSessionManager`; [src/main/services/session/session-lifecycle-service.ts](../../src/main/services/session/session-lifecycle-service.ts) + [src/main/index.ts](../../src/main/index.ts) — pause/archive/shutdown teardown
- [src/shared/types.ts](../../src/shared/types.ts) (`ProviderId`, `cursorApiKey`, `allowCursorSessionSpawn`), [src/shared/provider-capabilities.ts](../../src/shared/provider-capabilities.ts), [src/renderer/src/lib/visible-providers.ts](../../src/renderer/src/lib/visible-providers.ts) — provider enumeration / visibility
- [src/renderer/src/components/ui/CursorIcon.tsx](../../src/renderer/src/components/ui/CursorIcon.tsx), [src/renderer/src/features/sessions/ProviderBadge.tsx](../../src/renderer/src/features/sessions/ProviderBadge.tsx) (indigo Cursor icon), [src/renderer/src/features/sessions/ChangeProviderButton.tsx](../../src/renderer/src/features/sessions/ChangeProviderButton.tsx), [src/renderer/src/features/sessions/ProviderSplitButton.tsx](../../src/renderer/src/features/sessions/ProviderSplitButton.tsx) — icon + per-launch switcher + project-default order
- [src/renderer/src/features/settings/sections/accounts/AccountSettings.tsx](../../src/renderer/src/features/settings/sections/accounts/AccountSettings.tsx) — the Cursor (CLI provider) Settings section: allow-toggle, API-key field, and the binary-status card (with **Install** + **Recheck** buttons in the not-found state)
- [.claude/memory/contracts/provider-registry-engines-more-cursor-shot-cli-1-contract.md](../../.claude/memory/contracts/provider-registry-engines-more-cursor-shot-cli-1-contract.md) — the Cursor contract: `a-switch-surface-is-registry-derived` … `a-team-policy-block-is-surfaced-never-silent`, including `the-launch-clears-the-workspace-trust-gate`, `the-prompt-is-delivered-on-stdin-never-in-an-argument` and `cost-is-estimated-from-tokens-never-fabricated`.
- [.claude/memory/contracts/provider-registry-engines-more-cursor-shot-cli-2-contract.md](../../.claude/memory/contracts/provider-registry-engines-more-cursor-shot-cli-2-contract.md) — the stream contract: `the-translator-never-throws-on-malformed-input`, `the-stream-is-flushed-at-close`, `streaming-is-incremental-with-snapshots-deduped`.

## Related

The [AI providers](ai-providers.md) page is the cross-provider overview these engines all sit in.
The closest neighbours are picked apart below: [OpenCode](opencode-provider.md) shares the
streamed-JSON shape, [Gemini](gemini-provider.md) is the other token-by-token project default, and
[Codex](codex-provider.md) is the one with a virtual project of its own.

- [opencode-provider.md](opencode-provider.md) — similar NDJSON/translator shape, but a `persistent-external` engine (the shared `opencode serve` server), not per-turn one-shot; no project default (`pickable: false`), Anthropic-keyed, and emits real cost
- [gemini-provider.md](gemini-provider.md) — also token-by-token NDJSON + `--resume`, a first-class project-default provider, but Google-billed (no cost emitted)
- [codex-provider.md](codex-provider.md) — first-class with a virtual project, but a long-lived JSON-RPC child (different process model)
- [ai-providers.md](ai-providers.md) — the cross-provider overview