* core: trigger compaction on the provider's actual input-token count
Compaction's trigger compared a character-based estimate (~3 chars/token)
against the model's input budget. Dense content -- disassembly, image dumps,
minified sources -- tokenizes far denser than that, so a transcript could
reach the real context ceiling while the estimate stayed under the threshold
and compaction never fired. Affected runs then filled the window and had
their turns squeezed down to a handful of output tokens.
The runtime now records the provider-reported input-token count for each
request and threads it to the prepare-turn pipeline as
previousRequestInputTokens; the trigger uses max(estimate, actual), so real
usage crosses the threshold even when the estimate does not. The estimate is
kept as a floor so the very first oversized turn is still caught before any
usage has been reported.
Also raises the default summarizer output budget from 4096 to 8192: a model
that reasons by default can spend a tight budget on thinking and return no
summary text, which skips compaction entirely.
* core: forward previous request input tokens through the runtime bridge
SessionRuntime.createRuntimePrepareTurn() rebuilds the prepare-turn context
field by field, so previousRequestInputTokens was dropped before reaching the
compaction pipeline. Every production core session therefore fell back to the
character estimate alone and the actual-usage trigger never engaged.
Forward the field alongside overflowRecovery and cover the bridge with a
regression test.
* core: scale the compaction budget by the observed token underestimate
The actual-usage trigger only moved the trigger; maxInputTokens still drove the
retention target off the unscaled estimate, so a compaction started by real
usage could retain too much and overflow again.
Divide maxInputTokens by max(1, actual / estimate) instead. The trigger test is
algebraically identical to comparing actual usage against the unscaled trigger,
while the target, message translation and projection costs now all correspond
to the provider's real limit in consistent estimate units. The factor never
loosens the budget, engages only on direct evidence of under-counting, and is
capped at MAX_INPUT_UNDERESTIMATE_FACTOR so a small estimate cannot collapse it.
* core: move the actual-count compaction test off the trigger boundary
The provider count was set to exactly the 1.05x multiple used for the
budget, which put the scaled trigger within 0.1 token of the estimate;
the test only compacted because of ceil rounding. Use 1.5x so it asserts
the behavior rather than the rounding.
---------
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
* feat(branding): add canonical Cline logo assets
* feat(vscode): refresh extension logo surfaces
* feat(web): update Hub and example branding
* docs: update Mintlify branding assets
* build(brand): lock asset generator tooling
* chore(branding): limit logo refresh to assets and required wiring
* fix(vscode): restore accessible logo names
* fix(branding): retain favicon contrast in dark browser tabs
* [2/3] Refresh desktop logo assets (#13966)
* chore(desktop): refresh native logo assets
* chore(menubar): refresh native logo assets (#13967)
part of logo migration
* docs: use latest adjusted navigation logo
* fix(hub): keep favicon visible in dark browser tabs
Point the Hub favicon at the dedicated favicon.svg and give it the same
prefers-color-scheme fallback as the example app. cline-logo-filled.svg
stays untouched since the sidebar renders it with dark:invert.
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>
* feat(telemetry): enable OTLP traces exporter in publish builds
Bakes OTEL_TRACES_EXPORTER=otlp into stable/nightly extension and CLI
publish builds. With the client trace pipeline (#13974) this turns on
cline-provider AI SDK trace emission at 100% task sampling, metadata-only
(no prompt/completion content).
Kill switches without a release: remote config openTelemetryTracesExporter
(extension) and the prod otel-collector's probabilistic sampler.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(telemetry): record prompt/completion content on traced requests
Sets CLINE_TRACE_RECORD_CONTENT=true alongside the traces exporter, so
cline-provider traces carry full message content (recordInputs/
recordOutputs) instead of metadata only.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* ci: assert content-capture env is inlined into the packaged extension
Stable gates pre-publish (right after vsce package); nightly packages
inside publish-nightly.mjs so its check is post-publish detection.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(ci): gate nightly publication on content flag inlining
* fix(telemetry): cover combined release packaging and verify trace artifacts
* fix(ci): type trace artifact checker and assert missing bundle fixture
* fix(test): use the .exe outfile Bun compile produces on Windows
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(telemetry): propagate Langfuse identity attributes through OTLP
* refactor(telemetry): move Langfuse integration ownership to llms
* refactor(ci): remove brittle trace artifact checks
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: abeatrix <beatrix@cline.bot>
* feat(shared): add disableCurrentDirectoryExecutableSearch for Windows
libuv resolves a bare program name on Windows by searching the child's
working directory before PATH, gated on the spawning process having
NoDefaultCurrentDirectoryInExePath defined. Cline spawns rg, git and
powershell with the user's workspace as cwd, so a repo shipping an rg.exe
would get it executed at index time. Expose a one-call helper that sets
Microsoft's documented opt-out, plus a Windows-only test that plants a
zero-byte cmd.exe and asserts the real one still runs.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix: stop Windows resolving bare program names through the workspace cwd
Call disableCurrentDirectoryExecutableSearch() at startup in every
process that hosts Cline core: the CLI (and the hub daemon it boots), the
desktop sidecar, the VS Code extension host, and the JetBrains cline-core
process. One environment variable covers every spawn site (file indexer,
search, simple-git, shell executor, hooks, MCP, taskkill) and is inherited
by children, which cmd.exe, libuv and Bun 1.4+ honor as well.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix: hand user-facing children the inherited NoDefaultCurrentDirectoryInExePath state
libuv reads the opt-out from the spawning process, so Cline's own protection
does not depend on children carrying it. But cmd.exe, Go, Bun 1.4 and
libuv-based children honor it as well, so letting them inherit Cline's
setting would silently change how a user's own bare program names resolve
(npm scripts running a cwd-local .bat through cmd.exe, MCP servers named
relative to cwd, hooks that spawn helpers).
disableCurrentDirectoryExecutableSearch() now latches the value the process
inherited, and withInheritedExecutableSearch() restores that state on the
child env at the spawn sites that run user-authored programs: the shell
executor, MCP stdio servers, hook subprocesses, the plugin subprocess
sandbox, and the VS Code host's HookProcess.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* Revert "fix: hand user-facing children the inherited NoDefaultCurrentDirectoryInExePath state"
This reverts commit b643517257.
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(desktop): stop sidecar holders with Restart Manager in the Windows installer
The hook added for the "Error opening file for writing" update failure
never terminated anything. Tauri builds an x86 NSIS installer, so its
nsExec launches the WOW64 32-bit powershell.exe, and from there
Get-Process returns an empty Path for every 64-bit process. The
Where-Object filter on $_.Path therefore matched no code-sidecar.exe on
any x64 machine, Stop-Process received nothing, and the installer went on
to write over the exe the detached Hub daemon still held. Updates from
0.0.25 through 0.0.28 failed exactly as 0.0.24 did.
Replace the process query with the Windows Restart Manager: register the
installed cline-app.exe and code-sidecar.exe, ask which processes hold
them, and force-shut those down. This is bitness-independent, scopes to
this install's files without matching on names or paths (a side-by-side
Cline Beta hosting the shared Hub is left alone because it holds a
different file), and covers every holder, including connector children
the Hub spawns from the same exe. Registering the main binary as well
stops a still-running desktop from respawning the sidecar mid-install.
If Restart Manager itself fails, print the error, offer Retry/Cancel
(the updater runs the installer passive, so the user who clicked
"Restart now" is watching), and abort rather than continue into a
half-replaced install. Silent installs take the Cancel default.
* refactor(desktop): flatten the installer hook's error handling with goto
Review nit: jump to end_session / failed / done labels instead of nesting
each Restart Manager step one level deeper. Same control flow and register
discipline; every exit path still ends an opened session and pops $0-$4.
Fixed issue: when a tool streams its output through chat_tool_call_update and the runtime's chat_tool_call_end event carries no output field (the tool already streamed everything), the desktop chat hook rebuilt the tool message payload with result: null. buildToolPresentation() reads a null result with no error as "still running", so a finished tool went back to a running/spinning presentation for the rest of the turn.
The sidebar time/project toggle showed the icon for the mode that was
already active. Swap it so the icon reflects the mode a click switches to:
folder icon while sorted by time, clock icon while grouped by project.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* feat(cli): add Enter key steering for empty input in queue
Allow empty input with Enter to promote the first queued prompt, enhancing navigation efficiency. Update hint text and add tests for key routing behavior.
* fix(desktop): bound queue steering waits and reject stale replies
* fix: steer the current queue head atomically in core
* ui update
* fix(core): type session ID in hub UI event test mock
---------
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
* telemetry: fire ui.panel_opened from initializeWebview on every host
ui.panel_opened was only captured from VscodeWebviewProvider, so the
JetBrains plugin never emitted it: the activation funnel there jumps from
user.extension_activated (core spawned on project open) straight to task
events, with no signal that the Cline UI was ever rendered.
The webview calls the initializeWebview RPC once on mount on every host,
so capture the same event there with source "webview_initialized".
Existing dashboards keyed on ui.panel_opened pick up JetBrains for free;
VS Code gains one extra source value on an event it already emits.
* telemetry: type the ui.panel_opened source
A PanelOpenedSource union on capturePanelOpened keeps the documented
source list and the call sites from drifting apart. Also note that on
JetBrains webview_initialized fires on every webview reload, so a
crash-restart loop emits one per restart.
* fix(desktop): add standard macOS margin to the Dock icon
The bundled icon.icns and the selectable runtime Dock icons were
edge-to-edge, so Cline rendered larger than neighbouring apps in the
Dock. Regenerate icon.icns from the source artwork scaled to 824/1024
of the canvas, and add padded copies of the runtime icons under
icons/app/macos/ that set_app_icon uses on macOS. Windows keeps the
existing edge-to-edge .ico and icons/app PNGs.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(desktop): refresh icon artwork and platform exports
* fix(desktop): limit artwork updates to macOS icons
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
Co-authored-by: Haley Park <haleypark.design@gmail.com>
* telemetry: report host-provided core spawn ordinal and reason as metadata
Out-of-process hosts spawn cline-core and sometimes respawn it: after a
crash, on a rollout fallback or demotion, or when the user restarts the
agent. From core's side every spawn is just another
user.extension_activated, so a core that crash-restarted three times and a
user with three project windows open are indistinguishable in telemetry.
Read CLINE_CORE_SPAWN_ORDINAL and CLINE_CORE_SPAWN_REASON from the spawn
env, the same contract as IS_DEV and CLINE_ROLLOUT_VARIANT, and attach
them as core_spawn_ordinal / core_spawn_reason metadata on both telemetry
pipelines (classic TelemetryService and the SDK handle), so they land on
every event. Malformed values are dropped field by field; in-process hosts
set nothing and get no fields, like host_plugin_version on the CLI.
The JetBrains plugin sets the env in a companion change.
* telemetry: type core_spawn_reason as the shared CoreSpawnReason union
Define CORE_SPAWN_REASONS / CoreSpawnReason once in @cline/shared next to
TelemetryMetadata and use the union in every metadata contract (shared,
classic TelemetryService, vitest stub) and in the env parser, so a
programmatic setMetadata/updateMetadata cannot introduce a reason the
parser's allowlist would have rejected.
* telemetry: derive the classic metadata's spawn fields from CoreSpawnTelemetryMetadata
Index into the parser's type instead of redeclaring both fields and their
doc comment, mirroring how extension_variant defers to
RolloutTelemetryMetadata. Also fix import order flagged by biome check.
Whether 4.1.18 ships as the combined package or the standalone SDK build is
a release-mechanics detail that may change again; keep the entry to what
users actually get.
The 4.1.18 entry claimed the package was now SDK-only and smaller. The
standalone publish path is timing out against the Marketplace, so this
version ships through the combined A/B workflow instead; state that
accurately, since the entry is pasted verbatim into the release and Slack.
The ext-sdk-bundle-rollout flag reached 100%, so stable releases now ship a
standalone build of main through ext-vscode-publish-stable.yml rather than the
combined legacy+next A/B VSIX. Rewrite the skill around that path and record
what is left to retire.
Also corrects two things the old text got wrong: nightly is manual-dispatch
only (the cron was removed because PublishNightly gained required reviewers,
so unattended runs sat waiting and starved their successors), and the stable
workflow tags before it builds, so a tag-push failure fails early rather than
leaving a published-but-untagged release.
@pierre/diffs 1.4 added a second required type parameter to FileDiffProps
while keeping defaults on the FileDiff component itself, so FileDiffProps<undefined>
fails with TS2314 under any 1.4.x. The package is declared as ^1.3.0, and the
release pipeline deletes bun.lock and re-resolves, so CI built against 1.4.2
while the committed lockfile pinned 1.3.6 locally.
Deriving the options type from ComponentProps<typeof FileDiff> compiles against
both 1.3.x and 1.4.x, and stops the emitted .d.ts from re-exporting a peer
dependency type whose arity changes between minors.
deepseek.r1-v1:0 was retired from the Bedrock catalog upstream, so the two
cases asserting a us. prefix for it started failing once the release regen
picked up the new catalog. The resolver is correct: it only prefixes a geo
profile when the catalog confirms that variant exists. Inject hasCatalogModel
in both cases so they state their catalog premise explicitly, matching the
isolation added for the fallback cases in #14017.
firstGeneratedModelId took the first entry of the cline-pass catalog, which
is release-date ordered and mixes cline-pass/*, cline-free/* and :free
models, so the default drifted to whichever free model shipped most
recently. Restrict the default to cline-pass/* ids, falling back to the
previous behavior when the catalog has none, and add a regression test
that asserts the tier rather than a specific model id.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* refactor(core): improve yolo mode
update submit_and_exit tool and yolo mode prompt
* feat(agents): retry transient provider errors before failing a run
A model turn that fails with a transient, provider-returned error is now
re-issued up to 3 times with exponential backoff (1s/2s/4s, capped at 15s)
before the error ends the run. Previously a single OpenRouter
"Provider returned error" (typically a forwarded 429) aborted the whole run
with exit 1, which cost the agent most of its runs against rate-limited
models.
Retryability is decided from the AI SDK's own typed signal rather than
message matching:
- isRetryableProviderError prefers APICallError.isRetryable, unwraps
RetryError, and walks AISDKError.cause; for non-typed errors it falls
back to the HTTP status, and finally to the single documented
"Provider returned error" provider quirk.
- Because the agent loop only sees a flattened error string, the flag is
computed at captureStreamError (where the structured error is still in
hand) and threaded through a new errorRetryable field on the finish
event, mirroring the existing errorClass path.
Non-retryable failures (auth, context-window overflow, other 4xx) and turns
that already produced tool calls are never retried, so a turn that would
otherwise succeed is unchanged. The backoff is abort-safe.
* llms: increase max retries limit to 5
Pass maxRetries=5 to AI SDK model calls (the SDK default is 2). The SDK
retries the initial request on 429/5xx/network failures with exponential
backoff that honors retry-after headers. Errors a provider emits mid-stream
(OpenRouter's "Provider returned error" after a 200) never reach this layer,
so the agent loop keeps its own turn-level retry; the two are complementary.
* fix(llms): judge RetryError retryability by its final attempt only
When the final error inside an AI SDK RetryError was not a typed instance,
isRetryableProviderError fell back to a structural walk over the whole
wrapper, including the earlier attempts the SDK had already retried away.
An earlier 429 could therefore make a final plain 400, or a statusless
transport failure, look retryable.
The structural fallback is now a standalone helper and, for a RetryError, runs
on the final attempt alone. The outer fallback for non-wrapped errors is
unchanged.
* fix(agents): only retry provider errors when the attempt left nothing behind
Tighten the transient-provider-error retry so a re-issued request can never
duplicate or repeat what the failed attempt already did:
- Do not retry once the attempt streamed any content (text, reasoning, media,
or local tool calls). Those deltas were already emitted and there is no
event to retract them, so a second stream would show the output twice.
Previously only local tool calls blocked the retry.
- Do not retry once the attempt recorded provider-executed tool activity.
That activity lives in message metadata rather than content, so the old
content-only check missed it and a retry could run the side effects again.
- Reset lastError, lastErrorClass, lastErrorRetryable, and lastErrorReported
at the start of every turn and before every provider-error retry. The
AgentModel contract allows a finish event with reason "error" and no error
payload; such an event previously inherited the class and retryability of
an earlier attempt. Overflow recovery's own inner request is left alone,
since its "nothing to compact" error reports the first attempt's message.
* fix(llms): keep request-start retries in one layer
The turn-level retry unwrapped the AI SDK's RetryError and, when its final
attempt looked transient, re-ran the turn. The SDK had already spent its
request-start retries with retry-after-aware backoff, so the two counts
multiplied: up to 6 SDK attempts times 4 turn attempts for one persistent 429.
Decide turn-level retryability with a dedicated helper that treats a
RetryError as terminal and otherwise defers to isRetryableProviderError.
Each failure class now has exactly one retrying layer: request-start failures
belong to the SDK's maxRetries; pre-output socket deaths and empty responses
to withEmptyResponseRetry, whose first doStream runs outside its retry loop
so it never re-runs request-start rejections; and mid-stream provider errors,
which the SDK never retries, to the turn-level retry alone. Document that
ownership next to MODEL_REQUEST_MAX_RETRIES.
* fix(llms): guard the RetryError check in isRetryableBeyondSdkRetries
`RetryError.isInstance` throws when the "ai" module is only partially
available, which is the case in test files that mock it with a subset of
exports. Wrap the check like the other typed checks in this file so the
classifier falls through instead of crashing the stream error handler.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* test(llms): expect errorRetryable on error finish events
Error finish events now carry `errorRetryable` alongside `errorClass`.
Update the exact-shape assertions in gateway.test.ts to include it; every
covered case is a non-retryable failure, so the expected value is false.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
* desktop: only refresh the live model list when the picker opens
Opening the composer model picker re-ran the whole load effect, which
first replaced providerModels/modelDetails with the bundled catalog and
then restored the live list once loadProviderModels resolved. For a
frame in between the trigger resolved against the bundled catalog,
flashing the raw model id (or a stale name) before snapping back.
Refresh only the active provider's live list on open, via a shared
applyProviderModels helper that the load effect and the
subscribeToProviderModels listener already duplicated.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* desktop: mark reasoning capability as catalog-backed after a picker refresh
The load effect flips reasoningCapabilitySource to "catalog" once live
models land; the picker-open refresh path should too, so a session that
started offline (source stuck at "fallback") trusts the live reasoning
data once a later refresh succeeds.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
The hub proxies every AgentRuntimeEvent to a client-contributed onEvent
hook as a capability round trip carrying the full session snapshot, and
the agent loop awaits it. For a streaming model that meant one ~200-300 KB
serialization, a persisted capability.requested row, four hub log lines,
and a blocking IPC hop per token (#14091).
Skip assistant-text-delta, assistant-reasoning-delta, and tool-updated in
the hook proxy; no client consumes them through a remote hook. Every other
event still reaches the hook unchanged.
Also set synchronous=NORMAL on the hub event log so the remaining
per-chunk delta rows stop costing an fsync each; WAL still syncs at
checkpoints and the log survives a process crash for reconnect replay.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(desktop): keep retrying the backend connection when the sidecar is slow to start
The webview asked the Tauri shell for the sidecar endpoint exactly once. When
the sidecar took longer than the shell's 15s poll (hub startup lock plus hub
daemon boot can exceed that on a slow Windows machine), the command returned
"desktop backend endpoint not ready" and the UI parked on "Desktop backend
unavailable" until the app was relaunched, where the same race repeated.
Schedule a reconnect with backoff after any failed connect, and drop the
cached endpoint on each attempt so a sidecar the shell respawned (which
issues a new approval token) is dialed with its current endpoint.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(desktop): keep the existing flat reconnect delay
Drop the consecutive-attempt backoff so the reconnect-after-drop path stays
identical to before apart from re-resolving the endpoint.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
On macOS with a Chinese/Japanese IME, the Enter that commits the current
composition also reached the composer's Enter-to-submit handler and sent
the message. Skip keydown handling while a composition is in progress
(isComposing, or WebKit's post-compositionend Enter with keyCode 229) so
Enter and arrow keys are left to the IME.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
The composer's ModelSelector loaded a provider's model list once, on
mount and on provider change, so the Recommended/Free tiers stamped by
the SDK feed stayed frozen until an app restart. The CLI and extension
refresh on picker open; do the same here via a new SearchCombobox
onOpen hook. The sidecar caches the feed and catalog, so repeat opens
are cheap.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
Sign in with Cline card now lists the three benefits (free model
promotions, ClinePass for generous usage across open weights models, no
API key needed). After a Cline sign-in the done step shows the current
free models from list_cline_recommended_models and a Get ClinePass link.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* docs: cline desktop page
* docs: move cline desktop page to usage section
- Relocate page from getting-started/ to usage/ and move nav entry to top of Usage group
- Remove Plan/Act mode bullet (not supported in Cline Desktop)
---------
Co-authored-by: Renee Huang <renee@cline.bot>
* desktop: show one actionable bubble for a credential failure
When a turn fails before the runtime takes the prompt (e.g. the Cline OAuth
refresh throws), the hub's run.failed reaches the webview as a detail-less
chat_done, then the send RPC resolves with the actual error. The second
report was deduped as a bubble but still overwrote the hook's error state,
so the chat rendered the detail-less bubble plus a banner with the detailed
copy underneath it.
appendTurnFailureMessage now tracks the bubble shown for the current turn:
a later, detailed report upgrades it in place and the error state follows
the bubble, so the failure renders exactly once.
Credential failures also carry a fix action in the bubble: Cline goes to
Settings -> Account (Settings -> Models keeps reporting a stale token as
signed in), other providers open Settings -> Models, and local-auth
providers keep pointing at their CLI.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* desktop: treat a rejected Cline refresh token as signed out
resolveFreshClineAuthToken fell back to the persisted access token even when
the refresh failed with OAuthReauthRequiredError. That token is dead too, so
the account request failed with a 401 and the Account page showed an error
card whose Retry failed the same way, instead of the sign-in prompt.
Return no token for a rejected refresh so cline_account reports the typed
not-authenticated result. Transient refresh failures still fall back.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* desktop: give a session that fails to start over credentials the same guidance
A fresh session applies the OAuth credentials in start, so a rejected Cline
refresh surfaced there as the raw runtime message with no hint or action.
Route start failures inside sendPrompt through the shared credential check
so they get the hint and the Sign in to Cline action too, and shorten the
Cline hint so it does not repeat the runtime's own wording.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* desktop: key the shown failure bubble on user bubbles, not the turn epoch
A retry submitted while the failed send is still settling is forced onto
the queue path, which bumps the turn epoch without adding a user bubble.
The late RPC report then missed the bubble already on screen and appended
a second copy. Track user bubbles appended to the live transcript instead:
a failure bubble stays the trailing one for its turn until the next user
bubble lands, which is what the old trailing-error check measured.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(desktop): make Cline and Cline Pass sign-out stick
Signing out of the Cline provider from the Providers page removes its
providers.json entry, but the legacy import re-adds it from the classic
extension's secrets.json (cline:clineAccountId / clineApiKey) on the next
sidecar command, so the user appears signed back in. Same root cause as
the ChatGPT/Codex sign-out fix (#14040), which only cleared Codex secrets.
Signing out of Cline Pass did nothing at all: its credentials are stored
under the "cline" provider (storageProviderId), so deleting only the
cline-pass entry left the account signed in.
Generalize the legacy-secret clearing helper to a per-provider key map
and, when a provider with a different storage provider is disabled, also
remove that storage provider's entry.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* test(desktop): cover Cline Pass sign-out cascading to the shared cline entry
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>