Skip to content

feat(adapters): add Cursor and Windsurf (Devin) harness adapters#241

Open
CodeGhost21 wants to merge 5 commits into
tinyhumansai:mainfrom
CodeGhost21:feat/cursor-adapter
Open

feat(adapters): add Cursor and Windsurf (Devin) harness adapters#241
CodeGhost21 wants to merge 5 commits into
tinyhumansai:mainfrom
CodeGhost21:feat/cursor-adapter

Conversation

@CodeGhost21

@CodeGhost21 CodeGhost21 commented Jul 10, 2026

Copy link
Copy Markdown

Summary

Adds Cursor and Windsurf (Devin Desktop) harness adapters to plugin-tinyplace, so the one-plugin-any-harness core can run inside those agents — the session-origination side of OpenHuman issue #4730 (the recognition side is a separate openhuman PR).

Each adapter is a per-harness descriptor registered in mcp/harness.mjs's ADAPTERS map, following adapters/README.md and mirroring codex.mjs/claude.mjs.

What's new

  • adapters/cursor.mjs (provider cursor) — empirically verified + live-E2E'd.
  • adapters/windsurf.mjs (provider windsurf) — build-from-docs, not yet live-verified.
  • mcp/harness.mjs: register both + self-provisioned detection.
  • harness-test.mjs: detection + wiring assertions for both (TDD).

Design notes (why these differ from codex/claude)

Both Cursor and Windsurf/Devin are GUI-IDE agents, so:

  • foregroundInject (tmux) is out → inbound is pull-only (the inbox tool); hook-based surfacing is a planned follow-up.
  • No ambient env reaches the MCP subprocessempirically verified for Cursor (cursor-agent hands the MCP server only HOME/PATH/SHELL/…; no session id, workspace, or CURSOR_*, nothing inherited). So detection is self-provisioned: install writes TINYPLACE_HARNESS=<harness> + TINYPLACE_<HARNESS>_HOME into the MCP config env block, which is also the only channel config reaches the server.
  • Install = launch.prepare() writes an isolated workspace MCP config under dataDir (Cursor: .cursor/mcp.json; Windsurf: .codeium/windsurf/mcp_config.json) — never touches the user's real global config.
  • Responder = the harness's headless CLI: Cursor cursor-agent -p --model <m> --force --approve-mcps; Windsurf devin -p --model <m> --permission-mode bypass.

Verification

  • Full suite green: npm test (harness, adapter-contract for all 5 adapters, envelope, session-info, registry, routing, lock, inject, hooks, mcp-smoke).
  • Cursor — live E2E on staging: a real cursor-agent turn loaded the plugin via the adapter's generated mcp.json, called whoami (reported session label cursor:1 — proving the cursor adapter was selected), and published a Signal identity to staging-api.tiny.place (independently confirmed via /directory/agents + /keys/{id}/bundle).

⚠️ Windsurf is build-from-docs — [VERIFY] before relying on it

No Windsurf/Devin install was available, so adapters/windsurf.mjs is sourced from docs.devin.ai and every unverified assumption is tagged [VERIFY] in-code:

  • MCP-subprocess env behavior (assumed sanitized, like Cursor),
  • exact default model id (devin models),
  • whether Devin honors a project-level mcp_config.json or only the global one (affects install),
  • a least-privilege responder flag--permission-mode bypass grants broad tool access; needs a --sandbox read-only equivalent before production (security follow-up).

