Provider Request: Cursor's agent CLI (agent / cursor-agent) as an LLM provider
Use Case
I run a self-hosted Hindsight and want it to do its internal work — retain-time extraction,
consolidation and reflect — against an existing Cursor subscription, the same way
github-copilot, claude-code and openai-codex already let an existing subscription serve as
the backend.
The appeal is the same as those three: no new per-token billing relationship, and one vendor
already in use rather than another to procure. Cursor ships a documented headless mode
(cursor-agent -p --output-format json), so it is at least mechanically drivable from a server.
Worth stating explicitly to avoid confusion with existing work: this is not about the existing
Cursor integration (hindsight-integrations/cursor, hindsight-integrations/cursor-cli), which
makes Cursor a client that calls Hindsight. This is the opposite direction — Cursor as a model
backend that Hindsight calls.
Problem Statement
There is no way to configure this today, and no supported workaround.
1. cursor is not a recognised provider. create_llm_provider() in
engine/llm_wrapper.py dispatches on a hard-coded if/elif chain over the lowercased provider
name and falls through to a raise. Calling it with provider="cursor" inside the container gives:
ValueError: Unknown provider: cursor2. The OpenAI-compatible provider does not help here — I want to flag this early, since it's
the natural first suggestion, and it's the workaround that was offered on #1449 for GitHub Models.
Cursor has no chat-completions endpoint to point HINDSIGHT_API_LLM_BASE_URL at, and Cursor states
this themselves.
From https://cursor.com/docs/api:
"The Cloud Agents API and SDKs run Cursor agent workflows (workspace context, tools, commands, and edits). They are not a standalone model-inference or chat-completions API."
From https://cursor.com/docs/sdk/typescript:
"The Cursor SDK is an agent SDK, not a standalone model-inference or chat-completions API. Router picks models for Cursor agent runs that can reason over a workspace, call tools, run commands, and edit files. Cursor does not currently document a raw Router endpoint for arbitrary model calls."
The public surface is agent-shaped — the Cloud Agents API on api.cursor.com creates and manages
agent runs — not inference-shaped.
3. LLM providers are not an extension point. The image does expose class-path extension slots
via env vars — HINDSIGHT_API_TENANT_EXTENSION, HINDSIGHT_API_HTTP_EXTENSION,
HINDSIGHT_API_MCP_EXTENSION, HINDSIGHT_API_OPERATION_VALIDATOR_EXTENSION,
HINDSIGHT_API_FILE_STORAGE_EXTENSION, HINDSIGHT_API_VECTOR_EXTENSION,
HINDSIGHT_API_TEXT_SEARCH_EXTENSION and HINDSIGHT_API_MEMORIES_EXTENSION are all referenced in
the package — but none of them is an LLM provider slot, and llm_wrapper.py contains no
importlib, import_module, pkgutil or entry_points usage at all. So the only route open to a
user today is patching the image and maintaining a fork across releases.
How This Feature Would Help
It would let a Cursor subscriber point Hindsight at capacity they already pay for, without adding
a per-token vendor — which is precisely the gap github-copilot, claude-code and openai-codex
already close for their respective subscriptions. For anyone whose per-token budget is ending but
whose editor subscription continues, that is the difference between keeping Hindsight running and
switching it off.
Proposed Solution
A CursorLLM implementing LLMInterface (which has exactly four abstract methods:
verify_connection(), call(), call_with_tools(), cleanup()).
There is already good precedent for driving a subscription CLI, and github_copilot_llm.py looks
like the closest template — it runs mode="copilot-cli" through CopilotClient from the copilot
package, which drives the CLI as a child process (subprocess.Popen in copilot/client.py, with
an FFI host offering a Popen-shaped alternative over a native library).
Notably it still obtains structured output, by constraining the model to a single synthetic tool:
it sets available_tools = [f"custom:{_STRUCTURED_TOOL_NAME}"] where
_STRUCTURED_TOOL_NAME = "structured_response", instructs the model that it "MUST call the
'structured_response' tool exactly once. Do not answer with prose.", and then reads the answer out
of invocation.tool_calls. codex_llm.py uses the same forced-tool idea over HTTP. So a
CLI-backed provider is not inherently second-class in this codebase — but note that this technique
depends on the CLI exposing a tool interface at all, which is where Cursor differs (below).
Sketch of the change:
providers/cursor_llm.pyimplementing the four abstract methods- export in
providers/__init__.py - an
elif provider_lower == "cursor":branch increate_llm_provider() - an entry in
PROVIDER_DEFAULT_MODELS, and the id added to thevalid_providerslist thatLLMProvider.__init__checks - docs entry in
hindsight-docs/src/data/llmProviders.json
Invocation would be cursor-agent -p --output-format json --model <model>, parsing .result from
the single emitted {"type":"result",...} object. Auth comes from CURSOR_API_KEY or the CLI's
existing login. (Cursor's docs now write the command as agent; on my install agent and
cursor-agent are both symlinks to the same binary, so either name works — I use cursor-agent
here because it is unambiguous.)
The one real gap is that Cursor's CLI gives less to work with than Copilot's, and it affects both remaining interface methods:
- No caller-facing tool or schema surface, so structured output would have to be soft.
cursor-agent --helpexposes noresponse_format, JSON-schema or temperature flag, and no flag for supplying tool or function definitions — only--output-format text|json|stream-json, which describes the envelope:resultcarries free-form assistant text. The only mentions of tools in its help text refer to Cursor's own built-ins that the agent runs itself. With no way to declare a tool and read back its arguments, the forced-tool technique above is unavailable, leaving the weaker approachclaude_code_llm.pytakes — inject"You must respond with valid JSON matching this schema:"into the prompt, thenjson.loadsthe reply with aJSONDecodeErrorretry. call_with_tools()has no clean mapping, for the same reason: the headless mode gives the caller no way to define tools and receive tool calls back.
Given the second point, a reasonable first cut might scope the provider to retain and
consolidation via the per-operation settings (HINDSIGHT_API_RETAIN_LLM_PROVIDER,
HINDSIGHT_API_CONSOLIDATION_LLM_PROVIDER) and leave reflect to another provider, rather than
trying to support every operation on day one.
Alternatives Considered
- OpenAI-compatible provider pointed at Cursor — not possible; no such endpoint exists (see Problem Statement, with Cursor's own wording).
- A local shim exposing
/v1/chat/completionsthat shells out tocursor-agent— needs no changes to Hindsight, but has to synthesise structured-output support and manage a subprocess per call, so it is the least maintainable option rather than the easiest. - Local OpenAI-compatible server (Ollama / vLLM / llama.cpp container) with
HINDSIGHT_API_LLM_STRICT_SCHEMA=true— this works today and is what I am using in the meantime. It answers the cost problem, just not "reuse the Cursor subscription." openai-codex/claude-code/gemini— all viable if the corresponding subscription or key exists.
Priority
Important - affects my workflow
Additional Context
Environment: self-hosted ghcr.io/vectorize-io/hindsight:latest in Docker, currently
HINDSIGHT_API_LLM_PROVIDER=github-copilot. cursor-agent version 2026.09.15-d2fe57e.
Checklist
- I would be willing to contribute this feature
Source: vectorize-io/hindsight