[STG-2515] feat(evals): cursor_sdk full-harness adapter#2341
Open
shrey150 wants to merge 6 commits into
Open
Conversation
shrey150
force-pushed
the
shrey/evals-harness-openai-agents-sdk
branch
from
July 9, 2026 13:36
9eb922a to
a9faa18
Compare
shrey150
force-pushed
the
shrey/evals-harness-cursor-sdk
branch
from
July 9, 2026 13:36
136e248 to
4c77455
Compare
|
shrey150
force-pushed
the
shrey/evals-harness-openai-agents-sdk
branch
from
July 9, 2026 14:06
a9faa18 to
c9e254d
Compare
shrey150
force-pushed
the
shrey/evals-harness-cursor-sdk
branch
from
July 9, 2026 14:06
4c77455 to
665bc5b
Compare
shrey150
force-pushed
the
shrey/evals-harness-openai-agents-sdk
branch
from
July 9, 2026 15:48
c9e254d to
0255867
Compare
shrey150
force-pushed
the
shrey/evals-harness-cursor-sdk
branch
from
July 9, 2026 15:49
665bc5b to
28dec25
Compare
shrey150
marked this pull request as ready for review
July 9, 2026 15:53
Contributor
There was a problem hiding this comment.
All reported issues were addressed across 8 files
Confidence score: 5/5
- Safe to merge after the addressed issues were fixed.
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
shrey150
force-pushed
the
shrey/evals-harness-openai-agents-sdk
branch
from
July 9, 2026 16:17
0255867 to
69033b7
Compare
shrey150
force-pushed
the
shrey/evals-harness-cursor-sdk
branch
from
July 9, 2026 16:18
28dec25 to
df2e2e2
Compare
shrey150
force-pushed
the
shrey/evals-harness-openai-agents-sdk
branch
from
July 9, 2026 17:20
69033b7 to
64633ae
Compare
shrey150
force-pushed
the
shrey/evals-harness-cursor-sdk
branch
from
July 9, 2026 17:23
df2e2e2 to
26b3ec0
Compare
shrey150
force-pushed
the
shrey/evals-harness-openai-agents-sdk
branch
from
July 9, 2026 18:48
64633ae to
e4f238c
Compare
shrey150
force-pushed
the
shrey/evals-harness-cursor-sdk
branch
from
July 9, 2026 18:48
26b3ec0 to
7cb8a7f
Compare
shrey150
force-pushed
the
shrey/evals-harness-openai-agents-sdk
branch
from
July 9, 2026 22:29
e4f238c to
1fbd7c2
Compare
shrey150
force-pushed
the
shrey/evals-harness-cursor-sdk
branch
from
July 9, 2026 22:29
7cb8a7f to
ab81880
Compare
shrey150
force-pushed
the
shrey/evals-harness-openai-agents-sdk
branch
from
July 14, 2026 21:49
1fbd7c2 to
57ae599
Compare
Registers --harness cursor_sdk via @cursor/sdk (Agent.create → send → stream/wait against Cursor's managed local agent). Classification: FULL harness, on the smart tier next to claude_code/codex — 'the same runtime, harness, and models that power Cursor' — not the bare tier, despite being a new SDK. The runner exposes exactly one custom tool (browse, same allowed-command gate as every other external harness) and prompts the agent to use only that tool; the SDK exposes no tool allow-list, so browse-only discipline is prompt + custom-tool based rather than a canUseTool hard gate (recorded in the design doc). cursorAdapter reverse-maps the SDKMessage stream (tool_call/assistant/thinking/usage) into a verifier Trajectory, collapsing running→terminal duplicate tool_call events by call_id. Auth: CURSOR_API_KEY. Live validation pending an API key — implemented and unit-tested against a mocked SDK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…result + clip tool output
Two comparability bugs in the cursor_sdk custom tool:
- execute() returned the bare output string and discarded `ok` from
runBareBrowseCommand. Since runBareBrowseCommand never throws, the SDK
had no signal a browse command failed, so every tool call read back as
"completed" and cursorAdapter (which derives ok from that status)
recorded every call as ok:true. Return the SDK's own
SDKCustomToolResult shape ({content, isError}, per @cursor/sdk's
options.d.ts) with isError: !ok instead.
- The tool ran runBareBrowseCommand directly, bypassing the 20k output
clip every other harness gets via createBareLoopToolRecorder's
readToolOutputLimit. Apply the same clip here so tool-output sizes are
comparable across all five harnesses.
Also strengthens the test: the previous mock hardcoded tool_call status
"completed" regardless of the tool result. Added a case that fails the
underlying browse command and asserts isError:true out of the custom
tool AND ok:false once that isError flows through a terminal tool_call
event into the recorded trajectory.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…call metric - cursorAdapter: non-terminal (running) tool_call events no longer become trajectory steps — an interrupted run must not surface an in-flight call as a successful step. Reasoning buffered before a running event still carries over to its terminal event. - cursorSdkRunner: cursor_tool_calls counts distinct terminal call_ids, matching the adapter's duplicate collapsing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tal/reasoning fields - CursorRunHandle's usage type only declared inputTokens/outputTokens/ cacheReadTokens, dropping the real @cursor/sdk TokenUsage's cacheWriteTokens/ totalTokens/reasoningTokens fields. Widened to a shared CursorTokenUsage type. - cursor_total_tokens now prefers the SDK's own totalTokens over recomputed input+output, which silently dropped cache-write/reasoning tokens. - added cursor_cache_write_tokens and cursor_reasoning_tokens metrics. - updated runBareBrowseCommand's call site for #2337's new skillMode parameter. Node runtime, cancellation, sandboxing, and the persistent-store leak are deliberately out of scope for this pass.
shrey150
force-pushed
the
shrey/evals-harness-cursor-sdk
branch
from
July 14, 2026 21:53
ab81880 to
2a3e7ae
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Part 5 of 5 — stacked on #2340 (
openai_agents_sdk) (base:shrey/evals-harness-openai-agents-sdk; GitHub shows only this PR's delta). When the parent merges, retarget this PR's base and rebase.Registers
--harness cursor_sdkvia@cursor/sdk(343K npm dl/wk):Agent.create → send → stream/waitagainst Cursor's managed local agent. Classification: FULL harness, on the smart tier next to claude_code/codex — "the same runtime, harness, and models that power Cursor" — NOT the bare tier, despite being a new SDK. It ships Cursor's complete loop, planning, and tool behaviors, which is exactly what makes it a useful smart-tier comparison point.browse, same allowed-command gate as every other external harness) and prompts the agent to use only that tool. Known limitation (recorded in the design doc): the SDK exposes no tool allow-list, so browse-only discipline is prompt + custom-tool based rather than acanUseTool-style hard gate; the agent's native shell still runs inside the per-run temp cwd.cursorAdapterreverse-maps theSDKMessagestream (tool_call / assistant / thinking / usage) into a verifierTrajectory— only terminal (completed|error)tool_callevents become steps (an interrupted run can't record an in-flight call as a successful step), duplicate terminal events collapse bycall_id, buffered assistant/thinking text folds into the call's reasoning, per-turn usage sums.cursor/composer-2.5default; prefix stripped). No SDK-level turn cap exists — like claude_code, it inherits harness-native limits.CURSOR_API_KEY(the SDK's own env var). Adds the@cursor/sdkdependency (pinned 1.0.23) — this PR carries its own lockfile delta.sdkinjection seam (Agent.create → send → stream/wait mock).External harnesses design + usage:
packages/evals/README.md#external-harnesses(in #2337). Linear: STG-2515.Comparability fixes (2026-07-09 audit)
Two comparability bugs in the custom
browsetool:execute()returned the bare output string and discardedokfromrunBareBrowseCommand. Since that helper never throws, the SDK had no signal a browse command failed — every tool call read back asstatus: "completed", andcursorAdapter(which derivesokfrom that status) recorded every call asok:trueregardless of outcome. Now returns@cursor/sdk's ownSDKCustomToolResultshape ({ content, isError }, peroptions.d.ts) withisError: !ok.runBareBrowseCommanddirectly, bypassing the 20k-char output clip every other harness gets viacreateBareLoopToolRecorder'sreadToolOutputLimit. Now clipped the same way, so tool-output sizes are comparable across all five harnesses.Strengthened the test: the previous mock hardcoded
tool_callstatus"completed"regardless of the tool result. Added a case that fails the underlyingbrowsecommand and assertsisError:trueout of the custom tool, then that it lands asok:falseonce thatisErrorflows through a terminaltool_callevent into the recorded trajectory.Open question for reviewers
@cursor/sdk'soptions.d.tsexposes an undocumentedsandboxOptions: { enabled }toggle onLocalAgentOptionsthat this runner doesn't set. Unknown whether enabling it narrows the shell-gating gap noted above (the SDK has no tool allow-list to hard-disable its native shell/file tools, so browse-only discipline here is prompt + custom-tool based, not a hard gate). Flagging for input before deciding whether to wire it — no doc coverage found beyond the type declaration itself.E2E Test Matrix
@cursor/sdk@1.0.23type declarations (Agent.create/send,Run.stream/wait,SDKMessage,TokenUsage), not from docs alonepnpm exec vitest run(this branch)Test Files 53 passed, Tests 385 passed— addscursorSdkRunner.test.ts(agent creation with gated custom tool wired to a real temp binary, prompt composition = arm prompt + browse-only instruction + task, stream collection, close() on both paths, metrics; plus cursorAdapter mapping tests: reasoning folding, duplicate call_id collapse, usage summing, error tool_calls)tsc --noEmit+ prettier + eslintpnpm exec vitest run(this branch, post-fix, stack tip)Test Files 57 passed, Tests 419 passed— adds the isError/ok:false propagation test, the in-flight (running-only) tool_call exclusion test, and the duplicate-terminal-event metric dedup caseCURSOR_API_KEYpnpm -w exec tsc --noEmit+ prettier + eslint (post-fix)Tier 1 fixes (post-review audit, 2026-07-14)
Scope: token-accounting fields only. An independent audit found the Cursor SDK's real
TokenUsage(cacheReadTokens,cacheWriteTokens,totalTokens,reasoningTokens?) was only partially typed here (inputTokens/outputTokens/cacheReadTokens), socacheWriteTokensandreasoningTokenswere silently dropped andcursor_total_tokensrecomputed input+output instead of using the SDK's real total. Fixed:CursorRunHandle's usage type (and the two other duplicated inline types) to a sharedCursorTokenUsagematching@cursor/sdk's realTokenUsage.cursor_total_tokensnow prefersusage.totalTokensover the recomputed sum; addedcursor_cache_write_tokensandcursor_reasoning_tokensmetrics.runBareBrowseCommand's call site for the base PR's newskillModeparameter (required for the fix to--skill-mode/none-baseline gating there).Explicitly not touched in this pass (Tier 2, deferred): Node
>=22.13runtime requirement vs. the repo's Node 20, missingrun.cancel()/timeout, no sandboxing of Cursor's native shell/file tools, and the persistent local-store leak.cursor_sdkremains a "hard hold" pending those.Verified:
pnpm --filter @browserbasehq/stagehand-evals build && lint && test:unit--Test Files 58 passed, Tests 445 passed(1 new test covering cacheWriteTokens/reasoningTokens/totalTokens preference). No live credential available (CURSOR_API_KEY-- documented pre-existing gap); unit coverage is the verification bar here.No changeset:
packages/evalsis private.🤖 Generated with Claude Code
Summary by cubic
Adds a full
cursor_sdkharness powered by@cursor/sdkto run Cursor’s managed local agent and feed results into our verifier. Implements STG-2515 with correct tool-failure signaling, terminal-only tool_call handling, consistent tool-output clipping, and richer token metrics.New Features
--harness cursor_sdkvia@cursor/sdkand adds it to the bench registry.browsecustom tool with the allowed-command gate; returns SDK-shaped{ content, isError }and clips output to the standard limit.cursorAdapter: only terminal tool_calls become steps, duplicatecall_ids deduped, assistant/thinking folded into reasoning, usage summed; metrics include dedupedcursor_tool_calls,cursor_cache_write_tokens,cursor_reasoning_tokens, andcursor_total_tokens(prefers SDKtotalTokenswhen present).@cursor/sdk@1.0.23and unit tests with a mocked SDK covering failure propagation, in-flight tool_call exclusion, and metric dedup.Migration
CURSOR_API_KEYto run this harness.cursor/composer-2.5(provider prefix is stripped).Written for commit 2a3e7ae. Summary will update on new commits.