← All posts
AI IntegrationAgent architectureCursorSelf-hosting

Your platform is the moat, the coding agent is a plug-in

We already run a self-hosted autonomous engineer. When we wanted Cursor's cloud agents too, we didn't choose between them — we made the executor swappable. Here's the integration architecture that turns any hosted coding agent into a first-class engine behind your own surface, in four small pieces.

We already run our own autonomous engineer. You assign a task and a repo; a Claude Agent SDK executor plans, writes the code, runs the tests, reviews its own diff, and opens a pull request. We call it Origin, and it lives entirely on our infrastructure.

Then we wanted Cursor’s cloud agents as well — their isolated cloud VM, the speed of composer, and a model mix (grok, opus, gpt) we don’t host. The obvious question is “which one do we standardize on?” It’s the wrong question. The right answer is: don’t choose the executor — make it swappable.

That reframing is the whole point of this post. After you’ve built the orchestration layer once, adding a second engine — even a hosted, third-party one — is not a rewrite. It’s a thin adapter. Here is the shape of it.

The orchestrator is the platform; the executor is a plug-in

An agent platform is two separable things: the surface + orchestrator (how tasks are assigned, tracked, reviewed, evaluated, budgeted) and the executor (the thing that actually does the work). Teams pour their differentiation into the first and rent the second. So the executor should be the swappable part.

flowchart TD
  subgraph Platform["Your platform — the part you own"]
    S["Surface: chat · task board · issues"]
    O["Orchestrator: state, review, evals, budget, PR flow"]
  end
  O --> REG{"Engine registry"}
  REG -->|self-hosted| C["Claude Agent SDK
(your machines)"] REG -->|hosted| CUR["Cursor Cloud Agents
(their VM)"] REG -->|local| L["Local model
(Ollama)"] C --> PR([Pull request]) CUR --> PR L --> PR

Our platform already had an engine registry — three of them (fast, smart, dsh). Adding Cursor meant adding a fourth value, cursor, and teaching the orchestrator what to do when it sees it. Nothing above the registry changed: same task board, same reviewers, same evals, same pull-request review.

An integration agent is four small pieces

Wrapping a hosted coding agent as a first-class engine took exactly four parts. This is the reusable recipe.

PieceJobOurs
ConnectorNormalize the vendor API to launch → poll → PRcursor.py — 90 lines of stdlib
ToolExpose it to any model as a functioncursor_code(task, repo, model)
AgentA persona whose job is to use the tool”Cursor Engineer” — a coding bot
HarnessRegister it in the orchestrator’s engine setengine=cursor on the task board

None of these is big. The connector is the only real code, and it’s small because Cursor’s Cloud Agents API is well-shaped for this:

sequenceDiagram
  participant P as Platform
  participant A as api.cursor.com
  participant G as GitHub
  P->>A: POST /v1/agents {prompt, repo, model, autoCreatePR}
  A->>G: clone repo into an isolated VM
  A-->>P: {id, latestRunId, status}
  loop until terminal
    P->>A: GET /v1/agents/{id}/runs/{runId}
    A-->>P: {status, result, git.branches[].prUrl}
  end
  A->>G: push branch + open PR
  P-->>P: surface the PR url on the run

Launch with a prompt and a repo, poll the run until a prUrl appears, surface it. That’s the entire contract. The connector exposes one blocking helper, run_blocking(...), with an on_event callback so the task board streams the agent’s status transitions live — exactly like our self-hosted engine does.

One surface, chosen per task

Because the engine is just a field on the task, the choice is per-task, not per-platform. The task board’s engineer picker simply lists both:

Self-hosted (Claude)Cursor Cloud
Where the code is clonedyour machinesCursor’s VM
Cost modelyour Claude subscriptionyour Cursor subscription
Control over the loopfull (plan/build/test/review, human gates)the vendor’s (fast, opinionated, auto-PR)
ModelClaudecomposer · grok · opus · gpt
Best forprivate code, custom review checklist, gated flowspeed, throwaway tasks, model variety

Same kanban, same assignee/labels/comments, same GitHub-issue sync, same PR review. The team picks the engine like they’d pick a runtime.

Why bother — three concrete wins

That last point is the real lesson. The industry keeps shipping better executors; that’s a gift, not a threat, if your platform treats them as interchangeable. Build the surface and the orchestration you’d want regardless of who’s behind it, register the engines as plug-ins, and let the best one win per task.

Want something like this built for your team?

Get a quote →