* fix(agents): continue unknown AI SDK completions once
* fix(agents): add hidden user continuation after unknown finish
* fix(core): preserve active attachments during hidden continuation compaction
* docs: correct shared paths, log files, commands and tool names
Several pages repeat the same facts about where Cline keeps its files,
which commands exist and what the built-in tools are called, and many of
them no longer match the shipped CLI and SDK:
- Workflows live in ~/.cline/workflows/, not ~/.cline/data/workflows/.
The session index is ~/.cline/data/db/sessions.db, each session has a
folder of JSON files, and hub locks and logs are under ~/.cline/data/.
- cline start and cline dev log do not exist; the log command is
cline doctor log, and the CLI log is ~/.cline/data/logs/cline.log.
- The built-in tools are run_commands, search_codebase,
fetch_web_content and skills, not bash, search, fetch_web and
use_skill.
- CLINE_HUB_ADDRESS is read only by cline schedule; the hub port is
CLINE_HUB_PORT.
- No CLI 3 release reads CLINE_COMMAND_PERMISSIONS. The pages that
offered it, including the PR review sample, now say the CLI has no
command allow list and point to a PreToolUse hook. The unused
permissionDeniedError string in the VS Code prompts goes with it.
Part of #14853.
Co-authored-by: Mikołaj Kondratek <19799111+mkondratek@users.noreply.github.com>
Co-authored-by: Manpreet Kaur <26463231+Manpreet2298@users.noreply.github.com>
Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
* docs: qualify log, tool routing and command-approval advice
- cline doctor log always opens the default log file, so say so where
CLINE_LOG_PATH is described.
- The editor and apply_patch choice is a default that SDK callers can
replace with toolRoutingRules.
- The PR review sample now warns that hooks and plugins in the checked-out
pull request run with the job's environment, and says how to keep them
from running before relying on a global PreToolUse hook.
- The conditional-approval example no longer auto-approves git diff,
which runs a configured external diff program, and notes that Git can
run programs named in a repository's config.
* docs: run the PR review sample with a read-only token
The sample gave an auto-approved Cline run a token that can write pull
requests. Split it into two jobs: `review` runs Cline with read-only
permissions on a checkout of the base branch and writes review.md;
`post` runs no repository or model code and posts the file with
`gh pr review --comment`.
Skip pull requests from forks and Dependabot, which get no secrets,
and warn against `pull_request_target`. Fence the workflow with four
backticks so the code block inside the prompt no longer ends it early.
---------
Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
Co-authored-by: Mikołaj Kondratek <19799111+mkondratek@users.noreply.github.com>
Co-authored-by: Manpreet Kaur <26463231+Manpreet2298@users.noreply.github.com>
Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
* 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.
* docs: state telemetry defaults and scope accurately
The Enterprise and monitoring pages called Cline telemetry opt-in and
anonymous, and said code never leaves the machine. Telemetry is on by
default in the VS Code and JetBrains extensions, Cline Desktop and the
CLI. It identifies signed-in accounts and organizations, and includes
prompts for requests to the Cline and ClinePass providers.
Rewrite the telemetry page around what is collected and how to turn it
off in each product. Scope the remote-configuration restrictions to the
products that enforce them, add a per-product support table, and tell
admins to ignore the folders Cline writes into workspaces. Align the VS
Code setting description and host-telemetry warning with the docs.
Part of #14853
* docs: drop bold from remote-config support table rows
The bold marked dashboard switch names, but nothing on the page said so,
so the rows looked inconsistent.
* docs: address review of the telemetry and remote-config pages
State only what the telemetry setting enforces in the extensions: the
lock, and that members can't turn telemetry on when the organization
turns it off. Scope the support-table introduction to the controls
marked Yes, since no product enforces YOLO or Kanban. Use recursive
.gitignore patterns and say why: the cached settings can include prompt
storage access keys, and a member may open a subfolder of a repository.
Narrow the indexing card to content: Cline lists file paths to search
them but doesn't index or embed file contents.
* docs: say products support remote-config controls, not enforce them
Organization rules and workflows reach sessions as instructions that a
member can edit, so "enforces" overstated what the CLI does with them.
The section heading already says "support".
---------
Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
The 3s default connect budget silently dropped every npx/uvx-launched
MCP server on Windows, where reaching initialize through cmd.exe
routinely takes 3-6s even with a warm cache. The model never saw the
server's tools and nothing was logged. 10s covers those launchers while
the doubled worst case (newline + framed attempts) still clears the 30s
hub session.create deadline with headroom.
* 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>
* fix: omit refusal fallbacks for custom Anthropic endpoints (#14816)
* fix(llms): gate Anthropic refusal fallback on the official endpoint
Move the endpoint check into an isOfficialAnthropicEndpoint URL helper and
keep it in the rule's build step so custom endpoints retain the generic
fanout suppression. Add an identity test that a custom endpoint changes
nothing but `fallbacks`, plus an HTTP-boundary test through the real
handler that checks the body and the server-side-fallback beta header.
Co-authored-by: Dante <duanjl.china@gmail.com>
---------
Co-authored-by: Dante <duanjl.china@gmail.com>
* refactor(core): select and order session rules in one place
The rules section of a session's system prompt was selected in several
places: `listEnabledRulesFromWatcher` (enabled only, sorted by name) for the
single-workspace plugin, and an inline filter without the sort in the
combined user-instruction service. Callers outside a session that need the
same section had nowhere to reach it and would have grown a third copy.
Add `listEnabledRulesFromRecords` / `loadRulesForSystemPromptFromRecords`
next to the watcher variants, make the watcher variants delegate to them,
and use the records variant in the combined service. Export the loader from
`@cline/core`.
Behavior change: the combined service now orders rules by name like the
single-workspace path does, instead of in listing order.
* fix(vscode): apply the user's rules when generating commit messages
"Generate Commit Message with Cline" built its prompt from a hardcoded
system prompt and never loaded `.clinerules`, so commit conventions users
had written down (language, format, ticket references) were honored in
chat and silently dropped by the button.
`Controller.getRulesForSystemPrompt` takes the enabled rules from the
user-instruction watcher the controller already runs for the workspace,
through core's shared `loadRulesForSystemPromptFromRecords`, and the
commit-message system prompt appends them behind a short preamble telling
the model which rules are relevant to a commit message. The button sees the
rule set a chat session in the workspace sees, including a rule that has
`disabled: true` frontmatter being left out of both. If the rules can't be
read, the base prompt is used.
Known limitation: the Rules panel's on/off toggles live in extension state
the SDK doesn't read, so a rule switched off there is still sent to chat
and to the button alike (CLINE-3120).
The new rules load is one more await before the request; the existing
cancel check before `createMessage` covers it, and a test pins that.
Fixes#9410
* fix(vscode): let Stop end a commit generation still waiting for rules
The first generation in a workspace waits for the user-instruction watcher's
initial scan of rules, skills and workflows. Stop was honored after that
await (no request went out), but the generation and its SCM progress
indicator stayed pending until the scan finished, which can take a while on
a slow filesystem.
Race the rules load against the generation's abort signal so Stop ends the
generation at once. The scan carries on in the background and warms the
watcher chat uses; nothing waits on it any more.
* fix(core): render hub client rules through the shared selection
The hub's client user-instruction proxy filtered and formatted rules inline,
in listing order, while the combined service now goes through the shared
`loadRulesForSystemPromptFromRecords`, which sorts by name. runtime-builder
wraps the client proxy in the combined service when host plugin skills are
present, even though the plugin service contributes no rules, so adding a
host plugin reordered the same client rules in the session prompt.
Render the hub snapshot's rules through the shared helper too. Every path
that builds a session's rules section now selects and orders rules the same
way; hub client sessions now get their rules sorted by name. A boundary test
renders reversed names plus a disabled rule through the proxy alone and
through the combined service, and expects the same output from both.
* docs(mcp): correct the CLI mcp config path
* docs(tools): correct the same stale .cline/mcp.json reference
* docs(cli): correct the MCP config entry in the configuration tree
The CLI reference listed `mcp.json` under the project-root `.cline/` tree,
but `resolveMcpSettingsPath()` only ever resolves a global path:
`~/.cline/data/settings/cline_mcp_settings.json` (overridable via
`CLINE_MCP_SETTINGS_PATH`). There is no project-level MCP config reader.
Move the entry to the `~/.cline/data/settings/` tree where the resolver
actually looks, and drop the project-root entry.
Three in-site links point at anchors that no longer exist, so they load
the target page without jumping anywhere. Verified against the live docs
DOM by counting the rendered heading ids.
* fix(cli): expose yolo mode
Show `-y/--yolo` in CLI help with a clear sandbox warning so unsupervised
benchmark and CI runs can opt in deliberately.
* revert: drop unrelated desktop sidecar hub-reconnect change
* 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
OpenAI-compatible adapters send the AI SDK's top-level reasoning value to
the endpoint unchanged as reasoning_effort, but the gateway resolved that
value from the raw request. The models.dev effort ladder was only applied
when composing provider options, after portable reasoning had already been
split off, so it never reached the wire. Endpoints with a short ladder
were sent levels they reject: Kimi K3 accepts only low, high and max, yet
received medium for the default thinking level and minimal for minimal.
For the openai-compatible and cline adapters, snap the portable level to
the nearest level the model advertises. When that nearest level is max,
which the portable setting cannot carry yet, keep the requested level
instead of lowering it. Adapters with their own per-model effort maps and
models without catalog facts are unchanged.
* Add GitHub Actions workflow to deploy Storybook
* Update path for Storybook deployment
* Update Storybook deployment workflow for dependencies
* Update Storybook build command in workflow
* Update Storybook deployment path and build command
* fix(llms): stop emitting hyphenated providerOptions keys for openai-compatible providers
`@ai-sdk/openai-compatible` 3.0.30 reads `providerOptions[<name>]` and its
camelCase alias, and logs a deprecation warning on every stream chunk when
the hyphenated raw name is present:
DeprecationWarning: AI SDK Warning (openai-compatible.chat / ...):
Deprecated: "providerOptions key 'openai-compatible'". Use
'openaiCompatible' instead.
Cline wrote its thinking, reasoning and prompt-cache patches under the raw
provider id (`openai-compatible`, `vercel-ai-gateway`, user-defined ids) as
well as the camelCase alias, so every chunk of every stream through the
generic provider spammed the warning to stderr.
Providers whose options target is `openai-compatible` now write only the
camelCase bucket. The SDK reads that alias for every provider name, so no
request-body passthrough is lost. Native and community vendor targets
(`openai-codex`, `claude-code`, ...) keep their raw id and alias as before.
* fix: resolve provider options PR review concerns
The lazy `config` wrapper declares its own `--json` option, so Commander
consumes the flag before the remaining arguments are forwarded to the
real config command. `cline config --json` therefore reached the runtime
command without the flag and launched the interactive view, which fails
without a TTY, while `cline --json config` worked because the inner
command also reads the root option. `cline config --json mcp` silently
printed text for the same reason.
Copy the wrapper's parsed value onto the runtime command before parsing
the forwarded arguments, and add a non-TTY e2e check that both flag
placements produce the same JSON inventory.
Co-authored-by: Dante <duanjl.china@gmail.com>
* feat(llms): opt-in Langfuse tracing for BYOK providers plus env tags, metadata and environment
Langfuse tracing was limited to the cline and cline-pass providers, so an
operator pointing LANGFUSE_* credentials at their own instance could not trace
OpenRouter or any other BYOK provider. CLINE_LANGFUSE_ALL_PROVIDERS=1 now opts
third-party providers in. They are exported only through the isolated direct
exporter and never through the host OTLP relay, so BYOK prompts cannot reach a
collector the operator did not configure themselves.
Benchmark and isolated-environment runs can label every trace from the
environment: LANGFUSE_TRACING_ENVIRONMENT sets the Langfuse environment on the
direct exporter, CLINE_LANGFUSE_TAGS adds comma-separated trace tags, and
CLINE_LANGFUSE_METADATA adds trace metadata as a JSON object or key=value
pairs. Env values merge under the runtime's own attributes, and env-only
attributes propagate even when a call supplies none of its own.
* docs(llms): state that Langfuse tracing covers streamed language requests only
Dedicated image generation uses the AI SDK's generateImage, which takes no
telemetry option and has no Langfuse integration hook, so it has never been
traced for any provider. Document that scope in the resolver and the runbook
rather than implying the BYOK opt-in covers it.
* 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
* 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>
* 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.
Restored team runs kept lastProgressAt as an ISO string, so team_list_runs
failed with "toISOString is not a function" on any resumed team that had a
run. Revive it alongside the other run dates in all three loaders.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(core): stop rewriting full team state on every streamed teammate chunk
* fix(core): retry failed team writes and persist retry/eventless state changes
- TeamPersistenceWriter requeues a failed batch and retries it instead of
dropping it; dispose no longer schedules retries after shutdown.
- Scheduled retries (run_progress with a queued run) are durable and flushed
immediately so recovery cannot grant an extra attempt.
- AgentTeamsRuntime notifies onStateDirty for eventless changes (mailbox
read receipts, cleanup) so they are written promptly.
- Telemetry progress throttle sends the latest event at the end of the window.
- drainStateDelta only indexes mailbox/mission log when they changed.
- handleTeamEvent applies the team_events retention cap.
- File history compaction is also bounded by bytes to avoid repeated rewrites.
- Remove unused TeamRunRow interface.
* fix(core): keep team write retries from duplicating history or dropping on logger errors
- Report write errors after requeueing so a throwing logger cannot lose the batch
- File store writes state before history; compaction and SQLite pruning are best-effort post-commit
- Clear queued telemetry progress when a durable event supersedes it
* fix(core): re-import legacy team state_json on every open
v2 builds only write an empty state_json, so a non-empty value means an
older build wrote it (e.g. after an image rollback). Previously it was
only imported during the one-time v2 migration, so writes made by a
rolled-back build were dropped after re-upgrading. Run the import on
every open.
* fix(core): avoid polynomial regex when trimming team names
`/^-+|-+$/` backtracks quadratically on long runs of `-` (CodeQL
js/polynomial-redos). Trim leading/trailing dashes by index instead.
* refactor(core): share a single sanitizeTeamName helper
Four copies existed; one (team-persistence-store) still used the
polynomial `/^-+|-+$/` trim. Move it to a dependency-free module so
storage can import it without pulling in team-tools.
* 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>
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>
* 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>
* fix(vscode): send Cline surface headers from commit message generation
Commit message generation builds a standalone ApiHandler through
`buildApiHandler` instead of going through ClineCore, and that path wired
no request headers at all. The Cline gateway only serves models that are
restricted to Cline product surfaces (the free models) to requests that
identify themselves, so "Generate Commit Message with Cline" failed with
HTTP 403 on a free model while normal chat on the same model worked.
Resolve the headers through the shared `resolveProviderRequestHeaders`
policy — the same one `local-runtime-bootstrap` uses for sessions — so
there stays one source of truth for provider header policy, and attach
them to the provider config the standalone handler is built from. The
caller now resolves the host client identity first, so a request reports
the same client/platform as a task from the same host. Custom provider
headers stored in providers.json are layered in as well; the commit path
dropped those too.
A standalone request is not a task, so no session id is claimed:
`sessionId` becomes optional in the resolver and `X-Task-ID` is omitted
rather than sent empty. The pre-SDK extension sent `X-Task-ID: ""` from
this same code path, so nothing depends on a meaningful id here.
Fixes#13590
* refactor(vscode): share one client-identity mapping between sessions and standalone requests
`resolveClineRequestClientContext` mapped the host identity to the SDK's
client shape, including the extension-identity fallbacks, and
`buildSessionConfig` built the same object inline. A commit-message request
and a task are supposed to report the same client, and two copies of that
mapping stop guaranteeing it the first time one is edited.
Move the resolver into the session factory and have `buildSessionConfig`
call it, so both paths share it by construction. The host-identity and
multi-root helpers go back to private.
* fix(vscode): send the core's own version and session source from standalone requests
The standalone path sent the extension's version as `X-CORE-VERSION` and an
inlined "vscode" source tag, while a task from the same extension sends the
core package's version via `local-runtime-bootstrap`. That made the "same
headers as a session" guarantee untrue for that header, and the reason given
for inlining the tag (avoiding a core import) didn't hold: this module already
sits behind `cline-session-factory`, which imports `@cline/core`.
Import `CORE_BUILD_VERSION` and `SessionSource.VSCODE` from `@cline/core` so
both paths agree by construction. The vitest `@cline/core` stub re-exports the
real values from SDK source.
* fix(vscode): honor a commit-generation cancel issued while the host identity resolves
`performCommitMsgGeneration` flips the SCM button to its stop action and
then awaits `buildApiHandlerWithHostContext()` (two host-bridge lookups)
before it created this generation's `AbortController`. A cancel in that
window aborted the previous controller, if any, and the request the user had
just cancelled was then sent anyway.
Create and install the controller before the first await, check it again
right before `createMessage`, and read it through a local so a later
generation can't swap the signal an earlier one checks. Two small follow-ons
from the same fix: a cancelled generation no longer reports "Failed to
generate commit message: This operation was aborted", and only the current
generation clears the context key, so one that settles late can't flip the
button back while a newer one runs.
* fix(vscode): cancel a running commit generation when a new one starts
Each generation now checks its own controller, but the stop action can only
reach the one stored in the module slot. The Generate keybinding, unlike
the SCM button and palette entry, stays live while a generation runs, so a
second invocation replaced the slot and left the first generation
unstoppable: Stop cancelled the newer request while the older one kept
writing into the commit input. Before the per-generation controller both
read the shared slot, which is why this only surfaced now.
A new generation now aborts the previous one before taking the slot, so at
most one runs and Stop reaches it by construction. The aborted one exits
without an error and leaves the context key to the new generation.
* fix(vscode): let Stop cancel every running commit generation instead of the newest
Having a new generation abort the previous one made Stop reach everything,
but it added a rule `main` never had, and it collided with "Generate for all
repositories": a generation started mid-run aborted the bulk run's current
repository, the bulk loop then moved to the next repository, and that one
aborted the newer request. The latest request was lost and a repository was
skipped.
Track every running generation in a set instead. Each registers its
controller before its first await and removes it when it settles; Stop
aborts all of them, and the context key stays set while any runs. Nothing
but Stop cancels a generation, so concurrent generations (the Generate
keybinding stays live while one runs, the bulk loop starts one per
repository) no longer cancel each other, and Stop still reaches all of them.
* test(llms): assert Cline surface headers on streaming requests too
The wire-level test only drove `doGenerate`, but commit-message generation
calls `createMessage`, which streams. A regression that dropped the surface
headers from streaming requests alone would have kept the test green while
the free-model 403 came back. Run the same assertions through `doStream`
(and check the request really is a streaming one) for both `cline` and
`cline-pass`.
* fix(vscode): time out undisplayed tool approvals (#14065)
* fix(vscode): drop the approval timeout, keep the resolver-ordering fix
The five-minute timer denied every pending approval, including ones the
user could see but had not answered yet. postStateToWebview() resolving
does not mean the ask was displayed (state delivery is fire-and-forget),
so the timer could not tell an undisplayed approval from a slow answer.
Keep registering the resolver before the state post so Approve/Reject
works while the post is in flight, and deny on a failed post only if
that post's approval is still the pending one; a late failure no longer
denies a later, unrelated approval. Log when an approval starts waiting
so stalls can be told apart from provider stream stalls in the output
channel.
---------
Co-authored-by: Dang Zitou <dengzitao888@163.com>
* 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
* 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
* fix(core): cache oversized external results without changing history
* fix(core): use volatile tool result URIs with iteration eviction
* docs(core): clarify cached result reads and truncation fallback
* docs(core): update tool result cache miss message
* fix(core): generate cache fallback agent IDs with crypto.randomUUID
* docs(core): keep cache guidance in result notices
* fix(core): preserve result recovery notices after cache eviction
* fix(core): make cached result recovery readable and text-only
* refactor(core): retain existing read_files availability and line limits
* docs(core): clarify exclusive read_files access to cache URIs
* fix(core): serialize structured cached results as multiline YAML
* fix(core): align recovery admission with persisted preview sizing
* fix(vscode): preserve cache URIs in workspace file reader
* fix(core): exclude MCP image blocks from recovery text
* fix(core): wrap long cached result lines so read_files can page single-line payloads
Cache reads go through read_files' MAX_LINE_CHARS cap, so a minified JSON
or other single-line payload was cut at 2,000 chars with no way to reach
the rest by line range. Wrap cached text at 1,000 chars on the read path
only; stored bytes stay verbatim and cache line numbers are a paging view.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* Revert "fix(core): wrap long cached result lines so read_files can page single-line payloads"
This reverts commit 07e166f3a2.
---------
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
@pierre/diffs defaults to a fixed 13px font and 20px line height, so diffs
ignored the desktop font-size setting. Drive both from the rem-based
--text-xs token instead.
* 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