Commit Graph
390 Commits
Author SHA1 Message Date
John Choi fa840c741c fix(desktop): sync session titles changed on the web (#14935)
* fix(desktop): sync session titles changed on the web

* fix(desktop): preserve unopened session renames during discovery

* fix(desktop): keep title refreshes ahead of React commits

* fix(desktop): limit refreshed title reconciliation to cloud sessions

* fix(desktop): keep every renamed title ahead of refresh completion

* refactor(desktop): reuse session history lookup for chat header
2026-10-08 12:16:22 -07:00
Saoud Rizwan 417526f19c chore(desktop): release v0.0.45 2026-10-07 22:57:29 -07:00
Saoud RizwanandSaoud Rizwan 3debb849a7 fix(desktop): let the composer model name use the available row width (#14936)
Drop the fixed max width on the composer's model picker so long model
names show in full when the settings row has room, truncating only when
it runs out. Keep the settings row on one line instead of wrapping,
reserve room for the model and thinking controls so a long branch name
can't squeeze the model name out, and give the conversation layout a
shrinkable grid column so the composer row truncates instead of being
clipped at narrow widths.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-07 21:23:33 -07:00
Saoud RizwanandSaoud Rizwan 49276bcf99 fix(desktop): cut SSH logins from the git branch label poll (#14934)
get_git_branch only needs the current branch, but it ran listGitBranches,
which also executes git for-each-ref. Over an SSH environment each command is
its own ssh login, so the 5s branch label poll opened two fresh SSH sessions
every 5 seconds against the remote host for as long as it was connected.

Run only git branch --show-current for the label, and poll it every 30s for
SSH environments (5s stays for local). Branch switches made through the picker
still refresh immediately, as do focus and visibility changes.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-07 20:04:45 -07:00
Saoud RizwanandSaoud Rizwan faf05ef067 fix(desktop): stop UPX-packing the Linux SSH remote helpers (#14928)
* fix(desktop): stop UPX-packing the Linux SSH remote helpers

Since the Bun 1.4.2 upgrade (#14631) the compiled helper lazily reads its
embedded module payload back from its own executable at runtime. On a
UPX-packed binary those reads return NUL padding, so the helper starts but
every --remote-hub-ensure fails with "SyntaxError: Invalid character:
'\0'" and SSH remotes cannot start (desktop 0.0.38 through 0.0.44).

Ship the helpers uncompressed again and drop the upx installs from the
publish workflow. Bundles grow by roughly 175 MB until the CLI-managed Hub
replaces the helper binary.

Fixes #14815
Fixes #14781

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* ci(desktop): smoke-test the bundled SSH remote helper before publishing

Run each Linux job's native remote helper through --remote-hub-ensure and
--remote-hub-stop against an isolated CLINE_DIR and discovery record. The
UPX regression started the helper fine but failed on exactly this path,
and nothing in the publish workflow exercised it.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-07 15:54:57 -07:00
Mikołaj Kondratek c747d494a2 chore(llms): align the AI SDK on one release line (ai 7.0.120, provider-utils 5.0.50) (#14862)
`ai` had stayed at 7.0.79 while single `@ai-sdk/*` packages were bumped on
their own. Each such bump brought its own `@ai-sdk/provider` /
`provider-utils`, whose types no longer unify with the ones `ai` pins, so
the hand-off to `ai` needed casts. Bumping `@ai-sdk/openai` alone for #14649
would have added two more.

Move `ai` and every `@ai-sdk/*` package to the releases built on
`@ai-sdk/provider-utils` 5.0.50 (all published 2026-09-28): ai 7.0.120,
gateway 4.0.98, anthropic 4.0.67, google 4.0.84, google-vertex 5.0.97,
mistral 4.0.53, openai 4.0.79, openai-compatible 3.0.58, otel 1.0.120,
react 4.0.123, provider 4.0.19. Bedrock 5.0.98 is already on this line and
stays. One copy of each is left in the lockfile; the only other `ai` is
Langfuse's own nested copy, as before. No casts are added.

This also fixes #14649: `@ai-sdk/openai` 4.0.79 knows which GPT-6 models
can turn reasoning off and drops the setting for the rest, so commit
message generation (which always turns reasoning off) and tasks with
reasoning set to "none" stop getting a 400 from GPT-6 Astra and 6.1 Sol.
GPT-5.x and GPT-6 Sol/Luna still get "none".

It fixes #13203 the same way: `@ai-sdk/anthropic` 4.0.67 leaves
`thinking` out for Claude models that can't turn thinking off (Fable 5,
Opus 5.5) instead of sending `thinking: {type: "disabled"}`, which they
reject. Other Claude models still get `disabled`.
2026-10-07 11:45:22 +02:00
Saoud Rizwan 66ff3731cf chore(desktop): release v0.0.44 2026-10-06 23:59:06 -07:00
Saoud Rizwan f832e8169d fix(desktop): let keyless configured providers (local models) start sessions (#14879)
* fix(desktop): let keyless configured providers start sessions

The webview's pre-flight credential gate demanded an API key for every
provider that was not OAuth or local-auth, so a local OpenAI-compatible
endpoint (LM Studio, vLLM, Ollama) migrated from VS Code or added via
Settings failed every turn with "Missing API key" even though the
sidecar already reported it as configured via base URL + model.

Carry the catalog's host-computed `configured` readiness into the chat
config and skip the API-key requirement when the host accepts the
provider, matching the runtime's own keyless handling.

* fix(desktop): gate sessions on the shared apiKeyOptional provider fact

Address review: instead of a desktop-only providerConfigured readiness
flag threaded through the chat config, make the credential requirement
explicit in the shared provider contract.

- @cline/shared: GatewayProviderMetadata.apiKeyOptional, mirrored into
  ProviderAuthInfo via resolveProviderApiKeyOptional (same pattern as
  localCliCommand -> localCli).
- @cline/llms: declare it on openai-compatible, ollama, lmstudio, litellm,
  vertex, and bedrock.
- @cline/core: listLocalProviders carries the fact in ProviderListItem.auth;
  user-added endpoints registered from providers.json / models.json declare
  it too (their key is already optional in the desktop form).
- desktop: resolveCredentialError consults auth.apiKeyOptional and no
  longer needs the extra config field.
2026-10-06 18:48:52 -07:00
b2c7148cd9 fix(desktop): render Mermaid diagrams inline (#13721)
* feat(desktop): render Mermaid diagrams in chat

Render model-authored Mermaid fences as interactive SVGs in Desktop without depending on model image-input support.

- extract the strict lazy Mermaid DiagramPlugin into @cline/ui
- keep shared defaults diagram-free and add explicit opt-in controls
- enable copy, download, fullscreen, and pan/zoom in Desktop
- preserve initial bundle size through dynamic Mermaid imports
- cover lazy loading, strict security, failures, and streaming fences

Refs #13568

* test(desktop): harden Mermaid rendering coverage

* test(desktop): stub scrollTo for streamdown 2.6 in Markdown interaction tests

streamdown 2.6 (pulled in by main) scrolls streaming code blocks with element.scrollTo, which jsdom does not implement. Stub it the same way chat-messages.test.tsx and channels-view.test.tsx already do.

* fix(ui): stop Mermaid diagram links from bypassing host link policy

Mermaid's `securityLevel: "strict"` blocks script execution and dangerous URL
schemes, but still renders a `click <node> "https://…"` directive as a live
`<a xlink:href>` anchor inside the SVG. Streamdown injects that SVG with
`dangerouslySetInnerHTML`, so those anchors never pass through the `a`
component a product supplies — bypassing the deceptive-link confirmation and
the external-open routing that ordinary Markdown links go through, with no
rel="noreferrer", and letting a click navigate the desktop webview away from
the app itself.

A diagram label is authored independently of its destination, so these links
are deceptive by construction: the desktop app's own isDeceptiveLink rule
would flag every one of them.

- neutralize rendered diagram links in @cline/ui so they cannot navigate on
  their own, preserving an http(s) destination on data-cline-diagram-href
- reattach them in Desktop to the existing confirmation modal, which shows the
  real destination before anything opens
- recover the anchor from pointerdown: Streamdown's pan/zoom surface takes
  pointer capture, so a real click is retargeted away from the anchor; a press
  that moves more than a few pixels is treated as a pan, not a link click
- drop non-http(s) destinations rather than surfacing them, so a renderer
  change cannot widen what the app will open
- assert the packed Mermaid control shape instead of a "not false" check that
  also accepted undefined or an empty object

Verified against real Mermaid 11.16.1 in a browser: script, event-handler,
iframe, foreignObject and javascript:/data:/vbscript:/file: payloads are all
neutralized by strict mode, and `%%{init}%%` / YAML frontmatter cannot
downgrade securityLevel. The http(s) `click` anchor was the one live escape.

Stacked on #13721, which adds the shared lazy Mermaid plugin this hardens.

Refs #13568

Co-authored-by: yiaany <ilyaantonievich@gmail.com>
Co-authored-by: yiheng-kkk <lij921093@gmail.com>

* feat(ui): theme Mermaid diagrams with the Cline design system

Replace Streamdown's built-in Mermaid block in Desktop with an owned
`@cline/ui` block (`createMermaidRenderer` from
`@cline/ui/components/mermaid-block`):

- Mermaid's `base` theme with variables resolved from the live design tokens
  (`--card`, `--foreground`, `--primary`, ...), following light/dark and the
  accent palette and re-rendering when either changes; a diagram's own
  `%%{init}%%`, frontmatter `config:`, `classDef` and `style` still win
- a filename header from the fence title (```mermaid title="my-diagram"),
  frontmatter `title:`, or a name derived from the diagram
- zoom, fullscreen, copy, and PNG / `.mmd` downloads; PNGs are opaque, 2-3x,
  capped at 4096px and kept under the image attachment limit
- renders are serialized through one queue, since Mermaid is a process-wide
  singleton that is not safe to render concurrently

The owned block injects the SVG itself, so diagram link neutralization moves
into the shared render service and covers both this block and the Streamdown
diagram plugin. Desktop keeps routing diagram link clicks through the existing
confirmation modal.

Refs #13568

* fix(ui): avoid polynomial backtracking in Mermaid helpers

* fix(ui): make diagram label extraction linear-time

Replaces the backtracking label-regexes CodeQL flagged (ReDoS, PR #14757) with single-pass scanners and line-column parsing.

* refactor(ui): share the Mermaid render service and drop dead naming helper

createLazyMermaidPlugin now delegates import/retry/queueing/link-neutralization to createMermaidService instead of duplicating that path, keeping only theme-keyed config. Removes deriveMermaidDiagramName, which had no consumer outside its own tests.

---------

Co-authored-by: harrisonofcline <harrison@cline.bot>
Co-authored-by: yiheng-kkk <lij921093@gmail.com>
2026-10-06 10:50:06 -07:00
Bee b78d91b79b fix(desktop): reconnect chat sessions after Cline Hub loss (#14725)
* fix(desktop): reconnect chat sessions after Cline Hub loss

Retry session rebuild for up to a minute when the hub transport closes or the session is missing, then end the turn as interrupted so the user can resend. Surface reconnect status in the webview instead of treating it as an error.

* fix(desktop): scope hub-loss recovery to lost sessions

- Probe the hub before rebuilding so a session it still holds is left alone
- Cancel the reconnect loop on stop/abort/reset and undo a racing rebuild
- Recover queued sends too, re-queueing once the session is back
- Show the resend notice as an error-role bubble that history sync keeps
- Recognize missing sessions wrapped as generic hub command failures

* fix(desktop): share hub recovery and preserve session ownership

* fix(desktop): isolate replacement recovery and avoid duplicate queued sends

* fix(desktop): serialize recovery across session replacements

* fix(desktop): bound the entire shared recovery lifecycle
2026-10-05 14:33:23 -07:00
Saoud Rizwan 476b165b97 chore(desktop): trim v0.0.43 changelog 2026-10-02 13:17:41 -07:00
Saoud Rizwan 97facdb231 chore(desktop): release v0.0.43 2026-10-02 13:17:19 -07:00
Saoud Rizwan aa47bcc97f desktop: Connectors "What's new" spotlight (#14786)
* desktop: add Connectors 'What's new' catch-up

* desktop: tighten Suggested combos copy to fit two lines

* desktop: give the Connectors catch-up its own spotlight layout with an Open Connectors CTA

* desktop: move connector logos and pitch into the What's new body, make the example clickable

* desktop: let the What's new dialog scroll in short windows
2026-10-02 13:14:12 -07:00
Saoud Rizwan c269dbb7f9 chore(desktop): release v0.0.42 2026-10-02 02:43:01 -07:00
Saoud Rizwan 7e7ff84efd Move installed connectors search under its header and show Browse in two columns (#14771) 2026-10-02 02:37:14 -07:00
Saoud RizwanandSaoud Rizwan 4e3eb7ea15 desktop: browse connectors from Customize, make Connectors the default tab (#14764)
* desktop: browse connectors from Customize and make Connectors the default tab

Customize > Connectors gains a Browse section below the suggested recipes
that lists the rest of the Composio catalog with search and infinite
scroll, so nobody has to open the Marketplace just to find a connector.
Connectors is now the first and default Customize tab (falling back to
Tools for accounts without beta access).

Connector rows and cards always show the connector's own brand logo:
ConnectorLogo falls back to Composio's slug-based logo URL, and the
Marketplace connector rows use it instead of the generic type glyph.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* desktop: format connectors view

* desktop: point the empty Connectors state at Browse and in-chat connect

Connectors can now be connected from inside a task (cline/cline#14755),
so the empty state no longer sends people to the Marketplace.

* desktop: render connector logos at full tile size

ConnectorLogo was drawn at size-5 inside size-7/8/10 tiles, leaving a
grey margin around every mark. The image now fills the tile itself
(size and radius come from className) and the wrapper spans are gone;
the lucide/letter fallbacks keep the tile background.

* desktop: always show the Connectors tab with sign-in and beta guidance

The Connectors tab no longer depends on Composio availability, so the
default tab never swaps underneath the user. When connectors are
unavailable the tab explains why: signed out shows a Cline sign-in card
(same device-code OAuth flow as the Account page) plus Create account;
signed in without beta access shows a rollout notice with Check again.
The tab count is hidden while connectors are unavailable.

Also addresses review feedback: the search hint counts only the Browse
entries actually hidden, and a failed logo retries when a different URL
arrives.

* desktop: separate Installed and Browse searches, wait for account before sign-in prompt

Each Connectors list now has its own search field: the top field filters
Installed (and is hidden when nothing is installed), the field under the
Browse header filters the catalog. Suggested is no longer filtered.

AccountProvider exposes accountReady so the unavailable card can show a
spinner until the first identity lookup finishes instead of telling a
signed-in user without beta access to sign in.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-02 02:08:09 -07:00
Saoud Rizwan 63c2f65c24 feat(desktop): add /compact slash command (#14770)
* feat(desktop): add /compact slash command

Suggest /compact in the composer's slash menu and run it in the sidecar
like the CLI's local command: summarize the transcript with a manual core
compaction and persist it as the session's compaction state instead of
sending the literal text to the model.

* fix(desktop): forward session baseUrl and headers to /compact

Build the compaction connection with buildSessionConnectionUpdate so a
session's top-level baseUrl and headers reach the summarizer like they
reach the live session, and cover the successful compaction path.
2026-10-02 01:56:56 -07:00
Saoud Rizwan 4515a410a0 chore(desktop): release v0.0.41 2026-10-01 21:48:38 -07:00
Saoud RizwanandSaoud Rizwan eabcc2a915 desktop: make marketplace rows self-describing by type (#14750)
* desktop: add marketplace design exploration with five layout variations

Exploration-only /design/marketplace route (not linked from the app) that
renders the same unified item list through five candidate directions:
typed rows, storefront, tabs like Customize, one row per service, and a
directory table. Catalog entries are live; connectors are a fixture.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* desktop: make marketplace rows self-describing by type

Every row and the detail header carry the type's glyph on a per-type
tint, section headers explain what each type is, and rows in mixed
search results show a type pill, so same-named entries (Figma the MCP
server vs Figma the connector) read as different things. Category tags
move from a second row of pills into a single dropdown. Removes the
design-exploration route now that direction A is chosen.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* desktop: drop the count from marketplace section headers

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-01 13:05:09 -07:00
Saoud RizwanandSaoud Rizwan 227041a79d desktop: highlight sessions started in the app in the sidebar (#14749)
activeHistorySessionId was derived only from thread.historySession, which
is set when a session is opened from history. A session started fresh in
the app only gets thread.sessionId bound via thread-started, so the
sidebar never received an active id for it and its row stayed
unhighlighted (and its own chat events marked it unread).

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-01 11:12:35 -07:00
Saoud RizwanandSaoud Rizwan 7118784339 desktop: add suggested Connector recipes (#14730)
* desktop: align Customize > Connectors tab with the other Customize tabs

The Connectors tab rendered a lone 'Browse all connectors in the
Marketplace' button when empty (redundant with the page header's
Marketplace button) and a compact two-column grid when populated,
neither matching Skills / Plugins / MCP.

It now mirrors those tabs: description row with a refresh button,
full-width search, an Installed section with a count and stacked cards,
and the dashed empty-state box. The recommended connectors that are not
yet connected appear in a Suggested section with one-click Install, so
the empty state is never a dead end without linking out.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* desktop: drop Suggested section from Connectors tab, keep Installed only

Matches the Plugins / MCP tabs exactly: the empty state is the dashed
box pointing at the marketplace, and the header Marketplace button is
the single way to browse.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* desktop: address review on Connectors tab refresh and card click target

- A failed manual refresh keeps the installed list and refresh button and
  shows an inline error instead of replacing the whole tab.
- A refresh response that arrives after a newer connect/disconnect write
  is discarded (status version guard), so stale snapshots cannot
  resurrect an uninstalled connector.
- Successful refreshes go through applyStatus so the Customize tab count
  updates too.
- The whole connector card is the click target for the detail dialog,
  matching its hover treatment and MarketplaceEntryCard.
- Tests cover the refresh button and inline refresh error.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* desktop: suggest connector recipes on Customize > Connectors

Adds a Suggested section under Installed with four connector
combinations (incident RCA, spec-to-shipped, launch-and-market,
support-to-PRs), each explaining the use case, an example prompt, and
per-connector install chips. Connected members show a check; a recipe
disappears once all of its connectors are connected. Shown in both the
empty and populated states; search filters recipes too.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* desktop: add 'Organize your day' connector recipe (Gmail + Slack + Google Calendar)

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* desktop: drop the GitHub connector recipe (GitHub is not an available connector)

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-01 10:40:25 -07:00
Mikołaj Kondratek 0809928ab2 fix(desktop): preserve prompt drafts when switching sessions (#14645)
* fix(desktop): preserve prompt drafts when switching sessions

* fix(desktop): guard draft recovery and reuse session threads
2026-10-01 16:05:22 +02:00
Bee 8eee168b80 fix(desktop): show installed tool count, not catalog total (#14726)
* fix(desktop): show installed tool count, not catalog total

Drop the connector slug and the stale catalog tools total from the detail dialog so the count reflects only tools available in new sessions.

* fix(desktop): show zero installed tools instead of catalog total for empty connectors
2026-09-30 21:56:41 -07:00
John Choi 9fe17595de fix(desktop): match web cloud model catalog choices (#14720)
* fix(desktop): match web cloud model catalogs

* refactor(desktop): reuse existing cloud model picker

* fix(desktop): require a ready cloud model and allow catalog retry

* fix(desktop): load local cloud model capabilities explicitly

* fix(desktop): start cloud model selection without local fallbacks
2026-09-30 16:41:57 -07:00
Saoud Rizwan 50c83047f2 chore(desktop): release v0.0.40 2026-09-30 16:37:18 -07:00
John Choi 2261d13f9b fix(desktop): remember Cloud model and Git selections for new chats (#14619)
* fix(desktop): remember the Cloud/Local pick and cloud model for new threads

Every new thread seeded its config from `getInitialChatConfig()`, which
restored the last provider/model/workspace but never the execution
target, so a user who ran a task on Cloud was silently dropped back to
Local on the next "New chat".

Persist the user's Cloud/Local pick (and the model last chosen while on
Cloud, which the model picker deliberately keeps out of the per-provider
local memory) in localStorage, and seed new threads from it. Only the
user's own selection is written; the flag-off fallback that forces Local
never overwrites it. The cloud-agents flag now starts as "unknown" so a
thread remembered on Cloud is not bounced to Local before the flag has
resolved.

Part of #14492 (desktop half). The web half is cline/core-platform#3395.

* fix(desktop): open Cloud on the remembered cloud model when switching mid-thread

`getInitialChatConfig()` already seeds a new Cloud thread from the
remembered cloud model, but the Local → Cloud switch inside an open
thread still derived the model from the current local one, so the picker
showed a different model than the one the user last chose on Cloud (and
the next new chat would show the remembered one again). Read the
remembered cloud model there too.

Found by e2e; also flagged by Greptile/Codex on #14619.

* fix(desktop): honour an explicit Local pick and keep validated workspace on Cloud → Local

Three review follow-ups on the remembered execution target:

- Picking "Local" in the environment menu from an SSH host now also
  selects the local execution target, so the new local draft does not
  open on Cloud just because Cloud was remembered. Choosing another SSH
  host is not a Cloud/Local decision and leaves the memory alone.
- A thread that opened on Cloud never captured local settings; switching
  it to Local now keeps the workspace that discovery already validated
  instead of re-applying the raw remembered path, which may no longer
  exist.
- While the cloud-agents flag is still unknown, a composer remembered on
  Cloud keeps its cloud controls rather than showing the local folder
  picker; if the flag settles false the existing effect moves it to Local.

* fix(desktop): never leave a Cloud → Local switch with an empty workspace

If a thread that opened on Cloud is switched to Local before workspace
discovery has returned, nothing was captured and the Cloud config's
workspace is still empty, so the Local composer ended up with no
workspace and the persistence effect could record "" as the last
workspace. Fall back to the remembered path in that window; discovery
still validates and overrides it when it completes.

* feat(desktop): remember the cloud repository and branch for new threads

Remembering the Cloud target and model but making the user re-pick the
repository on every new thread was the remaining gap: the desktop never
had repo memory (only reopened sessions carried theirs). Store the last
cloud repo and branch alongside the target/model, seed new Cloud threads
and Local → Cloud switches from them, and write them from the repo and
branch pickers. Changing the repo forgets the branch until one is picked
for it; the composer's existing accessibility check still clears a repo
the current account can no longer reach.

* fix(desktop): keep the remembered cloud repo through composer clears and history

- The composer clears the repo/branch itself when the current account
  cannot reach the repository (GitHub not connected, no access), and
  that clear went through the same handlers that persist a pick, erasing
  the memory. Only non-empty picks are remembered now.
- reset() only re-seeded the repo when the execution target changed, so
  a new chat after viewing another Cloud session kept that session's
  repository. Re-seed the repo and branch from the remembered selection
  regardless of target, matching provider/model.

* fix(desktop): keep the remembered cloud repo through the first setup check

The composer's mount-time cloud setup check invalidated the scope
eagerly, clearing the repo and branch before confirming access. Since
every new thread mounts a fresh composer, the remembered repo was seeded
and then wiped on each launch and each Local → Cloud switch, and nothing
put it back once access was confirmed.

Only a switch to another user invalidates eagerly now; the first check
of a composer keeps the remembered repo and the existing access guard
drops it if the result shows it is no longer reachable. The sidecar's
scope-change broadcast still invalidates as before.

* fix(desktop): judge a re-activated Cloud composer only by fresh access results

When the composer comes back to Cloud (e.g. after reconnecting GitHub
while on Local), the access guard ran in the same render against the
previous activation's cloud setup results and cleared the remembered
repo before the new check had returned; the successful refresh then had
nothing to restore. Track which request the setup state reflects and let
the guard act only once it matches the latest one. Re-activation also
marks the scope as checking again (without clearing the repo), as it did
before a user-switch became the only eager clear.

* fix(desktop): wait for repository access before starting cloud tasks

* fix(desktop): recover from superseding repository check failures

* fix(desktop): retain validated composer on picker refresh errors

* fix(desktop): share concurrent cloud repository checks

* fix(desktop): persist target only after environment switch succeeds

* fix(desktop): coordinate local selection with disconnect events

* refactor(desktop): reuse model preferences and narrow persistence scope

* fix(desktop): remember Cloud repository and branch for new chats

* fix(desktop): expose recovery for Cloud branch restore failures

* fix(desktop): remember explicit Local selection after fallback

* chore(desktop): trim persistence comments

* fix(desktop): resume Cloud restore after picker failure
2026-09-30 11:05:10 -07:00
BeeandCursor Agent 2a83dd1a0e fix(desktop): provider execution, browser metadata, and MCP paths - CLINE-3283 (#14206)
* llms/core: register catalog custom providers with the gateway on the agent run path

Custom providers added via Desktop's Add Provider dialog (persisted to
providers.json/models.json and registered in the @cline/llms model
catalog) were visible in every picker but failed at execution time with
'Unknown or disabled provider "<id>"'. The agent run path
(createAgentModelFromConfig -> createGateway) only seeded builtin
provider registrations, while the legacy ApiHandler path already bridged
catalog collections into the gateway via resolveProviderRegistrationSync.

Export that bridge from @cline/llms as
resolveGatewayProviderRegistration(Sync) and apply it in
createAgentModelFromConfig, mirroring the ApiHandler path.

Fixes cline/cline#14180

* desktop: resolve MCP settings through one canonical resolver

The sidecar carried its own homedir-based resolveMcpSettingsPath next to
the shared @cline/shared/storage resolver, and the Tauri shell had a
third copy. All three agree under default env, but the duplicates
ignored CLINE_DIR/CLINE_DATA_DIR (and the shared resolver's HOME
handling), so MCP reads/writes, the OAuth authorize command, and the
'Open MCP settings' window action could target different files.

Delete the sidecar duplicate so every sidecar command uses the shared
resolver, and mirror the shared resolver's precedence
(CLINE_MCP_SETTINGS_PATH > CLINE_DATA_DIR > CLINE_DIR/data >
~/.cline/data) in the Rust open_mcp_settings_file path.

Fixes cline/cline#14152

* refactor(desktop): resolve provider metadata in the host

* Authenticate provider model source requests

* Avoid iterable Headers requirement in VS Code compatibility build

* Refresh source catalogs when provider credentials or endpoints change

* fix(core): save provider settings when model discovery fails

* fix(core): propagate provider catalog persistence failures

* fix(core): restore provider settings when catalog writes fail

* fix(core): serialize provider mutations and persist settings once

* fix: preserve provider customizations and handle unknown auth metadata

* fix(core): track discovery ownership on initial catalog refresh

* fix(desktop): address provider catalog review feedback

- Treat an empty source catalog as best-effort during credential saves
- Prune models from source-backed entries saved before discovery ownership
- Format turn failures with the submitted provider's auth metadata
- Re-export ProviderLocalCli/resolveProviderLocalCli from @cline/llms for compatibility

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-09-30 10:23:37 -07:00
Saoud Rizwan 1d48334ce3 chore(desktop): release v0.0.39 2026-09-29 23:46:23 -07:00
Saoud RizwanandSaoud Rizwan 018f97b555 desktop: ship AppStream metainfo in Linux deb/rpm packages (#14688)
GNOME Software and KDE Discover read /usr/share/metainfo to show an app's
icon, screenshots, and description, and to list it under Installed. The
Linux packages only shipped a .desktop file and icons, so the installer
showed a placeholder icon and "No screenshots".

Add a per-channel metainfo file (stable, beta, nightly install side by
side and have different desktop ids) and include it via the deb/rpm
files map. Channel configs override the same key so the merged config
ships exactly one file.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-29 23:37:59 -07:00
Saoud RizwanandSaoud Rizwan 816513fded fix(desktop): disable WebKitGTK DMA-BUF renderer on NVIDIA Linux (#14687)
On Linux with the proprietary NVIDIA driver the desktop app window opens
but stays blank. The sidecar/hub is healthy; only rendering fails, with
stderr showing:

  KMS: DRM_IOCTL_MODE_CREATE_DUMB failed: Permission denied
  Failed to create GBM buffer of size 1500x980: Permission denied

This is the known WebKitGTK DMA-BUF/NVIDIA incompatibility documented by
Tauri (https://v2.tauri.app/develop/debug/linux-graphics/), whose fix is
to set WEBKIT_DISABLE_DMABUF_RENDERER before the webview is created.

When /sys/module/nvidia is loaded and the variable is not already set, set
it to "1" as the first statement in main(), before tauri::Builder and any
threads. Existing values (including "0" and empty) are left alone so users
on hybrid-GPU systems can opt back into the DMA-BUF path with
WEBKIT_DISABLE_DMABUF_RENDERER=0. Done in the executable rather than the
.desktop launcher so terminal launches and every package format benefit.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-29 23:15:11 -07:00
Saoud Rizwan c604735fe1 chore(desktop): release v0.0.38 2026-09-29 19:57:24 -07:00
01176459b7 refactor(desktop): move web search controls into Customize Tools (#14662)
* refactor(desktop): move web search controls into Customize Tools

Relocate web search settings from General into the Customize tools
section so provider-backed search lives with other tool configuration.
Wire onOpenModelProviders through CustomizeView and drop the old
General-page toggle, catalog checks, and tools.web_search typing.

* fix(desktop): list web search alongside built-in tools

* fix(desktop): reflect web search defaults and provider readiness

* fix(desktop): retain provider guidance when refresh fails

* refactor(desktop): route web_search toggle through the hub like other tools

Core's setDisabledTools already persists model tool names to
tools.web_search.enabled, so the sidecar special case was redundant.
Also document why the web_search listing entry is hand-rolled instead
of coming from the core catalog, and shorten its description so it fits
the two-line clamp on the tool card.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-29 16:43:10 -07:00
Saoud RizwanandSaoud Rizwan af7e4ce5e9 ci(desktop): publish Linux arm64 deb and rpm packages (#14672)
* ci(desktop): publish Linux arm64 deb and rpm packages

Turn the build-linux job into an x64/arm64 matrix. The arm64 leg runs
natively on ubuntu-22.04-arm so the sidecar smoke test exercises the real
binary; the collect step names packages per arch (amd64/arm64 deb,
x86_64/aarch64 rpm), which generate-update-manifest.ts already maps to the
linux-aarch64-deb and linux-aarch64-rpm updater keys.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* test(desktop): cover arm64 Linux packages in updater manifest test

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-29 15:53:22 -07:00
Bee fef9de1665 fix(desktop): label beta connectors and filter marketplace navigation (#14654)
* fix(desktop): label connectors as beta

* fix(desktop): open connector marketplace with connector filter
2026-09-29 14:22:20 -07:00
Saoud RizwanandSaoud Rizwan 552c5841fd fix(desktop): skip mkdir when diagnostics output dir already exists (#14655)
On Windows, Bun's recursive mkdirSync can throw EEXIST for an existing
directory (e.g. C:\Users/Max/Downloads), so exporting diagnostics failed.
Only create the output directory when it is actually missing.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-29 13:37:03 -07:00
Bee bfc54d4d80 chore(llms): model list upadte - v1790702240106 (#14648)
Autogenerated output from running bun run build:models
2026-09-29 10:42:44 -07:00
Saoud Rizwan 3ba8dd8fa7 chore: upgrade Bun to 1.4.2 (#14631)
* chore: upgrade Bun toolchain and embedded runtime to 1.4.2

Bun 1.4.1+ writes a valid ad-hoc Mach-O signature for bun build --compile
output, so the CLI is no longer SIGKILLed on macOS 27, and 1.4 ships
x64 runtimes as baseline-only, so compiled binaries run on pre-AVX2 CPUs.

Pins engines.bun, packageManager, .tool-versions, every setup-bun step,
and @types/bun. The lockfile only moves @types/bun/bun-types so the AI SDK
hoisting is left untouched.

* fix(core): swallow reader.cancel() rejection in web_fetch size guard

Bun 1.4 rejects cancel() on an errored stream, which surfaced as an
unhandled rejection right before the size-limit error was thrown.

* build(cli): boot the hub from the compiled binary as a smoke test

--version only proves the executable launches. Bun 1.4.1/1.4.2 have a
compile-time chunk ordering regression with splitting: true that only
shows up once real modules load, so start the hub daemon from the freshly
compiled host binary in an isolated data dir and require a healthy status
before the build is considered good.

* test(core): stop asserting the forced-exit path in hub shutdown e2e

Under Bun 1.3 http.Server.close() never settled while a WebSocket was
open, so the daemon always exited through the 2s deadline and the e2e
asserted the forced-exit log line. Bun 1.4 completes the graceful close
(~50ms instead of ~2s in a side-by-side probe), so that line no longer
appears. The exit code, timing, discovery cleanup, and endpoint checks
still cover the behavior; the forced path stays covered by
shutdown-coordinator.test.ts.

* build(cli): isolate the smoke test hub from CLINE_HUB_DISCOVERY_PATH

The env var overrides the data-dir-derived discovery record, so an
inherited value would have pointed hub start/stop at a real hub.
2026-09-28 21:33:32 -07:00
John Choi b0a6a4518d feat(desktop): resume persistent cloud agent workspaces (#14501)
* feat(desktop): persist cloud agent workspaces by default

* docs: trim cloud session documentation and comments

* fix: reject duplicate one-shot starts and simplify resume tests

* fix: keep cloud resume resilient and clear suspended state

* fix(cloud): preserve reasoning settings and refresh resume status

* fix(runtime): retain saved reasoning preferences on restart

* fix: limit automatic cloud Git backups to ephemeral sandboxes

* test: trim redundant cloud resume coverage
2026-09-28 16:48:26 -07:00
Bee 241bc08ac4 fix(desktop): keep text after cursor on slash insert CLIENTS-122 (#14627)
Preserve any input after the cursor when inserting a slash command,
replacing only the typed query and adding a space only when needed.
2026-09-28 15:44:39 -07:00
John Choi 877106c8ef refactor(ui): share desktop conversation layout and header (#14423)
* refactor(ui): share desktop conversation layout and header

* docs(ui): document shared conversation layout

* docs(ui): condense conversation layout documentation

* docs(ui): keep layout documentation in UI package
2026-09-28 13:50:09 -07:00
Saoud Rizwan 3da9771ea5 chore(desktop): release v0.0.37 2026-09-25 19:08:29 -07:00
John Choi 0519ea2bbd refactor(ui): share desktop composer presentation (#14422)
* refactor(ui): share desktop context usage presentation

* refactor(ui): share desktop composer presentation

* fix(ui): inherit composer variants

* fix(ui): render zero context usage cost

* fix(ui): preserve context cost visibility

* test(ui): cover context cost visibility

* docs(ui): document shared context usage API

* docs(ui): document shared composer API

* docs(ui): shorten composer API reference

* docs(ui): keep composer documentation in UI package
2026-09-25 14:27:37 -07:00
Saoud RizwanandSaoud Rizwan 9a46aa9917 desktop: 'What's new' dialog and About settings page with release notes (#14529)
* feat(desktop): About settings page with release notes and a What's new catch-up dialog

Settings gains an About section: product name, version and channel, an
Updates row (check now / restart to update), a Highlights row that replays
the latest catch-up, and the bundled release notes for recent versions with
links to the GitHub release and full changelog. The notes come from
CHANGELOG.md, baked into the static export by a force-static route so they
always match the installed build.

Individual releases are not announced in-app. Instead, catch-up highlights
are authored in webview/lib/whats-new-content.ts and shown once, as a
dialog, on the first launch after an update that carries a new entry. Fresh
installs mark the current entry seen when onboarding completes. The first
catch-up covers SSH remotes, worktrees, pull request status, and parallel
sub-agents, with a composer screenshot in light and dark variants.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* fix(desktop): drop the image from the What's new dialog, add Report an issue

Replace the screenshot with a brand-gradient header that reads well in both
themes, remove the now-unused image plumbing from the catch-up content type,
and add a Report an issue button that opens the GitHub issue chooser.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* fix(desktop): restore the hero design for the What's new dialog, move Report an issue to About

Rebuild the catch-up dialog to the original proportions: deep-violet hero
with an eyebrow and a short headline, four compact highlights with icon,
title, and one line of copy, then a single footer row. Drop the release
description and the Report an issue button from the dialog; About gets a
dedicated Report an issue row and a Full changelog link beside the release
notes instead of header actions.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* docs(desktop): add the desktop-whats-new skill for publishing catch-up highlights

Walks through deciding whether a catch-up is warranted, picking highlights
from the changelog, copy constraints, adding the entry, previewing from
Settings → About, and the seen-tracking gotchas. Linked from the desktop
README alongside publish-desktop.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* fix(desktop): describe Cline on the About page instead of the page itself

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* fix(desktop): drop the About page subheader

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* refactor(desktop): name the About update-check result state clearly

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* fix(desktop): label the What's new dialog action Get Started

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-25 13:21:38 -07:00
Bee dcdd56b212 fix(desktop): expose diagnostics export in Help menu (#14521) 2026-09-25 12:56:28 -07:00
Saoud Rizwan c38a034eb6 chore(desktop): release v0.0.36 2026-09-25 01:59:41 -07:00
Saoud Rizwan 1c036a7cd0 desktop: disable Save for unchanged SSH hosts and label new-host action Add (#14503) 2026-09-25 01:49:33 -07:00
Saoud Rizwan 9ad615294e fix(desktop): warm plugin slash commands for the active workspace (#14502)
* fix(desktop): warm plugin slash commands for the active workspace

The slash menu loaded plugin commands lazily on the first '/' keystroke,
so the first request paid a cold plugin-sandbox spawn against the sandbox
import budget. One timeout dropped every plugin command, the service
cached the failure for 30s, and the webview swallowed the error, so /goal
silently disappeared from the menu and was sent to the model as plain
text.

The CLI runs the same service but loads it at TUI mount, off the
keystroke. Do the same in the desktop: warm the plugin command service
from the local branch of get_git_branch, next to the existing
prewarmWorkspaceMetadata call. The webview polls that for whichever
workspace it has adopted (remembered, launch-cwd fallback, or a reopened
session), so it is the one hook that sees every active local workspace.
Guarded to once per workspace; the service reloads itself when the
plugin set changes.

* fix(desktop): own plugin warm-up explicitly and recover from a failed cold load

Review follow-ups: the warm-up rode on get_git_branch, which made plugin
availability depend on branch polling and wait for two git subprocesses,
and it marked a workspace warmed before the load succeeded so a slow cold
start was never retried.

- New warm_plugin_commands sidecar command; the webview calls it from the
  effect keyed on its active workspace, so every adopted local workspace
  (remembered, launch fallback, reopened session) is warmed immediately.
- An empty result (the service reports a failed load that way) schedules
  a bounded background retry spaced past the service's 30s failure cache;
  the workspace is only marked warmed once commands come back.
- Regression test: failed first load, retry succeeds, list_plugin_commands
  and the slash runner then work without the user reopening the menu.
2026-09-25 01:44:11 -07:00
BeeandSaoud Rizwan 4580b1bb72 fix(desktop): Windows sidecar/backend startup failures (#14401)
* fix(hub): start the desktop backend reliably on Windows proxy machines

Two Windows-specific defects kept the desktop sidecar from ever seeing the
Hub daemon it had just spawned (#14265, #14292):

- Bun's fetch honors HTTP(S)_PROXY with no localhost bypass, so on machines
  whose system proxy exports those variables (Clash, v2ray, corporate
  setups) every loopback discovery probe was sent to the proxy and failed.
  The healthy daemon looked unreachable, the sidecar died with "No
  compatible hub runtime is available" before publishing its endpoint, and
  every respawned daemon exited with "Hub instance lock is held by a live
  Hub". New ensureLoopbackProxyBypass() appends the loopback spellings to
  NO_PROXY/no_proxy whenever a proxy variable is set; it runs at every
  entrypoint (desktop sidecar, hub daemon, CLI, menubar) and lazily inside
  probeHubServer/requestHubDrain/requestHubShutdown so every embedder of the
  hub client is covered. Reproduced end-to-end on Linux: the compiled
  sidecar with a dead HTTP_PROXY fails exactly like the Windows reports
  before this change and publishes its endpoint after it.

- Compiled-Bun embedded entry detection only knew the POSIX /$bunfs/ mount,
  not the Windows B:\~BUN\ drive, so Windows daemons were spawned with a
  dead B:\~BUN\root\entry.js argument (visible verbatim in #14292) instead
  of the --cline-hub-daemon marker, hiding them from cline doctor's
  marker-based process scan. New isBunEmbeddedModulePath() handles both
  spellings and replaces the three divergent checks (hub daemon spawn, CLI
  internal launch, menubar entry detection).

The compiled-backend startup test now also boots the sidecar behind a dead
HTTP(S) proxy so the regression stays locked in CI.

* fix(desktop): clamp the main window to the monitor work area

The window opens at a fixed 1500x980 logical pixels. On small or DPI-scaled
displays — a 1920x1080 laptop at 150% scaling has only a 1280x720 logical
desktop — it came up larger than the screen, leaving the bottom and side of
the UI (the settings and account controls among them) off-screen and
unreachable (#14270). Shrink an oversized window to the monitor's work area
(which already excludes the taskbar) minus the OS chrome, and re-center it;
windows that fit are left untouched, and a nonsense work area reading never
collapses the window.

* fix(shared): merge both NO_PROXY casings instead of copying one over the other

ensureLoopbackProxyBypass picked whichever of NO_PROXY/no_proxy was set
first and wrote that value (plus the loopback hosts) to both casings. On
POSIX the two variables can be populated independently — a launcher setting
one and a shell profile the other — and different tools read different
casings, so exemptions present only in the other casing were silently
dropped and previously exempt traffic could start routing through the
proxy. Merge the union of both casings (first spelling wins on duplicate
entries, compared case-insensitively) so entries are only ever added, and
honor a wildcard opt-out in either casing.

Review finding from cline/cline#14401.

* test(core): clear proxy env before setting it in the loopback-exemption test

On Windows process.env is case-insensitive, so the previous order — assign
HTTP_PROXY, then delete http_proxy — deleted the variable that was just
set. ensureLoopbackProxyBypass then correctly saw no proxy and wrote
nothing, and the assertion received '' on the Windows CI leg (Ubuntu
passed because POSIX treats the casings as separate keys). Delete all four
variables first, then set HTTP_PROXY, which behaves identically on both
semantics. Test-setup fix only; verified against a simulated
case-insensitive env that the old order reproduces the CI failure and the
new order passes.

* ci: retrigger checks after unrelated Windows python-hook flake

The previous run's sole failure (hook-file-hooks.test.ts > executes python
hook files, 15s spawn timeout on the windows-latest runner) is untouched by
this branch and passed on the prior commit of this same branch; the
loopback-exemption test fixed in 01009dd91 now passes on Windows.

* chore: trim duplicated call-site comments in the loopback proxy bypass

Keep the explanation once, on ensureLoopbackProxyBypass, and reduce each
call site to a one-liner.

* fix(shared): exempt loopback in the non-wildcard casing when only one NO_PROXY casing is *

A wildcard in one casing left the other casing untouched, so a tool that
reads only the lowercase variable could still route 127.0.0.1 through the
proxy. Leave the wildcard casing alone and merge loopback into the other.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-09-24 21:59:45 -07:00
Bee 3061382ff8 fix(desktop): keep provider models within panel width and height (#14487)
* fix(desktop): constrain provider model panel width

* fix(desktop): scroll model rows within the provider panel

* fix(desktop): improve provider list scrolling and settings layout

* fix(desktop): keep model controls reachable in short panes

* fix(desktop): rename API Providers settings to Providers
2026-09-24 20:20:03 -07:00
Saoud Rizwan b77c5f437e chore(desktop): release v0.0.35 2026-09-24 01:14:44 -07:00