Note: the harness key stays windsurf (matches OpenHuman's recognition key + the on-disk ~/.codeium/windsurf/ brand); only the responder binary follows the 2026-06 Windsurf→Devin Desktop rebrand.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added harness support for Cursor and Windsurf via adapter-based runtime selection.
    • Extended harness detection to recognize Cursor/Windsurf environment signals, with explicit harness overrides.
    • Added per-wallet isolated workspaces with auto-generated MCP configuration and untrusted inbound handling (tool-driven replying and contact request approval).
  • Bug Fixes
    • Prevented stuck responder runs by enforcing a per-run timeout, escalating termination, and reliably marking failed jobs.
  • Tests
    • Expanded harness detection and adapter wiring assertions for Cursor and Windsurf.

CodeGhost21 and others added 2 commits July 10, 2026 16:44
Registers a 'cursor' adapter in plugin-tinyplace mirroring codex/claude,
so the one-plugin-any-harness core can run inside Cursor.

Grounded in an empirical spike (2026-07-10): cursor-agent spawns MCP
servers with a sanitized env (no session id / workspace / CURSOR_* signal,
nothing inherited), so:
- detection is self-provisioned via TINYPLACE_HARNESS=cursor +
  TINYPLACE_CURSOR_HOME written into the mcp.json env block;
- launch.prepare() writes an isolated <dataDir>/cursor-home/<wallet>/
  .cursor/mcp.json carrying the full env block (never touches ~/.cursor);
- responder drives headless `cursor-agent -p --model <m>`;
- inbound is pull-only (inbox tool); hook-based surfacing is a follow-up.

Covered by the adapter-contract test (auto) + harness-test detection/wiring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Registers a 'windsurf' adapter in plugin-tinyplace, mirroring the cursor
adapter's GUI-IDE shape (self-provisioned detection, isolated-workspace
mcp_config.json install, pull-only inbound, headless responder).

Harness key stays 'windsurf' (matches OpenHuman's recognition key + the
on-disk ~/.codeium/windsurf/ brand); the responder uses the 'devin' CLI
per the 2026-06 Windsurf->Devin Desktop rebrand.

BUILD-FROM-DOCS: not empirically verified (no Windsurf/Devin install
available). Behavior sourced from docs.devin.ai; [VERIFY]-tagged items
(MCP-subprocess env, default model id, global-vs-project mcp_config
resolution, a least-privilege responder flag) must be confirmed against a
live install before production. Covered by the adapter-contract + harness
tests today.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

@CodeGhost21 is attempting to deploy a commit to the Vezures Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cee38d81-8bf9-4451-ade6-0ab89a58c7c3

📥 Commits

Reviewing files that changed from the base of the PR and between 80191b4 and 16d1a0f.

📒 Files selected for processing (2)
  • sdk/plugin-tinyplace/adapters/cursor.mjs
  • sdk/plugin-tinyplace/adapters/windsurf.mjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdk/plugin-tinyplace/adapters/windsurf.mjs

📝 Walkthrough

Walkthrough

Cursor and Windsurf adapters are added with isolated MCP workspace setup, pull-only inbound handling, responder launch configuration, runtime harness detection, adapter wiring tests, and bounded responder execution with failure handling.

Changes

Harness adapter support

Layer / File(s) Summary
Cursor adapter launch and MCP workspace
sdk/plugin-tinyplace/adapters/cursor.mjs
Adds Cursor configuration, safe responder arguments, isolated .cursor/mcp.json generation, and cursor-agent launch preparation.
Windsurf adapter launch and MCP workspace
sdk/plugin-tinyplace/adapters/windsurf.mjs
Adds Windsurf configuration, Devin responder arguments, isolated MCP configuration, and devin-desktop launch preparation.
Harness detection and adapter validation
sdk/plugin-tinyplace/mcp/harness.mjs, sdk/plugin-tinyplace/harness-test.mjs
Registers and detects Cursor and Windsurf, while testing explicit overrides and adapter wiring.

Responder execution lifecycle

