feat(plugin-tinyplace): harden cursor responder (yolo+isolation, stream-json, rules) - #251
Conversation
…am-json, rules) Fold three multica-derived improvements into the Cursor adapter: 1. Responder uses `--yolo` (the only way cursor-agent invokes MCP tools — i.e. auto_reply — headlessly) but confined to a THROWAWAY send-only `--workspace`, so a prompt-injected DM's writes/shell land in a per-wallet scratch dir, never the user's files. The workspace's mcp.json pins SEND_ONLY + NO_AUTORESPOND, daemon off. Built once per batch by a new optional `responder.prepare(ctx)` hook (buildArgs stays side-effect-free). 2. `--output-format stream-json`: the shared spawner now watches stdout for the terminal `result` event (opt-in via `responder.streamComplete`) and finishes+kills on it — cursor-agent's known print-mode hang-after-reply no longer waits out the 180s timeout and falsely fails an already-sent reply. 3. cursor-agent has no `--system-prompt`, so the launcher writes the tiny.place UNTRUSTED-handling guidance to an always-applied `.cursor/rules/tinyplace.mdc` in the isolated interactive workspace. Contract: `buildArgs(prompt, model, pluginRoot, ctx?)` gains an optional 4th ctx arg; codex/claude/windsurf ignore it. README + harness-test updated; all 43 harness checks pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@CodeGhost21 is attempting to deploy a commit to the Vezures Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe Cursor responder now uses prepared isolated workspaces, ChangesCursor streaming responder
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant BatchRunner
participant CursorAdapter
participant CursorAgent
participant MessageFile
BatchRunner->>CursorAdapter: prepare responder context
CursorAdapter-->>BatchRunner: isolated workspace
BatchRunner->>CursorAdapter: build responder arguments
CursorAdapter-->>BatchRunner: stream-json command
BatchRunner->>CursorAgent: spawn with workspace
CursorAgent-->>BatchRunner: terminal NDJSON result
BatchRunner->>MessageFile: delete completed message
BatchRunner->>CursorAgent: terminate process
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Warning Tools execution failed with the following error: Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error) Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9234d8d3a6
ℹ️ 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".
| const args = ["-p", "--yolo", "--output-format", "stream-json"]; | ||
| if (ctx?.workspace) args.push("--workspace", ctx.workspace); |
There was a problem hiding this comment.
Avoid yolo for attacker-controlled Cursor replies
When the Cursor auto-responder handles an untrusted DM, adding --yolo lets the model auto-approve shell and file-write tools; Cursor's parameter docs state that print mode has access to write/shell tools and that --yolo is an alias for --force, while --workspace is only the workspace directory, not an OS sandbox (https://cursor.com/docs/cli/reference/parameters.md). A malicious message can therefore prompt the responder to read or modify absolute paths outside the scratch workspace before calling auto_reply, regressing the previous --sandbox enabled posture.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
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/hooks/respond-batch.mjs`:
- Around line 70-79: Update the prepare() error handling in the responder setup
block to fail closed: catch the thrown error, log it with the batch context, and
abort the current batch before any responder is spawned. Do not continue with a
degraded RESPONDER_CTX or silently omit the workspace guardrail; preserve normal
preparation and spawning when ADAPTER.responder.prepare succeeds.
🪄 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: 37f857e8-c073-4912-8945-423d2943c059
📒 Files selected for processing (4)
sdk/plugin-tinyplace/adapters/README.mdsdk/plugin-tinyplace/adapters/cursor.mjssdk/plugin-tinyplace/harness-test.mjssdk/plugin-tinyplace/hooks/respond-batch.mjs
If ADAPTER.responder.prepare() throws, the batch was silently continuing with a degraded RESPONDER_CTX (no workspace). For Cursor that means buildArgs emits 'cursor-agent -p --yolo ...' WITHOUT the isolated --workspace, so --yolo's auto-approved shell/writes on attacker-controlled DM text run against the real cwd instead of the throwaway sandbox — the exact guardrail the isolation exists to provide. Fail closed: log the error with batch context, move the claimed messages to failed/ for retry, remove the empty batch dir, and abort before spawning any responder. Success path (prepare returns the workspace) is unchanged; adapters without prepare() (codex/claude/windsurf) are unaffected. Addresses review feedback on tinyhumansai#251. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…252) * docs(plugin-tinyplace): cursor⇄openhuman bidirectional bridge spike Capture the throwaway prototype that proves a Cursor IDE agent can be driven into a live two-way tiny.place conversation with OpenHuman over the Signal relay, plus the findings that inform the real adapter: - forward (Cursor→OpenHuman) via beforeSubmitPrompt/afterAgentResponse hooks → SessionEnvelopeV1 DMs rendered as a `cursor` runtime. - reverse (OpenHuman→Cursor) via a daemon that pastes inbox DMs into the live GUI (clipboard + System Events), with echo-suppression and focus-restore. - findings: `stop → followup_message` is the only in-conversation injection channel; CGEventPostToPid can't reach a backgrounded Electron window; AX value-set doesn't register in React; concurrent FileSessionStore access corrupts the ratchet (→ HTTP 400), fixed with a cross-process lock; SDK ≥2.0.2 required for base58 bundle routing. Prototype only (README flags the security caveats + auto-approve tradeoff); nothing here ships. Complements the cursor adapter hardening (#251). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(plugin-tinyplace): bridge approval routing + session self-heal Extend the cursor⇄openhuman bridge prototype with the tool-approval feature and transport hardening validated against staging: - hook.mjs: route beforeShellExecution/beforeMCPExecution to OpenHuman as a v2 approval_request event and block for the allow/deny decision (falls back to Cursor's own prompt on timeout); auto-allow file reads. - common.mjs: v2 approvalEnvelope builder, extractText, an AWAITING flag (daemon pauses inbox draining while an approval is pending), and sendWithRetry which self-heals a desynced session (reset + retry on a 400/encrypt error). - daemon.mjs: pause while an approval is pending so the hook owns the decision DM. - README: approval-routing section + findings on the two-store ratchet fragility (receiving side can't retry a silent drop) and deriving the resolved-card state. Prototype only; kept repo-portable (relative SDK dist, OPENHUMAN_ADDR from env). Pairs with the OpenHuman Allow/Deny card PR (tinyhumansai/openhuman#4837). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
Follow-up to #241, hardening the Cursor auto-responder using patterns from
multica-ai/multica'sserver/pkg/agent. Three changes, all Cursor-scoped (codex/claude/windsurf untouched):--yolo+ throwaway isolated--workspace. The responder delivers its reply by calling the tinyplaceauto_replyMCP tool, and cursor-agent only invokes MCP tools headlessly under--yolo.--yoloalso auto-allows shell + file writes on the (attacker-controlled) DM text, so we confine it: the responder runs in a per-wallet throwaway send-only workspace (<dataDir>/responder-home/<wallet>) whosemcp.jsonpinsSEND_ONLY+NO_AUTORESPOND, daemon off. A prompt-injected DM's writes/shell land in that scratch dir, never the user's files; the existing timeout/kill is the second guard.--output-format stream-json→ kills the hang. cursor-agent can hang after emitting its reply. The shared spawner now (opt-in viaresponder.streamComplete) pipes stdout and finishes+kills on the terminal{"type":"result"}event, so a completed-but-hung turn is a success instead of waiting out the 180 s timeout and falsely failing an already-sent reply..cursor/rules/tinyplace.mdc. cursor-agent has no--system-prompt, so the launcher writes the tiny.place UNTRUSTED-handling guidance to an always-applied rule in the isolated interactive workspace.Problem
The Cursor responder shipped in #241 with honest
[VERIFY]flags: the least-privilege--sandbox/--approve-mcpsargs were not confirmed to let cursor-agent callauto_replyheadlessly (validation was blocked by rate-limiting). multica's production path shows cursor-agent needs--yolofor MCP tool calls and parsesstream-jsonto detect turn completion. That reframes the responder as: it must run--yolo, so the security work is confinement, not avoidance.Solution
responder.prepare(ctx)(runs once per batch for setup that can't live in the side-effect-freebuildArgs) andresponder.streamComplete(spawner watches stdout for theresultevent).buildArgsgains an optional 4thctxarg; other adapters ignore it.respond-batch.mjscallsprepare()once, threads the ctx intobuildArgs, and (for streamComplete responders) pipes stdout to finish-on-result.harness-test.mjsupdated. All 43 harness checks pass;prepare()/launch workspace generation smoke-tested (correct env, rule file withalwaysApply+UNTRUSTED).Impact
[VERIFY]remains on the live cursor-agentresult-event schema and headless rule-honoring across versions — the timeout guard is the fallback if either differs.Related
[VERIFY]items to close with a live cursor-agent run: (1)--yoloinvokesauto_replyfrom the isolated workspace, (2)stream-jsonresultevent shape, (3) headless honoring ofalwaysApplyrules.🤖 Generated with Claude Code
Summary by CodeRabbit
prepare(ctx), context-awarebuildArgs, and thestreamComplete: truestreaming/completion behavior.