Adaptive reasoning and direct Cursor mode - #27
Closed
nvdorman wants to merge 42 commits into
Closed
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Three blocking issues from review, fixed without weakening validation
for newly-explicit invalid values:
1. Default OpenRouter/direct-Anthropic chat failed pre-request. The
config default carried a non-empty "medium" reasoning effort with no
attached capability, and openai-compatible's static resolution is
deliberately nil, so every default chat errored before the network.
Direct Anthropic also failed because the runtime default base URL
("https://api.anthropic.com/v1") didn't match the static catalog's
bare-host check. Fixed by defaulting reasoning effort to "" (Auto,
valid everywhere) and widening the catalog's Anthropic base URL
match to an exact two-value allowlist.
2. Bedrock bypassed reasoning validation by calling anthropic buildBody
directly, silently dropping invalid explicit values and sending
signed requests upstream. bedrockClient.Chat now runs the same
validateReasoning gate before signing/sending.
3. A legacy Anthropic model could pass attached-capability validation
for a value absent from the hardcoded budget table, silently
vanishing from the request. Factored the body-shape decision into
anthropicThinkingBody, shared by buildBody and a new
validateReasoning check so an unmappable value now fails before
any request.
Co-authored-by: Cursor <cursoragent@cursor.com>
1. Direct Gemini's static catalogue required provider id "google", but the shipped provider default (internal/config/defaults.go) uses "gemini" as its provider map key, so the static fallback never fired for the real default Gemini provider. Widened the match to an exact two-value allowlist (isGeminiDirectProvider): "google" or "gemini", still rejecting arbitrary compatible/custom provider ids. 2. geminiThinkingConfig returned nil for both "no override" and "cannot map this effort", so a value that passed an attached ReasoningCapability but had no real mapping (unrecognised keyword, or "minimal" on a legacy budget model) silently vanished from the request instead of failing. geminiThinkingConfig now returns an explicit ok bool distinguishing the two; a new geminiClient.validateReasoning fails Chat/Stream before any network call when ok is false, while buildBody stays a pure, non-erroring body builder. Known low/medium/high/minimal/none mappings for both Gemini 3 (thinkingLevel) and 2.5 (thinkingBudget) models are unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Reuse scoped catalogues and preserve stale capabilities so interactive reasoning remains deterministic across repeated lookups and transient provider failures. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep catalogue fetches independent of individual waiters and retain resolved adapter identity without widening explicit Codex capabilities. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Validate raw edits against their candidate provider map without touching live config, and preserve repairability while distinguishing unavailable metadata from unsupported values. Co-authored-by: Cursor <cursoragent@cursor.com>
Apply provider credential and endpoint overrides to write-free candidate parsing so capability checks match the eventual reload without persisting derived values. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep opaque preferences intact while model metadata is unavailable and resolve role/config capabilities through deduplicated targeted lookups. Co-authored-by: Cursor <cursoragent@cursor.com>
Collapse free-text target edits to one stale-safe request and bound authoritative metadata caching to prevent lookup storms and partial-key growth. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Ensure every explicit operation is safely announced through one approval policy path and fully redact key-like display fields. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Preserve recoverability across local detach and restart while making ambiguous outcomes explicitly reconcilable without duplicate remote mutations. Co-authored-by: Cursor <cursoragent@cursor.com>
Ensure every recovery follower replays durable state and prevent automated cleanup from deleting active remote Cursor sessions across selection races. Co-authored-by: Cursor <cursoragent@cursor.com>
Keep compacted SSE replay reconstructable and protect terminal work while making bulk deletion and cancellation classification deterministic. Co-authored-by: Cursor <cursoragent@cursor.com>
Treat local create configuration failures definitively, make exact-set deletions atomic, and explain compacted tool activity without retaining payloads. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Leaving conversation B for A and returning gives two openings that share every textual identifier, and a navigation commits before its passive cleanup runs. The pending-approval refresh and the standing attachment were scoped by session id plus an `alive` flag, so a response, frame, error, retry, end-of-turn refresh, or finally callback from the first opening could still write into the second. Both operations now carry the exact route-open occurrence and ask it again at the instant they would write. Co-authored-by: Cursor <cursoragent@cursor.com>
…provider-impl Main advanced 15 commits past the PR enowdev#25 merge that this branch forked from, including the fix that ships the Cursor provider disabled by default. Merging before the follow-up PR keeps that fix rather than letting a stale `Enabled: true` ride back in, and pins the branch's new Cursor suites against the default a user will actually get. Three files overlapped and auto-merged cleanly: - internal/config/defaults.go: main's cursor `Enabled: false` alongside this branch's Auto (`""`) reasoning defaults. - internal/tools/cursor_agent_test.go: main's explicit `p.Enabled = true` fixture. - web/src/pages/ChatPage.tsx: main's ReasoningBlock expand-freeze fix. Co-authored-by: Cursor <cursoragent@cursor.com>
The reasoning docs still described the old global none|low|medium|high ladder and a `medium` default, and the Cursor docs still described the tool-only integration that shipped enabled. Both now match what the code does. Configuration gains a Reasoning section: Auto sends no field at all, values are opaque provider strings that are never normalised across providers, Off appears only where reasoning can actually be disabled, and the per-provider table records how Anthropic, Gemini, OpenAI/Codex and OpenRouter each spell it. The Cursor entry is documented as disabled by default, with the repository/ref rules and the warning that a cloud VM cannot see dirty files or local-only commits. Tools documents direct Cursor mode: exact variant selection with no synthesised combinations, the approval card and its immutable retained request, local Stop versus approval-gated remote Cancel, what keeps a follow-up on the same agent, and the five-image/15 MiB attachment limit. Verification states the metadata-only rule outright, adds a deny-at-the- approval-card dashboard pass that costs nothing, and records how to strip credential variables for a hermetic suite. Co-authored-by: Cursor <cursoragent@cursor.com>
Two documentation claims were wrong in a way that matters for trust.
"No request reaches Cursor before you approve" was false: the composer
cannot draw the model list, the variant options, or the approval card
without reading /v1/me and /v1/models first. The verification pass then
contradicted itself by asking the reader to confirm the live catalogue
loads in steps 2-3 and calling the whole pass request-free in step 5.
Both now state the guarantee that actually holds — no create-agent,
create-run, or cancel is sent before its own approval — and say plainly
that read-only metadata GETs do happen earlier and cost nothing.
The design said a model with no variants uses an empty parameter list,
which contradicts the exact-authoritative-variant rule in the same
section and the implementation: defaultCursorVariant returns null,
cursorModelSelectable is false, cursorTargetFromModel yields no target,
and the picker renders the row disabled. `{"params": []}` would be a
selection Cursor never offered. The design now matches, and separates
the one legitimate no-parameter case — the cursor_agent tool omitting
model_params so Cursor applies its own default, which is an absent field
rather than an empty array and is not reachable from the composer.
Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
Author
|
Ditutup atas permintaan pemilik repo. Pendekatan Cursor Cloud Agent dinilai tidak cocok untuk alur kerja lokal; arah integrasi akan dipertimbangkan ulang. Commit tetap tersimpan di branch feature/cursor-agent-provider-impl. |
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.
Follow-up to #25, which merged the Cursor Cloud Agent integration. This adds
the two things that integration was missing: reasoning controls that match the
model actually selected, and a way to run a Cursor agent from the composer
without asking an Antares model to manufacture a tool call.
origin/mainis merged in (22392df) rather than rebased, so main'sfix(config): ship the Cursor provider disabled by defaultand itsReasoningBlock freeze fix are preserved rather than reverted by this branch's
older base.
Summary
Model-aware reasoning. The global
none|low|medium|highenum is replaced bya capability resolved from the selected provider and model. Auto is the new
default and sends no reasoning field at all. Values stay opaque provider
strings —
extra-highis never rewritten toxhigh. Off is offered only wherereasoning can genuinely be disabled. Adapter request bodies were corrected so
every displayed choice has its advertised effect: Anthropic adaptive thinking
with
output_config.effort, GeminithinkingLevel(whereminimalmeansMinimal, not Off), OpenAI/Codex nested vs flat effort, and OpenRouter sending an
explicit disable instead of omitting the field. Preferences are stored per
provider/model, so a value cannot leak across a model switch.Direct Cursor mode. Cursor models appear in composer search as a separate
execution target; selecting one never changes
model.providerormodel.default.POST /api/chat/cursorruns the turn through a sharedinternal/cursorrunservice and streams over the existing SSE envelope. Thelive catalogue is authoritative: Antares copies one concrete variant's whole
param array (including params Cursor does not publish as user-facing, such as
cyber) and never synthesises a combination Cursor did not return.Safety boundaries. Start, follow-up, and cancel each require an explicit
approval even under
approval_mode: auto, and the retained request — not thedisplayed card — is what executes. Create requests are never auto-retried; an
ambiguous failure is recorded as ambiguous instead. Local Stop only detaches and
can only win before the non-idempotent POST; remote Cancel is a separate
approved action. Runs survive browser detach and daemon restart via persisted
agent/run IDs, with reset-and-replay rather than duplicated text on resume.
API keys are redacted from every error, event, log, and persisted field.
Test plan
go test ./internal/llm ./internal/agent ./internal/config ./internal/cursor ./internal/cursorrun ./internal/approval ./internal/tools ./internal/store ./internal/server ./cmd/antares -count=1go test -raceover the same packages — cleanmake check(vet, fullgo test ./..., typecheck, 154 web tests) — greenmake smoke— all 16 dashboard routes cleanGET /v1/meandGET /v1/models(34 models, all with variants; confirmed the hiddencybervariant param exists upstream). No agent, run, follow-up, orcancellation was created.
no upstream request before approval, deny sends nothing, approval payload
bounded/redacted/immutable, and local interrupt does not cancel remotely.
Suites were run with credential variables stripped so nothing silently went live.
Known issue (pre-existing, not from this branch)
internal/mcpis flaky under full-suite parallel load —TestStdioRoundTripand
TestRefreshReplacesToolsAndReadinesscan exceed their 30s subprocessdeadline. Reproduced on unmodified
origin/main(1/9 full-suite runs) as wellas here, in a package this branch does not touch. Left alone rather than
folded into this PR.
Made with Cursor