- Updated the default proxy mode in `api.ts`, `ProxySettingsCard.tsx`, and `SettingsPage.tsx` to "default".
- Adjusted related translations in `catalog.json`, `en-US.json`, and `zh-CN.json`.
- Modified the `ProxyMode` enum in `settings.rs` to reflect the change from "system" to "default".
- Enhanced proxy handling in the server code to support the new default mode.
- output.rs: the read_lints test helper (#398) builds ToolCall without
the argument_error field added in d83e14a, so the lib-test target
does not compile
- connect_wire.rs: cursor::router takes NetworkClients since 2dad593
- runtime.rs: clippy::nonminimal_bool from 5cdf642
- update/mod.rs: clippy::needless_return in the Windows arm, ddaa61c
A provider that reuses a tool call id across two rounds of one run
wedges the run permanently. `ToolDispatcher::start_batch` skips any call
whose id is in `ToolBatchState::completed`, so the second call is never
dispatched and never produces a `ToolCompletion`, while
`tool_round::execute` blocks waiting for `calls.len()` results with no
timeout on that path. The client sees the tool call appear and then
nothing: no completion, no further output, no end-stream frame.
The `completed` set is built once per run and never cleared, so it is
run-scoped. A tool call id is only unique within a round, which the
schema already states as `UNIQUE (round_id, call_id)`; the sibling
runtime completed-map is likewise already cleared per round at
output.rs:615.
Clear `completed` when `ExecuteToolRound` begins a new round, tracked
independently of `active_round` so it does not depend on the order in
which ToolRoundStarted and ExecuteToolRound are observed. Replaying a
round still skips the calls that round already committed.
The practical trigger is openai_chat.rs:196, which synthesizes
`call-{index}` from a per-stream index when a provider omits tool call
ids, so `call-0` recurs on every model call. That file is left alone
here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Modified the content security policy in `tauri.conf.json` to include `https:` in the `img-src` directive, enhancing security by allowing images from secure sources.
- Changed the `ADS_ENDPOINT` from a local server URL to the production URL for ads.
- Commented out the local server URL for clarity and future reference.
- Added `app_version` field to `ControlService` and updated its initialization to include the app version.
- Modified the `ADS_ENDPOINT` to point to a local server for development purposes.
- Refactored conversation command and output handling to utilize a new `RunFinish` enum for better state management.
- Enhanced the conversation runtime to handle queued user messages after a turn has ended, ensuring smooth transitions between turns.
- Added tests to validate the new behavior of queued messages and transport handling.
- Updated the `append` function to include a flag for replacing closing requests, improving request handling.
- Refactored the `run_sse_handler` and `bidi_handler` functions to utilize a new tracing mechanism, enhancing observability.
- Introduced a new `trace_outcome` function to standardize tracing outcomes for requests.
- Removed the `CursorTraceRecorder` in favor of a new `CursorTraceService` for better performance and non-blocking behavior.
- Added tests to validate the new tracing functionality and ensure correct behavior during request processing.
- Introduced a new module for managing process resource limits, specifically for raising the open file limit on Unix systems.
- Added a `NetworkClients` struct to handle reusable outbound HTTP clients, improving network request management.
- Updated various components, including `ControlService` and `CursorProxy`, to utilize the new network client structure for better client handling.
- Enhanced the API router to accept network clients, ensuring consistent client usage across different services.
- Added tests to validate the integration of network clients and resource limits functionality.
- Introduced `UsageSnapshot` event to track token usage during conversation runs.
- Updated `RunEngine` to emit usage snapshots, providing better visibility into token consumption.
- Refactored compaction logic to utilize a new `compaction_estimate` function for improved token budget management.
- Added tests to validate timeout constants for blob synchronization and ensure correct behavior of usage tracking during compaction.
- Added a new test to validate the projection of reasoning response items to valid input items in the Codex API.
- Introduced `CallRecorder` to track network requests and responses during plugin interactions.
- Updated the `PluginRegistry` and `PluginWorker` to support call recording, ensuring that reasoning items are correctly processed and recorded.
- Refactored the `responses_input` function to handle reasoning items more effectively, improving the overall response handling logic.
- Added tracking for interaction events in the `Output` struct, including `summary_started` and `token_delta`.
- Updated the `run` function to push relevant interaction events to the `interaction_events` vector.
- Enhanced the automatic compaction test to verify the immediate reset of cursor usage and the correct logging of interaction events.
- Introduced `ContextUsageAnchor` struct to track context input tokens and message count for conversations.
- Updated token estimation functions to utilize the context usage anchor, enhancing accuracy in estimating tokens for projected messages.
- Refactored compaction logic to incorporate context usage anchor, allowing for more efficient management of token budgets during model runs.
- Added tests to validate the behavior of the context usage anchor across different scenarios, including model switching and message additions.
- Updated the `merge_usage` function to include `total_tokens` in the usage merging process.
- Implemented logic to calculate `total_tokens` based on the sum of `context_input_tokens` and `output_tokens`.
- Added a new test to verify that streamed usage correctly includes cached input in the total token count.
- Removed the `retry_count` field from `ProviderConfig` as it is no longer needed.
- Introduced `argument_error` field in `ToolCall` to capture errors related to tool arguments.
- Updated various components to handle argument errors more gracefully, including in the `ToolDispatcher` and `ConversationOutput`.
- Enhanced tests to validate the new error handling and ensure proper functionality of tool calls.
- Added `estimate_context_tokens` function to calculate provider-visible context size based on prompt specifications and projected messages.
- Updated `CheckpointBuilder` to record estimated context tokens during message processing.
- Refactored compaction logic to utilize the new token estimation, ensuring proper context management during model runs.
- Introduced tests to validate context estimation and compaction behavior under various scenarios.
ReadLints accepts an array of paths, but codec::request encodes only
paths[0] into the DiagnosticsArgs exec, and the exec protocol cannot
carry more than one path. Nothing downstream mentions the drop: for
{"paths": ["a.ts", "b.ts", "c.ts"]} the model is handed
"No diagnostics found in a.ts" with is_error false, so it concludes
b.ts and c.ts are clean when neither was ever opened. The existing
truncation notice cannot catch this, since it compares total_diagnostics
against a per-file diagnostics count.
Name the unread paths in the model-facing result, using the bracketed
notice convention already in this file and the ToolCall that output()
is already given (as task() and render::read() already use it).
Single-path calls are unchanged.
This does not close the capability gap. A real fix fans out one
DiagnosticsArgs exec per path and aggregates the results into the
repeated FileDiagnostics that ReadLintsToolSuccess already defines,
which needs multi-exec reservation and a completion barrier because
take_exec drops the pending entry on the first result. That is a
separate change; this one only stops the silent misinformation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`release.yml` only runs on `v*` tags, so nothing verifies a commit
before it lands on `main`. `main` is currently red on three of the five
gates `make check` defines, which is the drift this is meant to catch.
Adds a single job running the three Rust gates verbatim as the Makefile
spells them:
cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --all-targets
Conventions follow release.yml so the two workflows stay consistent:
ubuntu-22.04, the same apt packages (the workspace includes
apps/desktop/src-tauri, so even `cargo check` needs webkit2gtk),
dtolnay/rust-toolchain@stable and Swatinem/rust-cache@v2 with the same
`. -> target` workspace key.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`make check` currently fails on main before any change is made: one
`cargo fmt --all -- --check` diff and four `cargo clippy --workspace
--all-targets -- -D warnings` errors. All five are pre-existing and
none of them change behaviour.
- `server/tests/knowledge_rules.rs:125` — rustfmt wants the long
`assert!` split across lines. Applied `cargo fmt --all` verbatim.
- `server/src/plugin/data.rs:206,215` — `path` is only read under
`#[cfg(unix)]`, so every other target sees an unused binding. Added
a `#[cfg(not(unix))] { let _ = path; }` arm, matching the
`let _ = error;` idiom already used at line 189 of the same file.
Windows behaviour is unchanged: these helpers stay no-ops there.
- `server/src/provider/openai_responses.rs:158` — `collapsible_match`.
Applied clippy's own suggestion (move `thinking_open` into a match
guard). The match ends in `_ => {}`, so a failed guard falls through
to a no-op exactly as the inner `if` did.
- `server/src/store/models.rs:327` — `items_after_test_module`. Moved
`optional_u64` and `to_i64` above `mod tests`; the bodies are
untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`Usage::add_assign` folds every field through
fn sum(left: Option<u64>, right: Option<u64>) -> Option<u64> {
left?.checked_add(right?)
}
so `sum(Some(900), None)` is `None`, not `Some(900)`. Adding a record that does
not report a field therefore does not leave the running total alone - it wipes
it.
Both accumulators are per-turn and run over every provider call in the turn:
`run/engine.rs::accumulate_usage` (the compaction call plus each model cycle)
and `cursor/conversation/output.rs::turn_usage`, which is what `turn_ended`
reports to Cursor.
Providers report these fields inconsistently *across calls of one turn*, which
is all it takes. `openai_usage` reads `cache_read_tokens` from
`prompt_tokens_details`, an object most OpenAI-compatible gateways omit until
the prompt cache warms: call 1 (cold) yields `None`, call 2 yields
`Some(1024)`, and the turn reports `None`. The same happens to
`reasoning_tokens` when only some cycles reason, and to `total_tokens` on
gateways that omit it from the streaming usage chunk. Any tool-using turn makes
more than one call, so this is the normal case rather than an edge case.
Treat an unreported count as zero and keep the field unknown only when neither
side reported it. `checked_add` also becomes `saturating_add`: with the new
rule, silently turning an overflow into `None` would be the same erasure by
another route, and token counts never approach `u64::MAX` anyway.
`context_input_tokens` is untouched.
Before (with `left?.checked_add(right?)` restored):
cargo test -p cursor-server --lib model::observability
-> 2 failed: assertion `left == right` failed: left: None, right: Some(900)
After:
cargo test -p cursor-server --lib model::observability -> 3 passed
`truncate_text` looks for a fixed point where the kept prefix length equals the
byte count printed in its own truncation notice. Two things go wrong when the
limit is small, and both are reachable because two call sites pass a *remaining*
budget rather than a constant:
* `gate_grep_content` -> `truncate_text("Grep", .., budget.content_bytes)`
* `gate_mcp` -> `truncate_text("MCP text", .., remaining_text)`
1. The loop can spin forever. `notice.len()` grows with the decimal digit count
of `shown`, so `kept.len()` alternates between two values across a power-of-
ten boundary and never equals `shown`. With `tool_name = "Grep"` this happens
at `limit` 78 and 170; with `"MCP text"` at 82 and 174. The conversation task
then spins at 100% CPU and the turn never completes. `truncate_middle` in
`model/tool_result_replay.rs` already guards against exactly this.
2. When the notice itself does not fit, `available` saturates to 0 and the
function returns the ~68 byte notice alone, i.e. *more* than `limit`.
`gate_grep_content` then evaluates `budget.content_bytes -= <68 bytes>` on a
budget of at most 68, which panics with "attempt to subtract with overflow"
in debug/test builds and wraps in release, silently disabling the 32 KiB
content cap for the rest of the result.
Both are ordinary Grep results away: 16 matches of ~2 KiB leave a two-digit
remainder of the 32 KiB budget, and the next match then hits the small-limit
path.
Fix: return a plain prefix when the notice cannot fit, and stop as soon as the
kept length repeats a previous value. The reported byte count is then off by one
at most in that rare oscillating case, and the result is guaranteed to be at
most `limit` bytes. Every constant-limit call site is unaffected: their notices
always fit and their limits do not oscillate. `budget.content_bytes` now uses
`saturating_sub`, matching every other subtraction in this file.
Verified against the unfixed function:
cargo test -p cursor-server --lib gate::tests::truncate_text_never_exceeds_its_limit
-> FAILED: limit 1 produced 67 bytes
cargo test -p cursor-server --lib gate::tests::grep_content_gate_survives
-> FAILED: panicked at gate.rs:261: attempt to subtract with overflow
cargo test -p cursor-server --lib gate::tests::truncate_text_terminates
-> never returns (killed after 45s)
cargo test -p cursor-server --lib gate::tests::grep_content_gate_terminates
-> never returns (killed after 60s)
After: cargo test -p cursor-server --lib tools::tool_call_result::gate -> 4 passed