Repository navigation
[Local|Release] Release desktop 2.2.3 - #2307
Merged
Merged
Conversation
…redentials before every build
A failed sign-in's poll carried only a sentence, in English, so the screen could not say why in the user's language. The API now sends a code beside it, SignInFailure, one per cause it already told apart: the provider's refusal, declined or other, a timeout, a host not allowed, the sign-in server unreachable, a server that could not list its tools, a plugin disconnected or connected under another name meanwhile, an unfinished flow, and anything else. The sentences and the browser's callback page are unchanged.
A failed plugin sign-in read "The sign-in didn't finish" whatever the cause. The screen now has its own words for each code the API sends, wherever a sign-in is shown: Connect on a listed plugin, Add custom plugin and Sign in again. A code it has no words for shows the API's own sentence; once the API has forgotten the sign-in, the old sentence stays.
The ten sentences of sign-in-failure-text.ts in the nine other languages, following the translate skill's tone per language and its glossary.
plugins.md describes the code where it describes the poll, the screen and the tests, and its Known gaps line for the unshown reason is gone. localization.md lists the sign-in reasons among the backend texts that carry a code.
Once the API has forgotten a sign-in, Sign in again says "The sign-in didn't finish." without "Try again.", as the other surfaces do.
The Word builder gave every ordered list the template's List Number style, and every paragraph in that style shares one numbering. A second list in the same document went on from where the first stopped (4, 5, ...), and a numbered list nested under a later bullet went on from the one under the earlier bullet. The PDF builder already starts each list at 1. Each ordered list now gets its own numbering on the style's list, with a start override of 1. Removes the Known gap from docs/architecture/studio.md.
# Conflicts: # docs/architecture/localization.md # docs/architecture/plugins.md
Four of the screen's sentences said what the API does not know, or dropped what it does: the host it was not allowed to contact, and the name the server is already connected as, are now sent as raw values beside the code and placed in the sentence, as an install's disk refusal sends its sizes. A sign-in that finds its plugin gone says "removed", which is what Remove does; Disconnect keeps the row. And "declined" is the provider's word, not the user's: access_denied also covers the authorization server refusing. A provider refusal other than access_denied is covered by a test now.
Measured: Claude Code returns output_format answers through its own StructuredOutput tool, valid against the schema in every run, and needs more than one turn. A Claude plan model can therefore take the chat router step and Studio's schemas; the open question is closed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
locate_claude searches PATH, then where Claude Code's installers put it on macOS, Linux and Windows; on Windows only a claude.exe counts, since the Agent SDK refuses npm's claude.cmd. read_status runs --version and auth status and reports not_installed, too_old, signed_out or ready, treating a hang or unreadable answer as no answer. Tests run a stand-in claude on every system. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
launch_options is the one place a run's options are made: the user's binary, no user settings or MCP servers, verbatim prompts, the default permission mode, no API key, no telemetry, and SurfSense's proxy, none of which a caller can override. failure_of turns Claude Code's messages into the sign-in, plan-limit and egress errors SurfSense already shows. Pins claude-agent-sdk 0.2.168. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ClaudeRun is the only code that touches the Agent SDK's client: send a message and stream what follows, interrupt, list models, close. Its tests run the user's real Claude Code under the new opt-in claude_code marker, skipped unless SURFSENSE_CLAUDE_CODE_TESTS=1 and Claude Code is signed in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Revision 0031 widens provider_connections.auth_kind to accept claude_plan, the only schema change the Claude plan needs: it stores no credential, and what Claude Code reports is read live. SQLite cannot alter a CHECK, so the table is rebuilt from its own stored definition, with every remote selection kept aside and put back, since dropping the table cascades through selected_models. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /llm/connections/claude reads Claude Code's state live: not installed, too old, signed out, or ready with its account and plan. POST makes the claude_plan connection once Claude Code is ready and api.anthropic.com allowed, one per install. The connection serves chat only, lists Claude Code's models by their full ids so the manifest's Anthropic capabilities apply, and can only be renamed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ClaudePlanProvider is a Generator over Claude Code with every tool off: SurfSense's system prompt in place of Claude Code's, text and thinking streamed, a schema answered as structured output, the newest message's images sent beside it, and plan, sign-in and run failures raised as the errors chat already shows. A warm run per conversation, at most four, answers the next message from what it holds; an edited conversation, a busy run or another model starts from the whole conversation instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A claude_plan connection now resolves to the Claude plan's generator, over one pool of warm runs for the API process, so chat, thread titles, the chat check and Studio answer through the user's own Claude Code. The API closes every warm run when it stops. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every Claude Code run goes through a loopback proxy the API serves, which tunnels only api.anthropic.com, for model calls, and platform.claude.com, where Claude Code refreshes its sign-in, each while its egress is on. No run starts without the proxy. Connecting, listing models, choosing one and generating all require both hosts, named in one refusal; a host turned off mid-reply ends the run at Claude Code's first retry rather than its tenth. The proxy reuses the plugin proxy's tunnel with a host list of its own, so opencode never reaches Anthropic and Claude Code never reaches a plugin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An nvm folder's version is read by splitting vX.Y.Z, with a folder that is not a version ranked below every one, and the real chat test reads the tunnelled hosts from the proxy's log records rather than its text. Claude Code's own version string keeps its regex: it is another program's output, and a pattern found anywhere survives its changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The SDK's wheel carries a 241 MB Claude Code that PyInstaller leaves out, since nothing asks for the SDK's data. A packaging test now reads every spec and fails when any collector names the SDK, and the frozen API test fails when a claude binary is in the bundle. Measured: the SDK adds 0.22 MB to the frozen API and no Claude Code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The SDK's wheels carry a 241 MB Claude Code; its source package carries none. no-binary-package makes uv build the SDK from source, so no venv, CI cache or frozen app holds that copy, and pnpm dev's predev does it through uv run with nothing done by hand. A unit test, run on every system in CI, fails if the installed SDK ever carries one again; it replaces the spec-reading packaging test, which guarded a file that is no longer installed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Claude plan raised the ChatGPT plan's errors, so chat told its users to sign in to ChatGPT in Model setup. Its sign-in and limit errors are now Claude subclasses of those, sorted first into claude_sign_in and claude_limit, and a missing or signed-out Claude Code answers the model check with claude_sign_in_required. Code that catches the parent errors is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A "Use your Claude plan" row beside the ChatGPT one opens a dialog that reads Claude Code's state live and says what to do: install it, run claude and sign in, or update it, with Check again; once ready, Add Claude models asks for both of its hosts in one egress question and makes the connection. The egress prompt now asks about every host a refusal or askEgress names at once, wording a Claude plan's two by what each gets, which also makes ChatGPT's sign-in one question. Editing a Claude plan only renames it, chat names its failures in Claude's terms, and Electron opens Claude Code's install guide and no other page of that site. Every new string is in all ten languages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The architecture doc and the route's docstring still said the hosts were asked about one by one, which the multi-host egress prompt changed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PUT /egress takes every destination one answer covers and switches them
together: each is checked before any is written, and the request's one
transaction commits them all or none, so a failed Allow leaves no host
on. It replaces PUT /egress/{destination}; Settings > Network sends a
list of one, and every consent question, the frontend's only caller of
both, now makes one request. Recording consent changes; checking it,
egress.require() on every call, does not.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ADR 0058 records which hosts one egress question names: every host a user action may reach, including hosts its background work reaches, which no dialog could ask about later, and never a host SurfSense has not named. egress.md says the same under asking for consent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…its hosts The status check now runs through the Claude proxy with the environment every run gets: it contacts nothing today, but a later Claude Code that checks its sign-in online is held to consent too. The proxy logs a refused host as Claude Code's, not opencode's, so a host a new release tries on its own shows at once, and a claude_code test fails when the installed binary refreshes its sign-in on a host SurfSense does not allow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Claude Code run stopped at once when a host it needs was turned off mid-reply, but the chat called it unknown: chat had an error kind for an egress refusal only before a reply. It now sorts EgressDeniedError into egress_disabled, which says the host was turned off in Settings > Network and to allow it there, in every language. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The connection doc keeps Claude Code's proxy to Anthropic's two hosts, while the agent doc gave Claude Code each plugin server, which needs plugin hosts on that proxy. Record it as an open question for the agent spikes, preferring plugins through SurfSense's tool endpoint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dry-run-signing # Conflicts: # docs/architecture/packaging.md
… them Advanced settings and Edit connection hold rows of a header name and a hidden value, the first named Authorization with its value hinted "Bearer your-key", in place of a key with a Bearer, Basic or custom header switch. Only rows with a value are sent. The switch, its draft and the Basic encoding are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The own-app dialog lays out the steps it takes: register an app with the provider, give it the callback address, copied in one click with a note where a developer build's address moves, and paste the client ID and secret, with scopes folded away. - A plugin's own app is chosen when it is added, from Advanced settings, as Claude Code takes it; the menu item on every OAuth plugin is gone. - A provider whose metadata names no registration, as GitHub's, is asked for the user's own app at once, with no registration sent only to fail. Refusals SurfSense handles log one line, not the SDK's traceback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… is gone The SSE client raises a refused stream as the HTTP library's error, which SurfSense's MCP client never kept, so any status an SSE stream answered was lost. It is now kept, and when neither transport answers, an SSE address that answered anything but "nothing here" is the one reported: a key it asks for, a server down, or 410, an address its server retired, as DeepWiki did its /sse, reported as plugin_gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A sign-in listed the server's tools over Streamable HTTP alone, so an SSE server whose POST asks for a sign-in, as Asana's, Atlassian's and PayPal's /sse do, was signed in and then refused. A finished sign-in now lists as adding a server does, Streamable HTTP then SSE, and records the transport that answered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ails Connect from Add custom plugin, Browse, a public page or search saves an OAuth plugin and opens its details in Yours, where the sign-in runs; the browser opens at once. A provider taking only registered apps, or a registry entry declaring user_client, keeps the plugin as "Needs an OAuth app" and its details set one up. A saved Browse entry shows its status as Yours does, opening its details. A plugin never signed in reads "Not connected" with Connect, nothing to disconnect, and the user's own OAuth app is offered only where it is needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts row A plugin's details show one action beside its menu: Connect while it is not connected, whatever that takes, and Disconnect once it is. Edit connection, Edit headers, Change OAuth app and License settings move into the menu, and the status line holds no buttons. Every plugin in Yours not connected, of any type, shows the same Connect in its row with why beside it in grey; an OAuth sign-in started there runs in the plugin's details. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plugin's details show its state alone, with at most one reason behind an info button beside it: why it needs an OAuth app, the registry's reason for a delisted plugin, or "Not used" for a third-party plugin while those are off. Notes about its tools sit with its tools. The plugin's icon there is as tall as its name and type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ormEvent Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While third-party plugins are off, every route that reaches a plugin's server refuses one SurfSense does not publish with 403 third_party_off before sending anything, and turning the switch off ends any third-party sign-in under way. Yours says "Third-party plugins are off" with no Connect, a plugin's details say "Not used" with only Remove, and Add custom plugin holds Connect beside the same switch and consent as Browse. The in-place sign-in fallbacks for screens outside Settings › Plugins, which none are, and the test stand-in's probe route, which the API no longer has, are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves dev's sign-in failure codes (#2267) and its OCR ADR (#2295) against this branch: - dev's ADR 0062 keeps its number; this branch's become 0063, 0064 and 0065, with every citation updated. - A failed sign-in says why on the plugin's details, where this branch follows it, in dev's words for its code. - dev's refusal tests keep the plugin waiting to sign in, as this branch saves it first, and its failure tests read the plugin's details. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adding a server by URL now waits for the third-party switch, so the plugin route tests start with it allowed, and the few about it being off keep the default with a third_party_off marker. The agent tests allow it before they connect, as the screen does. A custom plugin held by the switch is now shown by turning it off once added, since none can be added while it is off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…igned in from its row Adding one no longer starts a sign-in: it answers 201 once the authorization server's hosts are allowed, and Connect on its row registers and opens the sign-in. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(plugins): connect any remote MCP server, through SurfSense's gateway
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ified chore(registry): mark Notion as verified
HomeButton now presses like the desktop app's button: a 0.95 scale that springs back, with a static option that presses by colour for wide buttons and a bare variant for pills and tiles that bring their own look. Marketing pages that used the dashboard Button now use HomeButton. The download buttons in the navbar and the homepage CTA are plain rounded HomeButtons, without the arrow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
44px tall with 15px text, so the button reads as the page's main action. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
feat(web): press feedback on marketing buttons
…rt their checks The accept job no longer needs a personal token: it acts as github-actions[bot] and runs plugins-registry.yml on the submission branch itself, live included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…thub-token ci(registry): open submission pull requests with GITHUB_TOKEN and start their checks
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedToo many files! This PR contains 477 files, which is 177 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files. ⚙️ Run configuration
⛔ Files ignored due to path filters: 1
📒 Files selected for processing: 477
You can disable this status message by setting the
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This branch was successfully deployed
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
Desktop release 2.2.3:
devintomain. The tagv2.2.3goes on this pull request's merge commit (surfsense_local/RELEASE.md).What's in it since 2.2.2:
GITHUB_TOKEN(ci(registry): open submission pull requests with GITHUB_TOKEN and start their checks #2306).runtime_busy(fix(local): sort llama.cpp's shared-cache overflow as runtime_busy #2299); each numbered list in a Studio Word file counts from 1 (fix(local): count each numbered list in a Studio Word file from 1 #2287).Why
Ship the desktop app's 2.2.3, with the signed plugin list, to
main.Fixes #
How to test
Before merging:
dev. Change any cell that changed in everyREADME.<locale>.mdtoo.After merging:
v2.2.3on the merge commit, open the draft release with--notes-start-tag v2.2.2, and wait for Mac, Windows, Linux andlegacy-update-bridge.mainon Coolify: the license server (APP_RELEASE_VERSIONis compiled in, so a redeploy, not a restart), the plugin host and the plugin registry. The website deploys itself on Vercel.🤖 Generated with Claude Code