Layer / File(s) Summary
Responder timeout and failure handling
sdk/plugin-tinyplace/hooks/respond-batch.mjs
Adds validated timeout enforcement, termination escalation, single-settlement cleanup, and failed-message handling for unsuccessful runs.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Harness
  participant cursorAdapter
  participant windsurfAdapter
  participant IsolatedWorkspace
  participant CursorAgent
  participant DevinDesktop
  participant MCPServer
  Harness->>cursorAdapter: resolve Cursor harness
  cursorAdapter->>IsolatedWorkspace: create wallet-scoped .cursor/mcp.json
  IsolatedWorkspace-->>cursorAdapter: return workspace path
  cursorAdapter->>CursorAgent: launch with workspace
  CursorAgent->>MCPServer: load MCP configuration
  Harness->>windsurfAdapter: resolve Windsurf harness
  windsurfAdapter->>IsolatedWorkspace: create wallet-scoped mcp_config.json
  IsolatedWorkspace-->>windsurfAdapter: return workspace path
  windsurfAdapter->>DevinDesktop: launch with isolated workspace
  DevinDesktop->>MCPServer: load MCP configuration
Loading

Possibly related PRs

Suggested reviewers: sanil-23

Poem

A bunny hops through Cursor’s home,
While Windsurf finds its MCP dome.
Tinyplace whispers, “Pull, don’t push,”
Timed replies finish in a hush.
Two new harnesses join the play—
Hop, launch, and reply today!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change by describing the new Cursor and Windsurf harness adapters.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a755eeb372

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// tool. NOTE: cursor-agent print-mode can hang after replying (verified) — the
// shared responder spawner must wrap this call with a timeout/kill.
buildArgs(prompt, model /* pluginRoot unused: MCP comes from the workspace mcp.json */) {
return ["-p", "--force", "--approve-mcps", "--output-format", "text", "--model", model, prompt];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep Cursor auto-replies out of write mode

When a Cursor session receives a queued DM, hooks/respond-batch.mjs passes the untrusted message text into this buildArgs command as the prompt. Cursor's headless CLI docs state that --force lets print mode make direct file changes without confirmation and the MCP docs state --approve-mcps skips MCP approval (https://cursor.com/docs/cli/headless.md, https://cursor.com/docs/cli/mcp.md), so a prompt-injected DM is not confined to the auto_reply path and can write through the Cursor agent before replying. Use a read-only/sandboxed mode or a tool allowlist here instead of --force.

Useful? React with 👍 / 👎.

// production, find and add devin's sandbox/tool-restriction equivalent so the only
// side-effecting path is the tinyplace `auto_reply` MCP tool.
buildArgs(prompt, model /* pluginRoot unused: MCP comes from mcp_config.json */) {
return ["-p", "--permission-mode", "bypass", "--model", model, prompt];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep Devin auto-replies out of bypass mode

When a Windsurf/Devin session receives a queued DM, this command runs the DM body as a headless prompt. Devin docs define bypass as auto-approving all tool calls, including writes and shell commands, and recommend --sandbox for unattended execution with OS-enforced limits (https://docs.devin.ai/cli/essential-commands, https://docs.devin.ai/cli/reference/permissions); with --permission-mode bypass, a prompt-injected DM can execute or write outside the intended auto_reply path.

Useful? React with 👍 / 👎.

// shape to reconcile once a live install confirms the resolution order.
launch: {
displayHarness: "Windsurf",
binary: "windsurf",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Launch Devin Desktop with its current shell command

The launcher uses launch.binary in installedHarnesses() and prepare() returns the same command below, but current Devin Desktop docs say the folder-opening shell command is devin-desktop while the terminal agent CLI is devin (https://docs.devin.ai/desktop/devin-desktop-faq, https://docs.devin.ai/cli). On a current Devin Desktop install there is no windsurf binary, so this adapter is not auto-offered and a forced launch fails before the generated MCP config can be used.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdk/plugin-tinyplace/adapters/cursor.mjs`:
- Around line 52-64: Update the responder flow in respond-batch.mjs to guard
each cursor-agent process created for the responder configuration with a timeout
and forced termination. Ensure the timeout covers post-reply hangs, rejects or
reports the stalled invocation, and allows the worker to continue processing
subsequent messages; preserve normal output and cleanup behavior for successful
runs.

In `@sdk/plugin-tinyplace/adapters/windsurf.mjs`:
- Around line 63-72: Update the Devin command construction in buildArgs to
remove --permission-mode bypass and use Devin’s sandbox or deny-by-default
permission configuration instead, allowing only the tinyplace auto_reply MCP
tool as a side-effecting capability while preserving the existing prompt and
model arguments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 647cf25a-957f-4782-9a3b-2b5aff9319b8

📥 Commits

Reviewing files that changed from the base of the PR and between adedeb7 and a755eeb.

📒 Files selected for processing (4)
  • sdk/plugin-tinyplace/adapters/cursor.mjs
  • sdk/plugin-tinyplace/adapters/windsurf.mjs
  • sdk/plugin-tinyplace/harness-test.mjs
  • sdk/plugin-tinyplace/mcp/harness.mjs

Comment thread sdk/plugin-tinyplace/adapters/cursor.mjs
Comment thread sdk/plugin-tinyplace/adapters/windsurf.mjs Outdated
…ktop launch

Addresses CodeRabbit/Codex review on tinyhumansai#241:

- cursor responder: drop --force (auto-allows writes/shell), use
  --sandbox enabled so a prompt-injected DM can't write/execute through
  the Cursor agent; --approve-mcps still lets the auto_reply MCP tool
  through (separate process, outside the sandbox). [P1]
- windsurf responder: drop --permission-mode bypass (auto-approves all
  tools), use Devin's --sandbox OS-level isolation. [P1]
- respond-batch: bound every responder turn with a timeout + SIGTERM/
  SIGKILL (cursor-agent print-mode can hang after replying, which would
  wedge a worker); tunable via TINYPLACE_RESPONDER_TIMEOUT_MS.
- windsurf launch: binary windsurf -> devin-desktop (current Devin Desktop
  folder-opener; no windsurf binary exists, so the adapter was never
  auto-offered). [P2]

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdk/plugin-tinyplace/adapters/windsurf.mjs`:
- Around line 63-76: The buildArgs method must terminate CLI option parsing
before passing the attacker-controlled prompt. Update its returned argument list
to include the standalone "--" immediately before prompt, preserving the
existing flags and model arguments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c13aa6ee-6bc8-437d-beea-d97c8f07a9fa

📥 Commits

Reviewing files that changed from the base of the PR and between a755eeb and 80191b4.

📒 Files selected for processing (3)
  • sdk/plugin-tinyplace/adapters/cursor.mjs
  • sdk/plugin-tinyplace/adapters/windsurf.mjs
  • sdk/plugin-tinyplace/hooks/respond-batch.mjs
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdk/plugin-tinyplace/adapters/cursor.mjs

Comment thread sdk/plugin-tinyplace/adapters/windsurf.mjs
The auto-responder passes attacker-controlled DM text as the trailing
positional prompt. Without a -- terminator a DM starting with -/-- is
parsed as a CLI flag (verified live: cursor-agent errors 'unknown option'
on a dash-leading prompt; with -- it's taken as the prompt). Insert -- in
both the cursor and windsurf responder buildArgs. Same commander-style
parser on devin, so the guard applies there too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@oxoxDev oxoxDev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — Cursor + Windsurf harness adapters

Solid harness-core wiring — Cursor path is genuinely E2E-verified, detection is unambiguous and crash-safe, config writes are injection-safe (JSON.stringify + 0o600), and the respond-batch.mjs refactor preserves codex/claude exit semantics byte-for-byte (idempotent settled-guarded finish()). Requesting changes on a few points before merge — the Cursor half is close, but the Windsurf responder's safety posture is unverified, not at proven parity with codex/claude.

Sanity-level parity (Windsurf vs codex/claude)

Guard claude codex cursor windsurf
UNTRUSTED-data prompt framing
OS sandbox --permission-mode dontAsk --sandbox read-only --sandbox enabled --sandbox ⚠️ unverified shape
Never dangerous-bypass ✓ (documented)
Hard tool-allowlist (auto_reply only)

Windsurf inherits the UNTRUSTED framing and documents codex-parity sandboxing, but its actual sandbox invocation is the [VERIFY] line — so least-privilege is claimed, not confirmed. Please close that before enabling it.

Please address

1. [major] adapters/windsurf.mjs:77--sandbox shape is unverified, and it's a SECURITY guard, not just a functional flag. buildArgs emits ["-p","--sandbox","--model",model,"--",prompt]. Cursor's verified form is value-taking (--sandbox enabled) and codex is --sandbox read-only. If Devin's --sandbox also takes a value, it consumes --model as the sandbox value and model as a stray positional → the responder either mis-parses (fails closed into failed/) or Devin runs the prompt-injected DM without the intended read-only sandbox — i.e. Windsurf would NOT be at the codex/claude least-privilege parity the comment claims. Self-flagged [VERIFY] + inert-until-provisioned, so not a blocker for the Cursor path — but confirm against a live Devin that (a) the flag parses and (b) it actually enforces read-only, before Windsurf is enabled anywhere. Cleanest: split Windsurf to a follow-up so Cursor lands verified and Windsurf merges only once its sandbox is proven.

2. [minor, security] Responder auto-approves the full tinyplace tool set. --approve-mcps (Cursor) / autonomous -p (Devin) approve every exposed MCP tool — send, contact_add, contact_accept, send_and_wait — not just auto_reply, and TINYPLACE_SEND_ONLY only changes mailbox/presence, not the registered tool set. So the only barrier stopping a prompt-injected inbound DM from messaging third parties or accepting contacts is the soft prompt instruction ("use no other tool"). Claude hard-enforces via --allowedTools mcp__tinyplace__auto_reply; Cursor/Devin headless may not have an equivalent. This is pre-existing (codex's exec responder has the same exposure) — not introduced here — but Cursor/Windsurf inherit it. Recommended hardening (fixes codex too): when TINYPLACE_SEND_ONLY is set, register only auto_reply in the MCP server, instead of relying on per-CLI allowlists.

3. [nit] cursor.mjs responder cwd. The responder is spawned with no cwd/--workspace, so cursor-agent must discover .cursor/mcp.json from the inherited cwd. Confirm the daemon's spawn cwd is the isolated workspace — a wrong cwd silently drops auto_reply with no error.

4. [doc] PR body. The respond-batch.mjs change adds a uniform 180s turn cap (SIGTERM→3s→SIGKILL) to all harnesses incl. codex/claude — a real behavioral delta (their turns were previously unbounded). Net-positive, but worth a line so it's not a silent change.

Verified good

  • No regression to codex/claude detection — harness.mjs diff purely additive, new branches sit before the claude default.
  • Detection unambiguous + crash-safe (TINYPLACE_HARNESS?.trim().toLowerCase(), unique home var per harness, tested); Windsurf is inert unless provisioned → cannot hijack a real cursor/codex/claude session.
  • Config-write injection-safe; -- prompt terminator on both buildArgs; full adapter-contract conformance; harness-test.mjs covers both new adapters.
  • Confirmed resolved: Codex "keep Cursor/Devin out of write/bypass mode", Devin binary reconciliation, CR "no timeout/kill guard" (this PR's timeout).

… [VERIFY]

Keep the sandboxed least-privilege responder the review asked for
(--sandbox enabled, no --force) and add --trust so headless mode can start
in the isolated workspace without --force's run-everything.

Corrects an earlier over-claim: whether cursor-agent can invoke MCP tools
UNDER --sandbox headlessly is NOT yet live-confirmed (validation was
blocked by cursor-agent rate-limiting). Documented as [VERIFY] with the
fallback if a clean-env retest shows the sandbox blocks the tool call. The
first clean E2E confirmed the adapter itself works; the -- terminator and
the respond-batch timeout guard remain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants