fix(server): Cursor V2 threads load project skills and rules - #13499
Conversation
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This changes the default configuration for all Cursor V2 threads and resumes to load project, user, team, MDM, and plugin settings, including hooks and file-based MCP servers. The resulting production behavior and external configuration side effects warrant human review. You can add or adjust custom eligibility rules. Learn more. |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
9fc8676 to
ae065ff
Compare
The Cursor SDK loads no on-disk settings layer unless local.settingSources names it, so V2 Cursor threads ignored AGENTS.md, .cursor/rules, project and user skills, hooks, and file-based MCP config. Pass every layer the Cursor CLI loads (project, user, team, mdm, plugins) and update the recorded agent.open frames that replay matches exactly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
9bf862b to
1237269
Compare
8d7d885
into
t3code/codex-turn-mapping
…loaded The first recording predated #13499, so the SDK loaded no project skills and the model found SKILL.md by searching the workspace. Recorded again with settingSources in agent.open (and an empty HOME), the SDK loads the workspace skill natively and the model reads it straight from the `/review` invocation before answering. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Cursor V2 threads ignored the project's
AGENTS.md,.cursor/rules, and skills.makeCursorAgentOptionsnever setlocal.settingSources, and@cursor/sdk1.0.31 loads no settings layer from disk when that field is missing. The SDK's parser mapsundefinedto every layer off, and the docs say "Withoutlocal.settingSources, only inline servers are loaded." So T3's$skill→/skillrewrite reached an agent that had no skill catalog. The model sometimes foundSKILL.mdanyway by grepping the workspace.What changed
makeCursorAgentOptionsnow passessettingSources: ["project", "user", "team", "mdm", "plugins"]on every create and resume. The recordedagent.openframes in the 10 Cursor transcripts gain that one field, because replay matches outbound frames exactly. Nothing else in the transcripts changed.Why these sources
/etc/cursor/hooks.json(MDM), team hooks and managed skills, user~/.cursor, and project.cursor. T3's design intent is to honor the user's provider customizations. The Claude V2 adapter already does this by leaving the Claude SDK on its "load everything" default."all". In 1.0.31,"all"expands to exactly these five, but an explicit list means a new SDK layer won't be picked up silently.teamandmdmare org-admin layers. Their policy should apply to the user's agents. Without a team account or/etc/cursor/hooks.jsonthey are no-ops. One visible side effect: when authenticated, theteamlayer syncs Cursor's built-in managed skills into~/.cursor/skills-cursor, as the CLI does.CursorTextGenerationkeepssettingSources: []for isolated commit-message and title generation. It is untouched.Effect on T3's sandbox and approval policy
sandboxOptions.enabledandautoReviewstill come from the runtime policy.sandbox.jsonis independent of this change. The SDK reads~/.cursor/sandbox.jsonwhenever an API key is present, and reads the per-repo.cursor/sandbox.jsoninside its permissions service.settingSourcesgates neither (CursorTextGenerationalready relies on this). A per-reposandbox.jsoncan add writable paths, but it could do that before this change too..cursor/hooks.json,~/.cursor/hooks.json, plus Claude-formathooksin.claude/settings*.jsonand~/.claude/settings.json) can deny tool calls or ask for approval. Local SDK runs have no interactive approval, so "ask" becomes a deny. They cannot auto-approve anything..cursor/mcp.jsonservers now start, and the SDK passesignoreApprovals: true, so it skips Cursor's per-server approval. This matches how Claude threads load project config.Verification
@cursor/sdk1.0.31,composer-2.5, a temp workspace with anAGENTS.mdrequiring the linePROJECT-RULE-MARKER-4417and a skill at.cursor/skills/zebra-audit/SKILL.md. Prompt: "Without using any tools: list the names of the agent skills available to you that are defined in this project (or say NONE), then answer: what is 2+2?"settingSourcesomitted (before):NONE … nothing in the context I was given lists project-defined agent skills … 2 + 2 = 4. No marker line.["project","user","team","mdm","plugins"](after):- zebra-audit … 2+2 = 4 … PROJECT-RULE-MARKER-4417/zebra-auditas the prompt and no sources, the model still answered correctly, but only aftergrep/read AGENTS.md/glob **/*/read SKILL.mdto find it. With sources it read the skill directly.["project"]alone also fixes the project case. Addinguser+pluginssurfaces~/.agents/~/.claudeuser skills and account plugins. Create-to-finish time was about the same with or withoutteam/mdm(7–11s per run).cd apps/server && vp test run src/orchestration-v2/Adapters/CursorAdapterV2.test.ts: 12 passed, including the new option test.cd apps/server && vp test run src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts -t cursor: 9 Cursor fixtures passed.cd apps/server && vp test run src/orchestration-v2/testkit/OrchestratorReplayRecovery.integration.test.ts src/orchestration-v2/Adapters/CursorAdapterV2.testkit.test.ts src/orchestration-v2/Adapters/CursorAgentSdk.test.ts: 9 passed. This covers theprovider_thread_resumeresume frame.cd apps/server && vp exec tsc --noEmit -p .: no errors.vp linton the two touched.tsfiles: only a pre-existing unused-layerwarning.Overlap
#13493 (
v2/cursor-rerecord) re-records the same Cursor transcripts and adds askill_invocationfixture that was recorded withoutsettingSources. Whichever PR merges second needs itsagent.openframes carrying"settingSources":["project","user","team","mdm","plugins"], most simply by re-recording after this change. That branch'sskill_invocationrecording currently shows the model findingSKILL.mdby exploring the workspace, not through native skill loading.Model: Claude Opus 5.5 (Claude Code)
🤖 Generated with Claude Code