Commit Graph
7630 Commits
Author SHA1 Message Date
Saoud Rizwan 8d09157569 fix(cli): keep the npm install's on-disk plugin bootstrap
An npm install sets CLINE_WRAPPER_PATH and ships the SDK and its other
packages beside the binary, and standalone plugins rely on that host lookup
(for example importing yaml without node_modules of their own). Only
standalone binaries, which have none of that on disk, switch to the embedded
worker.
2026-10-14 06:21:01 +00:00
Saoud Rizwan 3e78cad580 Merge remote-tracking branch 'origin/main' into saoudrizwan/plugin-sdk-from-binary-1570
# Conflicts:
#	apps/cli/script/compile-binary.ts
#	apps/examples/desktop-app/scripts/build-desktop-runtime.ts
#	apps/examples/desktop-app/sidecar/plugin-bootstrap.ts
2026-10-14 06:05:39 +00:00
BeeandSaoud Rizwan 83ea9a41d1 refactor: Start the desktop and SSH remote hubs through the bundled Cline CLI (#14792)
* Let hosts choose the hub daemon launcher and defer spawn-only setup

Adds setHubDaemonLauncher so an embedding process can start the hub
daemon through a bundled Cline CLI instead of its own executable, and a
beforeSpawn hook on ensureDetachedHubServer that only runs when a new
daemon is actually started.

* Add programmatic options to cline hub ensure

hub ensure/start gain --json (URL + auth token), --allow-port-fallback,
--no-connectors and --login-shell-path, and the hub command accepts
--discovery-path for a dedicated discovery record. These let the desktop
app and SSH remotes start or reuse a hub through the CLI.

Also extracts the standalone binary compile step into
script/compile-binary.ts so other packages can build the CLI binary.

* Run SSH remote hubs through the Cline CLI

SSH remotes now upload the Cline CLI binary and start or reuse the remote
hub with 'cline hub ensure --json --discovery-path ...', stopping it with
'cline hub stop'. The dedicated remote helper entrypoint is removed;
cleanup records from older releases still stop through the old flag.

* Run the desktop backend on the bundled Cline CLI

The desktop app no longer compiles its own executable. It bundles the
self-contained Cline CLI (externalBin cline-cli) plus the backend as a JS
bundle that runs on the CLI's embedded runtime. At startup the backend has
the CLI run 'hub ensure', which reuses a compatible healthy Hub or starts
one, and any Hub it starts later also goes through the CLI binary. Linux
CLI copies are bundled for SSH remotes.

* Launch the desktop backend through the bundled Cline CLI

The Tauri shell resolves the bundled cline-cli and the backend bundle
(per-platform resource layouts) and starts 'cline-cli run' on it. The
Windows installer stops users of cline-cli.exe, and also stops and removes
the backend executable of earlier releases on upgrade.

* Recognize the CLI-hosted desktop backend in cline doctor --fix

* Update desktop publish workflow and docs for the bundled Cline CLI

Telemetry selfchecks and smoke tests run the backend bundle on the
bundled CLI, and the Windows signature gate verifies cline-cli.exe.

* fix(desktop): address runtime packaging review findings

* fix(desktop): preserve failed hub cleanup and handle exited daemons

* fix(hub): distinguish missing discovery records from read failures

* fix(hub): preserve discovery recovery while enforcing strict shutdown reads

* fix(cli): do not count hub commands as CLI activations (#14870)

* fix(cli): do not count hub commands as CLI activations

The desktop app runs `cline-cli hub ensure` on every launch and the SSH
remote flow runs `cline hub ensure` on every connect. Both went through
runCli(), which captured user.extension_activated unconditionally, so each
desktop launch was double-counted as a CLI activation and every SSH remote
host (with its own distinct id) showed up as a new CLI user.

Skip the activation capture for the hub subcommand; it is plumbing other
clients invoke, not a user activation. Hub-owned task telemetry is
unaffected since the daemon path never enters runCli().

* test(cli): clear the hub command mock between main.test.ts cases

* test(cli): stop stubbing the hub command module in main.test.ts

The module-wide vi.mock("./commands/hub") replaced createHubCommand with
a no-op, so `cline hub dashboard` (routed through createHubCommand) never
reached the dashboard mock and three dashboard tests failed. Drop the
module mock and assert the activation exclusion through
`hub dashboard --no-open`, which is already mocked.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-10-13 21:58:41 -07:00
Saoud Rizwan a6282853c7 fix(core): keep a plugin's default export first when it is a plugin
Only fall back to the named export when default is a CommonJS module.exports
wrapper, so ESM plugins pick the same export as in-process loading.
2026-10-14 04:31:54 +00:00
Saoud Rizwan d1521ee725 fix(cli): keep the on-disk bootstrap for the subprocess sandbox
CLINE_PLUGIN_MODE=sandbox spawns a separate runtime that cannot read the
binary's embedded worker.
2026-10-14 04:24:07 +00:00
Saoud Rizwan ae4eb5a105 fix(core): prefer a plugin's named export over default
A native import hands a CommonJS module's whole module.exports to default,
so a plugin written as exports.plugin = {...} resolved to the wrapper.
2026-10-14 04:24:06 +00:00
Saoud Rizwan 589803d0ce fix(cli): serve @cline/agents and @cline/llms to plugins too
The npm wrapper provided all five SDK packages, so plugins importing from
@cline/agents or @cline/llms worked there and would fail from the binary.
2026-10-14 04:24:06 +00:00
Saoud Rizwan b0eae87292 docs(sdk): describe the plugin worker compiled into the CLI 2026-10-14 04:21:54 +00:00
1932d5dc37 feat(vscode): Cline Cloud sessions - run tasks in a hosted sandbox from the extension (#13799)
* feat(sdk): support header-authenticated remote Hub connections

Ports the NodeHubClient resolveConnectionHeaders option from #13519 so a
client can authenticate a remote proxy's WebSocket upgrade with HTTP headers
(refreshed on every reconnect), and threads it through HubRuntimeHost and
RemoteRuntimeHost so ClineCore remote runtimes can reach Cline Cloud sandboxes.

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

* feat(vscode): cloud session runtime, control-plane client and history integration

Adds the extension-host side of Cline Cloud sessions:
- CloudSessionsService: REST client for GitHub App status/repos/branches and
  the outer session records (create/list/status/delete/rename/history)
- CloudSessionHost: an SdkSessionHost over RemoteRuntimeHost that dials the
  sandbox Hub through the api.cline.bot proxy with the account token, maps
  outer/inner ids and re-emits events so the existing chat pipeline is reused
- SdkCloudSessionCoordinator: starts/reopens cloud tasks, keeps sandbox
  connections alive to track running/finished status, notifies on completion
  and projects cloud sessions into task history
- CloudService proto + handlers, NewTaskRequest execution target, history
  cloud fields/filter, ext-cloud-sessions feature flag

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

* feat(vscode): cloud sessions UI — home task-target panel, running strip, history and account

- Home screen: Local | Cloud chooser above the composer with GitHub repo and
  branch pickers (prefilled from the workspace's origin remote and branch) and
  an onboarding card that walks through connecting GitHub, polling until done
- Running-in-the-cloud strip and cloud status pills in Recent/History, plus a
  Cloud Only history filter
- Task header cloud badge (repo @ branch, opens the dashboard); cloud tasks
  render inline diffs, hide checkpoint/restore and only attach images
- Account view: Cline Cloud card with GitHub connection status

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

* fix(vscode): cloud task fixes found in manual testing

- claim the task-view generation after clearTask (which bumps it), so cloud
  task start/open is not treated as superseded and left on Thinking
- track completion from the agent done event and keep completed/failed over
  the trailing idle status so History shows Done instead of Cloud
- stop the branch picker from re-querying on every render
- readable popover text in the repo/branch pickers; hide cost chips on cloud rows

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

* docs(vscode): cloud sessions follow-ups and test stub for org switch reset

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

* fix(vscode): make cloud sessions Act-only

Match the desktop app and the cloud dashboard: cloud sessions are created in
Act mode regardless of the selected mode, no per-turn mode is sent, the host
refuses Plan/Act toggles while a cloud session is active, and the webview pins
the toggle to Act (disabled, with a tooltip) while a cloud task is shown or
about to start.

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

* test(vscode): add local cloud session boundary

* fix(vscode): preserve cloud outcomes and stale-work ownership

* fix(vscode): drain cloud cleanup before account changes

* fix(vscode): build cloud sandbox config through the Cline path

* docs(vscode): clarify cloud proxy rollout boundary

* fix(vscode): close cloud picker when leaving chat

* fix(vscode): wait for cloud session before opening dashboard

* fix(vscode): keep account history in document flow

* fix(vscode): report available cloud task usage

* fix(vscode): cancel cloud tasks during provisioning

* fix(vscode): make cloud model billing explicit

* fix(vscode): hide local approvals for cloud tasks

* fix(vscode): resolve cloud prompt before session adoption

* fix(vscode): seed local cloud development profile

* fix(vscode): sanitize local cloud fixture errors

* fix(vscode): handle cloud state before hydration

* fix(core): honor proxies for remote Hub WebSockets

* fix(vscode): preserve cloud task IDs

* fix(vscode): explain cloud auto-approval

* test: repair cloud and proxy CI fixtures

* fix(vscode): resolve cloud status after restart

* fix(vscode): resolve flags for restored accounts

* fix(vscode): keep cloud tasks in the cloud and in Act mode

* test(vscode): let the local cloud fixture delay model replies

CLINE_LOCAL_CLOUD_MODEL_DELAY_MS holds the scripted reply so cancellation and navigation can be exercised while a cloud task is still thinking.

* fix(vscode): cancel a cloud task that is still provisioning

Cancelling during "Starting a cloud sandbox..." did not stop the start:

- Cancel arriving while initTask awaited remote config found no pending
  start, so the sandbox went on to provision, connect and run the prompt
  behind a "Resume Task" view, and the user paid for a sandbox they had
  cancelled. initTask now claims the cloud start before its first await.
- Cancel arriving during the readiness poll took effect only after the
  sandbox came up. createSession accepts an abort signal that stops the
  poll, so the unused record is deleted as soon as the control plane
  answers.
- clearTask (New Task) now cancels a pending cloud start too; a
  provisioning task has no SDK session for the task controller to end.

The local cloud fixture reports `ready` like the hosted control plane and
accepts CLINE_LOCAL_CLOUD_PROVISION_DELAY_MS to hold new sandboxes in
`provisioning`, so this window is reproducible. The cloud Vitest config
restores the Zod namespace export the same way vitest.config.ts does.

* fix(vscode): keep cloud status displays truthful and hosts open while in use

A status check that resolved a cloud task as idle or finished closed the
host while openCloudTask could still be reading messages or attaching
the session on that same host. Entries now count the openCloudTask calls
using them, and neither status resolution nor the idle sweep disposes a
host that is pinned or belongs to the displayed task.

Cancelling a cloud start while remote config was still loading left the
turn phase at streaming after the view had returned home. initTask no
longer sets the phase for cloud starts; the coordinator sets it once the
start is still current and its task view is installed.

useResolvedCloudStatuses kept a resolved status after history reported
a newer one, so a task resolved as running stayed "Working" after it
completed. The hook now returns statuses only for tasks history still
reports as unknown, and the running strip lists a task only while its
effective status is active or unconfirmed.

An unknown status showed a spinner labelled "Checking" for as long as
connections kept failing. It is now shown as Unconfirmed with a static
icon; only provisioning and running animate.

* test(core): run the proxy tunnel fixture with a real Node executable

When Bun runs vitest it reports its own Node-compatible shim as
process.execPath and places it first on PATH. That runtime ignores
NODE_EXTRA_CA_CERTS, so the spawned fixture failed TLS verification on
the Windows CI runner. The test now looks past the shim for an installed
Node and is skipped when none is available.

* fix(vscode): remember cloud task status across restarts

After a restart every cloud row in History showed "Unconfirmed" and
joined the running strip until a socket to each sandbox re-learned its
status, and the first status a socket reported bumped the row's
timestamp to now, reordering History (CLINE-3307, CLINE-3424,
CLINE-3418).

The coordinator now writes the last settled status it confirmed over a
live connection to global state (`cloudSessionStatuses`, keyed by
session id with the observation time) and reads it back when it first
sees a record. The remembered status is trusted only while the control
plane's `updatedAt` is no later than the observation plus a one-minute
grace, because the control plane touches `updatedAt` when a client
connects; a later touch means another client may have run the agent, so
the row falls back to unknown and is resolved live as before. Active
statuses are never remembered. Ids the account no longer lists are
pruned on every list refresh.

A connection that only learns the current status no longer counts as
activity; the row's timestamp moves only when the status changes while
connected or the user sends a follow-up.

* fix(vscode): settle account switches without blocking History

* fix(vscode): refresh stale cloud outcomes without restarting

* test(vscode): wait for cloud run cleanup before removing sandbox

* fix(vscode): reserve switch errors for account transitions

* refactor(vscode): remove unused cloud accessors and refresh notes

* test(vscode): seed expired sessions and a 402 model reply in the cloud fixture

The loopback fixture only ever served live sandboxes, so the read-only
expired path and the out-of-credits path could not be exercised without a
day's wait or real spend.

CLINE_LOCAL_CLOUD_SEED_EXPIRED=1 seeds two Personal sessions whose
sandboxes are gone: one with an archived transcript, one that answers
/history with the control plane's 404 "no history captured". Their
WebSocket upgrade returns 410 like the real proxy and /status reports
"expired". CLINE_LOCAL_CLOUD_INSUFFICIENT_CREDITS=1 makes every scripted
model reply the hosted API's 402 insufficient_credits body.

* fix(vscode): explain expired cloud sessions instead of showing an error

Opening a cloud task whose sandbox the control plane retired showed a red
error row and, when no transcript had been archived, a synthetic Done row
above it. QA read this as data loss (CLINE-3322).

The row is now an informational notice that says the sandbox was retired
after 24 idle hours and names the repository to start a new cloud task on.
It tells three cases apart: the archived conversation is shown above it;
no conversation was archived (the control plane answered 404); or the
archive could not be loaded right now, with the error. An empty transcript
is no longer rendered, so no Done row appears. The History pill reads
Archived with a tooltip explaining the retirement.

* fix(vscode): show the out-of-credits card for cloud task failures

A cloud turn that failed on the Cline API's 402 insufficient_credits
reply ended with Retry / Start New Task and no explanation (CLINE-3376).
A Hub-hosted session does not forward the agent's "error" event; the
client learns of the failure only from run.failed, which the runtime host
turns into done(reason: "error") with the failure message as its text.
The translator recorded the error outcome for that event but rendered
nothing.

done(reason: "error") now renders the same api_req_started and
api_req_failed rows as an "error" event, once per turn, so ErrorRow shows
the Buy Credits card (or the plain error) for cloud tasks as it does for
local ones. Both paths share one helper.

* fix(vscode): explain cloud sessions deleted or retired since History loaded

Opening a cloud task deleted from the dashboard showed "Could not connect
to this cloud session: Unexpected server response: 404" and left the row
in History (CLINE-3377). The Hub client reports only the HTTP status of
the refused upgrade.

A failed connection now asks the control plane for the session's status.
A deleted session shows a notice and leaves History; a session retired
since the list was fetched opens as expired, with its archived
conversation if one exists. Other failures keep the connection error.

The loopback fixture now answers an unknown session's upgrade with 404,
as the hosted proxy does, instead of 401.

* fix(vscode): only suggest cloud branches that exist on GitHub

The cloud branch picker was prefilled with the workspace's checked-out
branch even when it had never been pushed. The sandbox clones from
GitHub, so the session then failed to start (CLIENTS-109).

The workspace default is now the remote branch the checkout corresponds
to: its upstream on origin when one is configured, otherwise the same
name when origin has it. A local-only branch yields no suggestion and the
picker falls back to the repository's default branch. Branches listed
from the GitHub API were already remote.

* fix(vscode): accumulate streamed text for cloud tasks

A cloud task's reply flickered chunk by chunk and only appeared whole
when the message finished (CLIENTS-111). The translator showed each
content_start's `accumulated` text, falling back to the chunk alone. A
local session sends the running total, but a Hub-hosted session forwards
assistant.delta as a bare chunk, so each update replaced the last.

The translator now keeps the running total itself when `accumulated` is
absent, and clears it with the other per-block streaming state.

* fix(vscode): show the cloud model in the composer while Cloud is selected

With Cloud selected, or a cloud task open, the composer footer still
showed the local provider and model, e.g. anthropic:claude-opus, although
the sandbox runs a Cline model (CLIENTS-116).

The extension now reports cloudModelId in state: the model the displayed
cloud task runs on, else the one a new cloud task would start with. The
start path and the label share one function, so they cannot disagree.
The composer shows it as cline:<model> whenever it pins the mode to Act
for Cloud.

* fix(vscode): release the retained host of a deleted cloud session

When a session opened from History turns out to have been deleted, its
entry leaves the registry. A connection retained from an earlier status
check stayed open with nothing to watch; dispose it with the entry.

* fix(vscode): keep expired-session and cloud-model displays truthful

An archived cloud transcript was rendered with the final turn marked
completed, so the last reply of a task that was still running when its
sandbox retired showed as a green Done box. Retirement is not
completion; the final reply now stays a plain text row.

An archive that exists but holds no messages rendered nothing yet the
notice pointed at "the conversation above", and the History tooltip
promised a readable conversation for every Archived row. An empty
archive now reads as none, and the tooltip says to open the session to
see whether one was saved.

With a non-Cline local provider the composer showed the cached or
fallback recommendation while the start path refreshed the list first
and could pick a different model. The start now uses the model shown
when the user submitted, snapshotted before any await, and the
composer warms the recommendation cache once so the label moves from
the fallback to the live recommendation before the next submit.

* fix(vscode): confirm an account switch overtaken by its own auth update

switchOrganization confirmed a switch by re-reading the organizations
and treating an answer overtaken by a newer read as no answer. The
extension's auth refresh after the PUT starts such a newer read, so a
switch that succeeded returned false and the ClinePass switch control
asked the user to retry it.

Every read now returns the server's answer; only the newest read still
writes context state.

* fix(core): connect header-authenticated Hub sockets under Bun

The desktop sidecar runs the cloud session controller under Bun. Since
a2cefba05 the header-authenticated Hub client imported the npm `ws`
package through a `node-ws` alias so a proxy agent could be attached.
Under Bun 1.3.13, the toolchain this branch was pinned to, Bun's
node:http client never emits `upgrade`, so the npm `ws` treats the 101
as an ordinary response and connect() fails with "Unexpected server
response: 101". Cloud sessions in the desktop app could not connect.

Import `ws` directly again. Bun substitutes its own client, which reads
the proxy URL and Proxy-Authorization from the same `agent` option that
Node's `ws` uses, so one socket shape tunnels on both runtimes and the
`node-ws` dependency and its @ts-expect-error go away.

The tunnel test now runs the fixture under Bun as well as Node. Bun's
TLS client does not read NODE_EXTRA_CA_CERTS, so the Bun leg tunnels a
plain ws:// destination; it still checks the CONNECT, the upgrade and
where the credentials land. With the `node-ws` import the Bun leg fails
on 1.3.13 and the Node leg passes, which is the gap that let this ship.

* fix(vscode): retry the cloud model recommendation after a failed fetch

The composer shows the model a new cloud task will run on. When the
local provider is not Cline that is the top recommendation, warmed once
by the coordinator. A fetch that failed returned the built-in fallback
without caching it, but the coordinator kept its warm-up handle, so it
never asked again and the fallback stayed in the composer and in new
sandboxes until the extension restarted.

Release the handle when a fetch settles without filling the cache, so
the next state post retries.

* fix(vscode): stop the recommendation warm-up from polling a failing endpoint

The warm-up posted state after every fetch, including one that returned
the built-in fallback. That post reads the model again, which starts the
next fetch, so a recommendation endpoint that keeps failing was polled
without bound while Cloud was selected with a non-Cline provider.

Post state only when the fetch filled the cache. A failed fetch still
releases the handle, so the next read the user causes retries.

* fix(vscode): disable the composer while a cloud sandbox is provisioning

The composer allows sending during the streaming phase so follow-ups can
queue on a running task, and a cloud start sets that phase while the
sandbox is still provisioning. There is no session to queue on yet, so
askResponse hit the expired-session guard and answered "This cloud
session is no longer running" while the sandbox kept starting, dropping
the message.

The displayed cloud task now reports status provisioning while its start
is pending, and the composer keeps submit disabled on that status so the
draft stays in place until the sandbox is running.

* fix(vscode): re-enable the composer after a failed cloud start

The failure path posts state while the start is still pending, so the
composer kept seeing "provisioning" and stayed disabled. Post state once
more after the pending start is cleared. The askResponse guard, still
reachable through the extension API while the composer is disabled, now
says the sandbox is starting instead of that the session is gone.

* style(vscode): wrap two JSX lines the CI formatter rejects

Biome 2.4.5 on main wraps these attributes; the branch's Quality Checks
failed on them after the rebase onto that formatter version.

* test(vscode): pin the composer's provisioning status through a failed start

The displayed cloud task reports provisioning while its start is pending,
and the last state post after a failed start no longer carries it, so the
composer re-enables without waiting for an unrelated refresh.

* fix(vscode): keep a draft typed while a cloud task is provisioning

handleSendMessage cleared the composer a second time after its RPC
resolved. Every send path already clears the draft before the RPC, so
the second clear only ever removed text the user typed while the RPC was
pending. A local newTask resolves too quickly to notice; a cloud newTask
resolves only after the sandbox is provisioned, so the draft the disabled
composer was holding for the user vanished when the sandbox came up.

* fix(vscode): follow main's rename of the sign-in retry field

#14628 replaced pendingClineAuthRetryPrompt with pendingClineAuthRetry.
The cloud coordinator's clearTask callback and the cloud branch of
initTask still cleared the old field, which no longer exists.

* fix(vscode): tear down cloud sessions when ClinePass switches the account

Selecting the ClinePass provider switches the active account to Personal
by calling switchAccount directly. The Account view's organization switch
runs the same call inside resetCloudSessions, which detaches the displayed
cloud task and its authenticated hosts and drains pending cleanup before
the account changes. Without that, an organization cloud task stayed on
screen after the account became Personal, and a still-pending DELETE could
use the new account's credentials.

Route the ClinePass switch through the same reset, and only when an
organization is active: every API configuration update passes through
this path, and the reset would otherwise clear a displayed cloud task for
a model change that switches nothing.

* fix(vscode): page History's Cloud Only filter after filtering

The Cloud Only filter ran on a page already cut from the merged
local-and-cloud list, and hasMore was computed from that page. With more
than a page of newer local tasks the first page came back empty while
cloud tasks existed further down, and hasMore said there was nothing more.

When Cloud Only is on, list the whole history, filter, then apply offset,
limit and hasMore to the filtered records.

* fix(vscode): stop reporting a model change the cloud sandbox never made

CloudSessionHost.updateSessionModel called an optional method that
RemoteRuntimeHost does not have, then recorded the new model anyway.
Choosing a different local model while a cloud task was displayed made
the composer name that model while the sandbox kept running on the one
it was started with.

The Hub protocol has no command to change a running session's model, so
drop the method. The lifecycle already treats a host without it as
unable to switch, and the composer keeps naming the model the sandbox
was started on.

* fix(vscode): hide a cloud task's cost until the sandbox reports usage

The task header decided whether to show the cost badge from the local
provider's cost display setting. For a cloud task with no usage yet that
showed $0.0000, which reads as a confirmed zero for a charge that is not
known. Cloud tasks now show the badge only when the sandbox has reported
usage; local tasks keep the provider-based decision.

* fix(vscode): do not offer to open a cloud task's files in the local editor

Cloud edits render through DiffEditRow, whose file name and open icon
call openFileRelativePath. The path is relative to the sandbox's
/workspace, so clicking opened an unrelated local file or failed. The row
now takes canOpenFile; ChatRow passes false for cloud tasks, which drops
the click action and the open icon and leaves the path as plain text.

* fix(vscode): keep the cloud repository and branch choice truthful

Two ways the stored cloud target could name something the current
account cannot start a sandbox on:

- The repository selection persists across account changes. When the new
  account has no accessible repositories the panel showed onboarding, but
  the old URL stayed stored, so the composer still treated the target as
  ready to submit. Clear the stored repository once the account's list
  is known and does not contain it.

- The workspace branch was persisted from the local remote-tracking ref
  without checking GitHub. A branch deleted on GitHub but not pruned
  locally was suggested and submitted, and provisioning failed. Start on
  the repository's default branch and ask GitHub for the workspace branch
  by name; the unfiltered branch list is paginated, so its first page
  cannot decide. Suggest the branch only if GitHub returns it and the
  default branch is still selected, so a branch the user has since chosen
  stays.

* fix(vscode): reconcile retained cloud connections with the account's list

Two ways a cloud session's record and its connection could disagree:

- A list refresh kept any entry that still had a host, so a task deleted
  from the dashboard stayed in History with an open authenticated
  connection, and a 401 cleared the entries without closing their hosts.
  An entry whose record has left the list, or every entry on a 401, is now
  dropped and its host closed. The displayed task is the exception in both
  cases: its host stays so that opening it can explain a deletion, and so
  that signing in again after a 401 leaves a task that can continue.

- When a sandbox retires after History was listed, its WebSocket rejects
  with 410 while /status may answer 200 with status "expired". The probe
  treated every successful /status as "not expired", so the task opened
  to a connection error instead of the retirement notice. A 200 that says
  expired now counts as retirement, like a 410.

* fix(vscode): subscribe monitored cloud tasks to their Hub event stream

The Hub streams a session's events only to clients subscribed to it, and
the runtime host subscribes when it first sends a command for that
session. A connection opened only to resolve a History row's status sent
none: it listed sessions, read the status, and then waited on an event
stream that was never opened. A turn finished by another client left the
row on Running and the completion notification never fired.

Listing the task's pending prompts after discovery sends that first
command and opens the stream.

* test(vscode): keep the local cloud fixture alive when a client drops its socket

An extension host that reloads or exits closes its bridged WebSocket
without a close frame. The bridge attached no "error" listener to either
side, so ws raised an unhandled error and the fixture process exited,
taking every sandbox with it. Terminate the failed socket instead.

* Own the account boundary in switchAccount

Four paths changed the active account (the Account view, ClinePass
selection and two remote-config paths) and only one of them tore down
the previous account's cloud task and connections first. Each caller
had to remember to wrap the switch, and two did not.

switchAccount now runs every change inside a boundary the controller
installs once (the cloud coordinator's reset), and serialises switches:
one requested while another is in flight waits for it and then decides
against the account as it is then. A caller that read a stale account
cannot skip a needed switch or repeat one that just happened, so the
ClinePass gate and the Account view's pending-switch map are gone.

* Resolve the cloud task target in the extension

The task-target panel computed the whole stored target from five
effects (account repositories, workspace remote, GitHub branch check,
repository click, Local/Cloud click), each writing the result back.
Any two could interleave and the later write won: a branch check that
landed after the user clicked Local flipped the target back to Cloud.

The extension now stores only what the user chose, one field per
choice (Local/Cloud, a repository, a branch), and resolves the rest on
every state post: the stored repository if this account can reach it,
else the workspace's, else the first; the user's branch, else the
workspace branch once GitHub confirms it exists, else the default.
Nothing derived is written, so no lookup can override a click, and a
repository chosen under another account is never submitted. Failed
lookups retry after 30 seconds; the GitHub connection is cached per
account and shared with the Account view.

The webview panel renders the resolved view and sends each click as
one choice.

* test(vscode): keep the cloud fixture alive when the Hub socket fails early

The fixture's session proxy attached its error handler to the upstream
Hub socket only after "open". An error during the handshake was
unhandled and exited the fixture process, which the extension then saw
as "Timed out connecting to hub after 8000ms". Attach the handler at
construction for both sides of the bridge.

* Refresh aged cloud-target inputs and always restore the server account

The task-target resolver cached repositories and the workspace checkout
for as long as the account and directory stayed the same. Repository
access changes in the browser and the checkout changes in the terminal,
and neither notifies the extension, so the picker could keep offering a
revoked repository or suggesting the previous branch. A loaded input now
ages out (repositories after 60 s, the checkout after 10 s): a read past
that returns the cached answer and reloads behind it.

switchAccount skipped the request when the cached account already
matched. The server's active account is shared by every client on the
login, so another client may have moved it. The request is now always
sent; only the cloud teardown is gated on the cached account changing.

* Retry a failed cloud start instead of declaring the session gone

A cloud task whose sandbox never came up shows "Failed to start the cloud
session" with Retry / Start New Task, but Retry appended "This cloud
session is no longer running" and the footer then stayed disabled: the
webview holds its recovery claim until the turn phase advances, and the
refusal never advanced it.

The coordinator now hands the failed input to the controller, which keeps
it as the same pending start retry a "Sign in to Cline" error uses. Retry
runs the cloud task again on the same repository and branch; a prompt
typed into the composer starts one with that prompt. Refusing a response
to a session that is gone sets the turn phase, so the footer releases
its claim either way.

The task-header tooltip no longer doubles its full stop when the task
has no repository. The local cloud fixture starts a sandbox's Hub when
the session becomes ready rather than on the first socket upgrade, so a
cold start cannot outlast the client's connect timeout.

* test(vscode): let the cloud fixture refuse session sockets

CLINE_LOCAL_CLOUD_REFUSE_SOCKETS=1 answers every session socket upgrade
with 503, so a cloud start fails after its sandbox is ready and the
footer's Retry path can be exercised by hand.

* Replace the carried-forward transcript when the new epoch's snapshot lands

Retrying a failed cloud start appended the new attempt below the old
one instead of replacing it. The new task's first partials reach the
webview before the debounced state snapshot of the new epoch; the
reducer deliberately carries the old rows forward on a lone newer-epoch
partial, but then treated the snapshot as same-epoch and merged, so the
old rows were never removed. The replica now remembers that it carried
rows forward, and the first snapshot at that epoch replaces them while
keeping the newer-epoch partials already held.

A prompt typed after a failed start also left its optimistic bubble
below the new task row: the bubble is user_feedback, but a restart is
confirmed by a task row. Any user-authored row with the same text and
attachments now confirms the bubble.

* test(vscode): keep the cloud scope fixture complete

Supply the failed-start callback required by the cloud retry path and
assert that the coordinator hands it the failed task and original input.
Check the fixture keys against the options interface before casting its
partial service doubles, so a new required option fails typechecking.

* fix(vscode): send Cancel to the task on screen, not a cloud start left behind (#14794)

A cloud start stays pending after the user opens another task from
History. Cancel always cancelled that pending start first, so cancelling a
running task (cloud or local) cleared its view instead, and a cloud turn
kept running in its sandbox. Only cancel the pending start when its own
view, or no task, is on screen.

* fix(vscode): delete cloud sandboxes left provisioning by a reload or quit (#14791)

* fix(vscode): delete cloud sandboxes left provisioning by a reload or quit

A terminating extension host cannot reach the network (VS Code's fetch
resolves proxies through the window it is detaching from), so the cleanup
DELETE a reload or quit triggers during provisioning never leaves the
process and the sandbox is orphaned. Record each provisioning sandbox in a
per-extension-host file under the Cline data directory until its task
starts or its cleanup is confirmed, and let the next extension host delete
records whose owner has exited.

* fix(vscode): recover abandoned cloud starts only when it is safe

Address review of the abandoned-start journal:

- Keep a record until the control plane confirms the sandbox is gone. A
  401, 429 or 5xx no longer erases it; only a successful DELETE or a 404
  under the account and endpoint that started it settles it.
- Keep a record until the sandbox has started the first turn, not just
  until the send was queued. Recovery connects to a sandbox before
  deleting it and leaves one that already holds a conversation, in case the
  host exited after the turn was accepted but before recording that.
- Name each journal by pid and extension-host lifetime and refresh it while
  it has records, so an earlier lifetime of this pid, or a pid reused by
  another process, reads as abandoned instead of being skipped or
  overwritten.
- Run recovery as one scope operation of the account that started the
  sandboxes: an account change waits for a DELETE in flight and stops the
  rest, nothing runs after disposal, and reset and dispose cancel the
  recheck timer.

* fix(vscode): never recover a cloud start that may still be live

- A sandbox's saved transcript stays empty until the first turn's
  iteration ends, so recovery could delete a task whose first turn was
  running. Treat any Hub session status other than idle as a turn the
  sandbox has taken.
- Drop the journal heartbeat. A missed refresh does not prove the owner
  exited (it may be paused in a debugger or asleep), so a journal whose
  pid is alive is never taken; a pid reused by another process only
  delays recovery until that process exits.
- Bound the sandbox inspection with a timeout and stop awaiting its
  detach, so an unresponsive sandbox cannot hold the account scope.

* refactor(vscode): simplify abandoned cloud start recovery

Drop the record right before the first prompt is sent instead of waiting
for the sandbox to report turn acceptance. A surviving record then means
the prompt never left the extension host, so its sandbox holds no user
work and recovery can just DELETE it. This removes the turn-acceptance
tracking in CloudSessionHost and the sandbox inspection (connect, status
check, transcript read, timeout) that recovery needed to tell an orphan
from a live task.

Also fail the start when the record cannot be written, so its normal
cleanup DELETE runs while the host can still reach the network, and treat
only ESRCH as proof that a journal's owner has exited.

* fix(vscode): fail a cloud start whose pending record cannot be dropped

Recovery deletes any recorded sandbox without inspecting it, so the record
must be gone before the first prompt is sent. If dropping it fails, fail the
start instead of sending; its normal cleanup then deletes the sandbox. After
a confirmed deletion a leftover record is harmless, so that path only logs.

* fix(vscode): preserve provisioning tasks opened from History (#14793)

Keep a pending cloud start alive when its own History row is opened,
reject older selections, and relist its sandbox when startup settles.
Carry the task-view fence across clearTask instead of claiming again.

Retain abandoned-start journaling from the target branch and verify its
record survives own-row reopening until immediately before the first send.

* fix(vscode): keep a failed cloud start's images when Retry is clicked

The webview's Retry response omits images, which protobuf decodes as an
empty list, so `images ?? retry.images` dropped the attachments and the
retried sandbox ran on the text prompt alone. Fall back to the failed
start's images unless the response actually carries some.

* fix(vscode): stop sending the account token into cloud sandboxes (#14970)

* fix(vscode): keep cloud sessions out of Delete All History (#14967)

* fix(vscode): keep cloud sessions out of Delete All History

* fix(vscode): report the actual Delete All History count

* fix(vscode): cancel a provisioning cloud start when the account changes (#14968)

* fix(vscode): forget a remembered cloud status once the agent runs again (#14969)

* fix(vscode): forget a remembered cloud status once the agent runs again

* test(vscode): cover forgetting a remembered status on connect

* feat(vscode): resume suspended cloud sandboxes and use the SDK's cloud client (#14973)

* test(vscode): let the local cloud fixture suspend and resume sandboxes

* fix(vscode): resume suspended cloud sandboxes instead of treating them as unknown

* fix(vscode): reopen cloud tasks whose last turn failed

* refactor(vscode): talk to the cloud control plane through the SDK's CloudSessionApi

* feat(vscode): show cloud provisioning progress and dashboard renames

* refactor(core): export the cloud session system prompt

* feat(vscode): give cloud sandboxes the SDK's shared prompt and work-branch rules

* perf(vscode): keep the local model catalog out of cloud session.create

* docs(vscode): update cloud session follow-ups for the SDK alignment

* fix(vscode): reconnect from scratch after resuming a suspended sandbox

* fix(vscode): link an organization to its own GitHub connect page

* test(vscode): only auto-suspend resumable fixture sandboxes

* fix(vscode): show the resume notice when a suspended cloud task is opened from History

showTaskWithId only dismisses the History view after the open completes,
which for a suspended sandbox is after the whole resume. The notice the
coordinator posts stayed covered and History sat unchanged for the
duration. Bring the chat view forward right after the notice is posted.

* fix(vscode): keep the UI on the server's account after a slow switch (#14971)

* fix(vscode): keep the UI on the server's account after a slow switch

* fix(vscode): retract a switch error only from the newest account read

* feat(vscode): opt-in Cloud toggle in Settings behind ext-cloud-sessions (#15018)

* feat(vscode): add opt-in Cloud toggle in Settings behind the ext-cloud-sessions flag

* fix(vscode): refresh History when the Cloud toggle changes

* fix(vscode): close four cloud-session gaps found in review and QA

- A cancelled cloud start left a ghost STARTING row in History. A list during
  provisioning adds an entry for the new record before the start holds one, and
  cleanupUnusedSession only dropped the caller's entry. It now drops the entry
  by session id and invalidates History after the DELETE settles.
- A failed DELETE left entry.host pointing at a disposed host, so a reopened
  task stopped reporting status. The host is cleared before disposal.
- Switching repositories left the previous repository's branches selectable
  while the new list loaded, so a stale branch could be saved against the new
  repository. The list is cleared and the picker disabled until it loads.
- Attached files were silently dropped from a cloud start. The composer now
  refuses the send and says why.
- compile-tests could not resolve @cline/core/cloud under classic node
  resolution; tsconfig.test.json maps the subpath to the built declarations.

* docs(vscode): track cloud session follow-ups as GitHub issues instead of a checked-in file

* merge: preserve latest VS Code cloud updates with reviewed fixes

* Saoudrizwan/cloud greptile fixes 26ac (#15073)

* test(vscode): stub @cline/core/cloud for the CommonJS integration host

* fix(vscode): keep the open cloud task's model visible when Cloud is turned off

* fix(vscode): restore a cloud runtime the Hub lost after a turn reports it missing

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
Co-authored-by: Dominic Cooney <dominic.cooney@cline.bot>
Co-authored-by: Dominic Cooney <dominiccooney@users.noreply.github.com>
Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
Co-authored-by: Dominic Coon <dominic@cline.bot>
Co-authored-by: John Choi <john.choi@cline.bot>
2026-10-13 20:31:05 -07:00
Saoud Rizwan 7574c5d9bd feat(cli): serve the SDK to plugins from the compiled CLI binary
Plugin installs drop their @cline/* dependencies because the host provides
them, but a compiled CLI had nothing on disk to provide them from, so a
CLI-managed Hub (desktop or an SSH remote, which gets only the binary) failed
plugins with "Cannot find module '@cline/sdk'".

Compile the plugin worker bootstrap into the CLI as an extra entrypoint and
point CLINE_PLUGIN_BOOTSTRAP_PATH at it. The worker registers @cline/sdk,
@cline/core, @cline/shared and their public subpaths through Bun's runtime
plugin API, loading each lazily from the executable's own build, and plugins
then load with Bun's native import() since jiti would not see those modules.
Plugin dependencies of their own still resolve from their node_modules.
2026-10-14 02:36:16 +00:00
abeatrix ddc0d6640e Merge main into PR #14792 and preserve plugin SDK resources 2026-10-13 18:13:10 -07:00
Saoud Rizwan 8009be6e33 fix(desktop): ship the SDK packages plugins import in production builds (#15068)
* fix(desktop): ship the SDK packages plugins import in production builds

Plugin installs strip @cline/* dependencies because the host provides them,
and the plugin worker resolves those from node_modules beside its bootstrap.
The packaged desktop app ships only the bootstrap, so a marketplace plugin
like goal failed with "Cannot find module '@cline/sdk'" (dev builds worked
because the repo's node_modules sit above src-tauri).

Bundle self-contained @cline/sdk, core, agents, llms and shared next to the
bootstrap (one shared copy via code splitting, ~17 MB), and widen the Tauri
resource glob so the nested files are actually bundled.

* fix(desktop): ship the SDK packages' subpath exports for plugins

Plugins can import supported subpaths such as @cline/shared/storage or
@cline/core/telemetry. Bundle every runtime export of each package and write
its exports map, skipping type-only exports that have no JS.
desktop-nightly-20261014011413
2026-10-13 18:07:06 -07:00
abeatrix 0df71b5917 Merge origin/main into cloud/cli-managed-desktop-hub-86ce 2026-10-13 14:10:42 -07:00
26cea2fa8b fix: stop undici's 5-minute idle timeout killing local model requests (#15024)
* fix(lmstudio): disable undici's idle-body timeout so slow local generations aren't killed mid-stream

* fix(lmstudio): compose the bodyTimeout override onto the active dispatcher instead of replacing it

* fix(vscode): disable undici idle timeouts for local model providers

Node's fetch applies undici's default 300s headersTimeout/bodyTimeout,
each measuring time since the last byte received. Local servers
(LM Studio, Ollama) legitimately go silent for longer while processing a
large prompt (headers arrive immediately, then no body bytes until the
first token), so long requests died mid-task with
'terminated: BodyTimeoutError (UND_ERR_BODY_TIMEOUT)' even though the
server was still generating.

Route LM Studio and Ollama inference through a fetch bound to an
EnvHttpProxyAgent with headersTimeout/bodyTimeout disabled. This calls
undici's own fetch rather than global fetch because VS Code's
http.fetchAdditionalSupport patch replaces caller-supplied dispatchers
with its own default-timeout Agent. Cancellation stays with each
request's AbortSignal.

Fixes #13464

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

* fix(vscode): disable undici idle timeouts for providers that may run a local model

Combine the two earlier drafts: dispatch through undici's own fetch so the
override survives VS Code's global fetch wrapper, and keep the env-var
proxy agent so JetBrains proxy support is unchanged. Widen the provider
set from LM Studio and Ollama to also cover OpenAI Compatible and LiteLLM,
which telemetry shows carry most of the UND_ERR_BODY_TIMEOUT failures.

The net test now exercises real undici fetch against a local server that
flushes headers and then stays silent, with a short-timeout control to
prove the silence is long enough to trip the default timer.

* test(vscode): keep the idle-timeout net test off any machine-level proxy

* chore: drop the changeset file, the repo no longer uses changesets

* fix(vscode): only skip idle timeouts when the base URL is on the local network

OpenAI Compatible and LiteLLM often point at remote corporate gateways.
On VS Code, fetchWithoutIdleTimeouts bypasses the host's proxy handling
(http.proxy, OS/PAC proxy, proxyStrictSSL), so routing those through it
would break proxied setups. Gate on the base URL as well: loopback,
private/link-local/CGNAT ranges, single-label hosts and .local/.internal
names get the idle-timeout-free fetch; remote gateways keep the
proxy-aware fetch and its dead-connection cap.

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

* fix(vscode): keep loopback direct in fetchWithoutIdleTimeouts, cover 0.0.0.0 and mapped IPv4

EnvHttpProxyAgent sends loopback through http_proxy whenever no_proxy is
unset, which VS Code's own resolver never does. Pick a plain Agent for
loopback hosts and the env-var proxy agent for everything else, both
with idle timeouts off. The local-network gate now also accepts 0.0.0.0
and IPv4-mapped IPv6 addresses, which Ollama and LM Studio print when
bound to all interfaces.

The net test now runs with HTTP_PROXY set and NO_PROXY unset against a
recording CONNECT proxy: loopback never touches it, an unroutable host
goes through it, and neither path has an idle timeout.

* fix(vscode): match loopback by address so IPv4-mapped IPv6 stays direct

The URL parser rewrites [::ffff:127.0.0.1] to [::ffff:7f00:1], which a
string match on "127." misses. Use a BlockList, which checks mapped
addresses against the IPv4 rules, for the loopback bypass and drop the
now-redundant unwrapping in the local-network gate.

---------

Co-authored-by: lizhe <marshal_li_b@163.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-13 21:44:25 +02:00
Saoud Rizwan ed469c753a chore(vscode): release v4.1.24 v4.1.24 2026-10-13 07:43:52 -07:00
Saoud Rizwan 1a25410ccd chore(desktop): release v0.0.46 desktop-v0.0.46 2026-10-13 07:40:40 -07:00
Saoud Rizwan c606894cb1 Revert "fix(vscode): preserve attachments in pending question responses (#14986)" (#15058)
This reverts commit f8997dac23.
2026-10-13 07:31:32 -07:00
Lucien f8997dac23 fix(vscode): preserve attachments in pending question responses (#14986)
* fix: preserve attachments in VS Code question responses

* fix(vscode): preserve pending question response ordering

* fix(core): preserve display text in Hub submitted prompt events
2026-10-13 15:09:05 +02:00
Saoud Rizwan 1a5c3f84e0 test(core): follow OpenCode Go's move to /inference/go/v1 in routing test cli-v3.0.71 sdk/sdk/v0.0.93 sdk/core/v0.0.93 sdk/agents/v0.0.93 sdk/llms/v0.0.93 sdk/shared/v0.0.93 2026-10-13 01:11:16 -07:00
Saoud Rizwan c96fb0992a chore(cli): release v3.0.71 2026-10-13 00:57:27 -07:00
Saoud Rizwan f15ed3d98b chore(sdk): release v0.0.93 2026-10-13 00:52:03 -07:00
Saoud Rizwan 482b13d38a chore(llms): refresh model catalog and follow v0 / OpenCode Go upstream changes (#15047)
* test(llms): follow models.dev catalog changes for v0 and OpenCode Go

models.dev dropped the v0 provider and moved OpenCode Go from /zen/go/v1
to /inference/go/v1 (both endpoints serve the same model list).

* chore(llms): refresh generated model catalog
2026-10-13 00:29:19 -07:00
Saoud Rizwan df56169ed5 fix(cli): let the compiled binary resolve plugin SDK imports natively (#15048)
* fix(cli): let the compiled binary resolve plugin SDK imports natively

Plugins import @cline/core from the wrapper's node_modules. Bun standalone
executables skip package.json loading at runtime by default, so every bare
import from those files failed to resolve natively and jiti fell back to
babel-transpiling the whole SDK graph (~440 files, ~8s cold). That blew the
8s plugin load timeout after an upgrade or reboot and left the plugin
failed for the life of the hub. With autoloadPackageJson the cold load
takes ~1.4s, matching the 3.0.70 subprocess sandbox.

* fix(cli): show the resumed transcript when startup posts a notice

Resuming a session hydrates the transcript after the session starts. A
plugin load failure posts a status notice during that start, and the
hydration guard treated any existing entry as user activity and skipped
the transcript, so the resumed session looked empty. Skip only once the
user has submitted, and keep startup notices after the transcript.

* Revert "fix(cli): show the resumed transcript when startup posts a notice"

This reverts commit 0fd43afc70.
2026-10-13 00:05:21 -07:00
Saoud Rizwan 2e7f442536 test(cli): stop stubbing the hub command module in main.test.ts
The module-wide vi.mock("./commands/hub") replaced createHubCommand with
a no-op, so `cline hub dashboard` (routed through createHubCommand) never
reached the dashboard mock and three dashboard tests failed. Drop the
module mock and assert the activation exclusion through
`hub dashboard --no-open`, which is already mocked.
2026-10-13 06:34:15 +00:00
BeeandSaoud Rizwan 12a4c6bfe2 feat(core): load plugins in the hub process and surface plugin errors (#14957)
* feat(core): load plugins in the hub process and surface plugin errors

Plugins now load in-process through one registry per process instead of a
node/bun subprocess. The compiled CLI and the hub it starts no longer need a
runtime on PATH, and plugins can use the Bun global under the Bun-built hub.
The subprocess sandbox stays available with CLINE_PLUGIN_MODE=sandbox.

The registry wraps import, setup, every hook, tool, and command call with
timeouts and error capture, and tracks loading/ready/degraded/failed/disabled
per plugin with the last error's phase, message, stack, and counts. A failing
plugin no longer silently drops its tools:

- session.create takes a `plugins` policy shaped like toolPolicies, and its
  reply and session.created event carry `pluginIssues`
- failures after start (setup and hooks run on the first turn) reach each
  affected session as a status notice
- the hub answers plugins.list/status/reload and broadcasts
  plugin.status_changed; `cline hub plugins [--reload]` wraps them
- the daemon marks a plugin failed instead of shutting down when an uncaught
  error's stack points into it
- the CLI prints a warning naming the plugin and error, and
  `cline config tools --json` lists failed plugins as plugin_failure entries

Setup and tool contexts gain cwd and emitEvent; __clinePluginHost keeps
working as a shim. Compiled binaries fall back to jiti/static when babel is
not on disk next to them.

* fix(core): give each session its own copy of every plugin module

Plugins written for the per-session sandbox keep state at module level. The
cline-monitor plugin, for example, builds one plugin object per import and
throws "Create a separate monitor plugin handle for each session" on a second
setup(). Sharing one imported copy across hub sessions broke it on the
second session.

The registry now hands each session its own copy: the first session claims
the copy imported at startup, and later ones re-evaluate the module while
reusing jiti's transform cache (about 180ms for cline-monitor instead of
2.8s with no cache).

A setup() failure now costs only the session it happened in: other sessions
keep their copies, the next session tries again, and a successful setup
returns the status to ready. Import failures, attributed uncaught errors, and
repeated hook/tool failures still turn the plugin off for every session.

* docs(sdk): document the plugin hooks that exist and how hook failures behave

The Hook Stages list (snake_case stages such as tool_call_before) and the
Hook Policies table (mode, retries, queueLimit, maxConcurrency) described no
code: plugin hooks are the seven runtime hooks, and there is no per-hook
policy object. Replace both with the real hooks, what each can return, and
how hookErrorMode and the 3-second hook timeout apply.

* fix(core): report failed plugins as failed in settings

Settings marked a plugin "failed" only when the plugin tool report had no
entry for it. The report now lists failed plugins with their state, so they
showed as "available" (desktop Extensions page and other core settings
hosts). Use the reported state, and fill loadError with the failing phase
and message so the reason is visible.

Also tell only the calling session about a per-session setup failure;
other sessions' copies still work. Keep the hub log line's own message
instead of letting the plugin error text overwrite it.

* fix(core): address plugin registry review findings

Reload and generations: each session copy records the generation (import)
it came from. A reload starts a new generation without invalidating older
copies, so running sessions keep working. Failures and blocking are counted
per generation, so a broken reload cannot turn off copies that still work,
and an old copy's failures cannot change the current status. A stray error
attributed by stack turns off every live generation, since a stack cannot
tell copies of one file apart.

Sandbox mode: the hub daemon no longer preloads plugins in sandbox mode,
and tool listing and MCP settings sync inspect plugins through the sandbox.
The session plugin policy now applies in sandbox mode: paths whose file
name the policy turns off never reach the sandbox, and plugins matched only
by their exported name are dropped after import.

Calls: commands, rule content, and message builders go through the same
guarded path as hooks and tools (timeout, error capture, failure count), and
calls into a copy that is turned off, failed setup, or whose session ended
are refused. A failing rule contributes no text and a failing message
builder leaves messages unchanged.

Cleanup: ctx.onDispose registers per-session cleanup. Timers a copy creates
while its own code runs, including at import, are tracked and cleared when
the session releases the copy; host callbacks given to plugins run outside
that scope so host timers are never cleared with them. Release runs on
session stop and dispose, and now also when building the session runtime
fails. The automation-events example uses ctx.onDispose.

Registrations: automation event types are validated while setup runs. If
the host still rejects a registration on commit, the copy's setup is
recorded as failed and the error is rethrown so the partial registrations
are discarded.

Also: forward importTimeoutMs, hookTimeoutMs, and contributionTimeoutMs to
the in-process registry per session; key the plugin tool inspection cache on
registry status; keep the module copy imported by preload for the first
session; report settings-disabled plugins a session would use as issues
with reason "settings".

* fix(core): keep this bindings and route import-time plugin events

- Timer tracking forwards the runtime's this (the timer handle in Node and
  Bun) to plugin callbacks.
- Plugin methods keep their receiver: rule content, message builders,
  command handlers, and setup() are called on their own objects, including
  setup() in the existing in-process loader.
- Each plugin copy owns one async scope from import onward. Claiming the
  copy for a session fills in that scope's session id and event callback,
  so events emitted by work the module started at import reach the session.
- Each generation tracks its own health, so an older copy's first failure
  after a reload is reported to its sessions even though the status shows
  the reloaded generation.

* fix(core): bind plugin changes to sessions started afterwards

Plugins are bound to a session when it starts; added, edited, or reloaded
plugins reach only sessions started afterwards. Change detection used the
entry file's mtime and size, which missed edits to files the entry imports
(common for package plugins) and same-size edits within one clock tick, so
the next new session could claim a stale preloaded copy.

Detect changes with a content fingerprint of the entry file and every file
it reaches through relative static imports, for the registry, the plugin
tool inspection cache, and the plugin command service. Add a bootstrap test
that a session started before a plugin is added or changed keeps its
original tools even when its setup runs afterwards, and a registry test for
a change outside the entry file.

* fix(core): include side-effect imports in plugin change fingerprints

The fingerprint only followed `from`, `import()`, and `require()`
specifiers, so a fix to a file the entry imports only for side effects
(`import "./setup.js"`) left the fingerprint unchanged and new sessions kept
getting the failed entry.

Scan statement-level side-effect imports for the fingerprint only. They are
kept out of the dependency preflight, where a loose match on text inside a
string (`'run import "x" first'`) would reject a valid plugin.

* fix(core): retry failed plugin imports on a backoff

Change detection follows static and side-effect relative imports, but a
broken import can also be fixed where no fingerprint sees it: a dynamically
imported file, a file behind a block comment, or a dependency installed in
node_modules. Such plugins stayed failed until an explicit reload.

Retry a plugin whose import failed when a session starts, at most every 30
seconds, even if no change was detected. A broken plugin costs at most one
extra import attempt per 30 seconds, bounded by the import timeout.

Replace the SHA-1 fingerprint with a non-cryptographic checksum; it only has
to notice edits to plugin source.

* feat(desktop): show plugin failures in the chat

Plugin failures reached the desktop webview only as core logs written to the
devtools console. Show them as status lines in the chat:

- session start returns `pluginWarnings` for plugins the session asked for
  but could not load, and the webview adds one status line per warning
- plugin notices that arrive later on the session stream (setup and hooks
  run on the first turn) carry `metadata.pluginIssue`; the webview adds
  those as status lines too, while other core logs stay out of the
  transcript

* fix(desktop): keep plugin warnings when saved history reloads

Finishing a turn replaces the live transcript with saved history, which
never contains plugin warnings (they come from the start reply and stream
notices), so the explanation of a missing plugin disappeared after the first
turn. Carry the session's live plugin-warning lines into canonical history
at their original position by time.

* refactor(core): simplify the in-process plugin registry (#14999)

* refactor(core): simplify the plugin registry

Drop the pieces added in review rounds that cost more than they buy:

- Global timer tracking. It monkeypatched setTimeout/setInterval/
  setImmediate and their clear* counterparts on globalThis for the whole
  host process (the hub, the CLI, and the shared VS Code extension host),
  and attributed timers by async scope, so a host callback invoked from
  inside a plugin call had its timers cancelled when the session released
  its plugin copy. Plugins stop what they start in ctx.onDispose, which
  stays.
- Plugin generations and per-generation health. Blocking and consecutive
  failures are counted per plugin again; a re-import (reload or source
  change) resets them. Running sessions keep their copies callable unless
  the plugin is turned off, so a broken reload fails closed for them.
- Automatic retry of failed imports on a 30 s backoff, and the side-effect
  import scan in the change fingerprint. plugins.reload covers changes the
  fingerprint cannot see.
- The duplicated registerAutomationEventType validation; the commit
  try/catch already records a host-rejected registration as a setup
  failure.

* refactor(core): do not apply the session plugin policy in sandbox mode

The sandbox is an opt-in escape hatch and the per-session policy is new;
filtering sandbox results by file-derived and exported names added two
passes nobody needs yet. The policy is documented as in-process only.

* revert(desktop): drop the live plugin-warning merge into saved history

Reverts 90df99574. The merge re-inserted unpersisted warning lines by
timestamp after every history reload and still lost them on reopen.

* fix(core): keep plugin status consistent across a reload and a late block

- A re-import now resets `blocked` and the failure count only after it
  succeeds, so calls that hit the failure threshold while the import was in
  flight cannot leave the plugin blocked under a `ready` status.
- Sessions are notified when a plugin is turned off even if its status
  already read `failed` from another session's setup failure.
- Docs: say which relative imports the change fingerprint follows, that a
  failed reload turns the plugin off for running sessions too, and that the
  sandbox ignores the session plugin policy.

* fix(core): attribute stray plugin errors by the plugin's real path too

Module resolution follows symlinks and, on Windows, expands 8.3 short names
(the GitHub runner's temp dir is C:\Users\RUNNER~1\...), so a stack frame can
spell the plugin path differently from the configured one and the uncaught
error was never attributed. Match the realpath as well, and compare with
normalized separators and case on win32. Fixes the Windows-only failure of
"attributes a stray error to the plugin whose file is in its stack", which
also fails on the base branch.

* fix(core): match both realpath spellings when attributing plugin errors

On Windows fs.realpathSync keeps 8.3 short names while realpathSync.native
expands them, and module resolvers differ in which they use. Accept either
so the stack frame matches regardless.

* fix(core): do not count a stale plugin copy's failures against the reloaded module

Each copy is stamped with the entry's import counter. After a reload (or a
source change), failures from copies of the previous import are reported to
their own session but no longer bump the error counts, block the plugin, or
change its status, so a session still running a broken copy cannot turn off
the fixed plugin for everyone else. The pre-review registry had this guard
via its generation number; the simplification dropped it.

* fix(core): don't let a stale session import turn off a reloaded plugin

A session's copy import that failed after plugins.reload (or a source change)
had already re-imported the plugin was recorded against the current module:
the catch around claimInstance had no import identity, so recordFailure
blocked the plugin and dropped the freshly loaded copy for every session.

Capture the entry's importId before claiming. If it changed by the time the
import fails, report the failure to that session only and leave the current
module's status alone, matching how stale copies' call failures are handled.

* fix(desktop): don't duplicate a queued prompt behind a plugin start warning

Session start appends plugin warnings after the optimistic user bubble. The
chat_queued_prompt_start handler searched backward for that bubble and
stopped at the first non-user row, so it hit the warning, missed the bubble,
and appended the prompt a second time until saved history replaced it.

Skip plugin warning rows in that search.

* feat(core): default a worker-thread transport for isolated plugins (#15034)

* feat(core): add a worker-thread transport for isolated plugins

CLINE_PLUGIN_MODE=worker runs each session's plugins in a worker thread
inside the host runtime. It reuses the subprocess sandbox protocol and
bootstrap, so it needs no node/bun executable on PATH and works from the
compiled CLI and hub. terminate() stops a plugin stuck in a synchronous
loop, and an uncaught error or process.exit() ends only that worker.

- WorkerSandbox mirrors SubprocessSandbox (timeouts, idle reclaim,
  per-worker pending calls) over the worker message port.
- The bootstrap talks to parentPort in a worker and posts ready once it
  listens; the host holds calls until then because Bun drops earlier
  messages.
- Workers override process.cwd() instead of chdir, which Bun would apply
  to the whole host process.
- Uncloneable values fall back to JSON, matching the subprocess IPC.
- The sandbox test suite runs against both transports, plus worker-only
  tests for loops, stray throws, process.exit, cwd, and no runtime.

* fix(core): ship a self-contained plugin bootstrap and snapshot queued worker calls

The CLI copies plugin-sandbox-bootstrap.js beside the compiled binary,
where nothing can resolve @cline/shared or jiti, so a worker (or
subprocess) exited before initializing any plugin. Bundle every package
into the bootstrap and fail the core build if it imports anything but
Node builtins.

Calls made before a worker reports ready kept the caller's args object,
so later changes leaked into the call and an uncloneable value added in
that window threw from the message listener. Queue a structured clone
instead, skip calls that already timed out, and reject a call whose
flush fails.

* test(core): give the worker loop-recovery test room for a cold worker on CI

The recovery call boots a fresh worker and re-imports the plugin within
the same tool timeout, which 200ms did not cover on CI runners.

* fix(core): keep worker plugin stdout off the host's stdout under Bun

Bun workers write console.log and process.stdout straight to the host's
stdout (Bun ignores the Worker stdout option), so a plugin's logging
corrupted --json output and JSON-RPC streams. Discard stdout-bound output
inside the worker, matching the subprocess sandbox's stdio "ignore".

* fix(core): complete discarded stdout write callbacks in plugin workers

The worker's process.stdout.write replacement dropped the completion
callback, so a plugin awaiting a write hung until the call timed out.
Call it on the next tick for both write(chunk, cb) and
write(chunk, encoding, cb).

* feat(core): run plugins in workers by default, through the plugin registry

Worker mode now goes through PluginRegistry instead of around it, so it
keeps everything in-process mode has: plugin status and plugins.list/
status/reload, plugin.status_changed, degraded/failed tracking with the
failure threshold, session notices, the per-session plugins policy,
stale-copy protection, and per-session timeouts. Only where a copy runs
changes: each plugin gets its own worker per session, and the copy the
registry wraps is a proxy to it.

One worker per plugin means a worker that dies on its own (uncaught
error, unhandled rejection, process.exit) is that plugin's failure with
no stack attribution: the plugin is turned off and the host and other
plugins keep running. The hub preloads status in throwaway workers.

- CLINE_PLUGIN_MODE defaults to worker; in_process and sandbox are
  opt-ins. Hosts that ship no bootstrap fall back to in_process.
- WorkerSandbox reports unexpected exits; sandbox tool proxies honor a
  tool's own timeoutMs.
- The bootstrap is resolved lazily and can be set with
  CLINE_PLUGIN_BOOTSTRAP_PATH. The desktop app ships it as a Tauri
  resource and points the sidecar at it; the VS Code build copies it
  next to the bundle as .mjs.

* fix(core): carry setup context, tool context, and tool definitions into plugin workers

Worker plugins lost what in-process plugins get, now that workers are the
default:

- Setup context: ctx.cwd, ctx.emitEvent, and ctx.onDispose. Cleanup runs
  in the worker before it stops (session release, shutdown, idle
  reclaim); calls arriving during an idle stop wait for a fresh worker.
- Tool context: the abort signal (the host forwards aborts by call id),
  emitUpdate (routed back to the calling tool), and emitEvent.
- Tool definitions: lifecycle, executionMode, resultPolicy, maxRetries,
  and any other plain field now reach the runtime.
- Setup gets its own timeout inside the worker. A setup that throws,
  times out, or takes the worker down fails only that session's copy,
  instead of being reported as an import failure that turns the plugin
  off for every session.

* fix(core): don't treat an exit during plugin cleanup as a crash

stopGracefully() ran the dispose method before marking the worker as
stopping, so a disposer that called process.exit() or threw from a timer
looked like an unexpected exit and turned the plugin off for every
session. Mark the worker as stopping before cleanup starts.

* fix(core): apply the session plugin policy by a worker plugin's own name

The host checked the policy against the file-derived name before the
worker had imported the plugin, so a policy keyed by the plugin's
exported name misfired: "*" off plus the plugin turned on by name
skipped it for good, and turning it off by name still ran its setup().

The worker now applies the policy after import, against both names, and
before setup. The host skips the worker up front only once it has seen
the plugin's own name for its current code.

* fix(core): keep setup prompts and cleanup for worker plugins

Worker plugins run setup() while the session bootstraps, before it is
registered, unlike in-process plugins whose setup runs on the first turn.

- Prompts a plugin sends to its session during setup (steer_message,
  queue_message, pending_prompt) found no session and were dropped. The
  host now holds them until the session is registered, then delivers them.
- A setup that registered ctx.onDispose and then threw or timed out lost
  its cleanup, since only plugins that loaded are disposed later. The
  worker now runs that cleanup right away, so a child process started
  before the failure does not outlive it.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-10-12 22:29:54 -07:00
Bee 79341eb295 fix(desktop): retain catalog counts on installed connectors (#15042) 2026-10-12 17:11:28 -07:00
abeatrix 0fd59a8b04 Merge origin/main into cloud/cli-managed-desktop-hub-86ce
Conflict resolutions:

- scripts/build-sidecar-bin.ts stays deleted. main's change to it (#14928:
  stop UPX-packing the Linux SSH binaries, which breaks Bun 1.4's reads of
  its own embedded payload) is carried over to build-desktop-runtime.ts,
  which no longer UPX-packs the Linux Cline CLI copies it bundles for SSH
  remotes.
- desktop-publish.yml drops the upx installs, as on main, and main's new
  SSH smoke test now runs the bundled remote CLI through the same
  `hub ensure` / `hub stop --discovery-path` the desktop sends over SSH.
- hub.ts keeps main's per-subcommand hub options (#14900) and adds this
  branch's --discovery-path to them, so it works before or after the
  subcommand, alongside the ensure/start flags (--json,
  --allow-port-fallback, --no-connectors, --login-shell-path).
- The desktop README keeps this branch's CLI-based SSH description with
  main's note that the binary must not be UPX-compressed.
2026-10-12 17:06:45 -07:00
Bee d2cbeea6ff fix(composio): discover connector tools through meta search (#15041) 2026-10-12 16:40:08 -07:00
John Choi cbe3a4d4f0 fix(desktop): drain copy feedback timer in chat test (#15040) 2026-10-12 16:13:10 -07:00
John Choiandabeatrix d3c82673ed feat(sdk): allow host-authenticated Composio requests (#14716)
* feat(cli): add Composio connector marketplace and installation

* feat(sdk): support host-supplied Composio requests and schemas

* docs(sdk): shorten connector host options note

* fix(sdk): normalize supplied Composio schemas consistently

* test(sdk): verify local connector auth through real resolver

* test(sdk): remove supplemental local auth verification suite

* test(desktop): mock shared integration auth token resolver

* refactor(sdk): move shared Composio helpers into connector services

* fix(connectors): use API availability instead of beta access

* feat(sdk): reuse Composio meta tools with host authentication

* feat(sdk): use host connector transport during session bootstrap

* test(sdk): initialize valid connector meta session in host regression

* feat(sdk): forward connector transport to scheduled sessions

* refactor(sdk): limit connector change to host transport support

* chore(sdk): clarify local auth scope and trim host regression setup

* fix(sdk): bound connector discovery requests

* chore(sdk): remove redundant connector option comment

* refactor(sdk): simplify connector session discovery

* refactor(sdk): read connector session responses sequentially

---------

Co-authored-by: abeatrix <beatrix@cline.bot>
2026-10-12 16:07:14 -07:00
John Choi 55d32c22ab fix(desktop): allow cancelling cloud account sign-in (#14943)
* fix(desktop): allow cancelling cloud account sign-in

* fix(desktop): keep cancel label stable during sign-in cancellation

* fix(desktop): swap account creation for cancel during sign-in

* fix(desktop): keep sign-in controls visible during account refresh

* fix(desktop): keep sign-in retry responsive during account refresh

* fix(desktop): ignore stale account reloads after sign-in retry

* fix(desktop): limit sign-in changes to cancellation and stop polling

* fix(desktop): keep sign-in cancellation in existing screen handlers

* refactor(desktop): share sign-in cancellation mechanics

* refactor(desktop): remove redundant sign-in error callback

* fix(desktop): preserve sign-in cancellation across screens

* test(desktop): simplify sign-in cancellation coverage
2026-10-12 13:48:01 -07:00
Fenil Modi ff255c08a9 feat(vscode): show dedicated ai& provider settings (#14611)
* feat(vscode): show dedicated ai& provider settings

Selecting ai& in the extension showed the generic OpenAI-compatible form
(base URL, model ID, custom headers, Azure options). Register aiand as a
known provider so it gets the standard API key + model picker form, backed
by the SDK builtin (fixed base URL https://api.aiand.com/v1, catalog models,
default zai-org/glm-5.3).

* chore: drop unused changeset file
2026-10-12 22:08:48 +02:00
Younes Beriane 9f003b3a45 fix(shared): stop billing image base64 payloads at CHARS_PER_TOKEN (#14520)
* fix(shared): stop billing image base64 payloads at CHARS_PER_TOKEN (#14512)

estimateRequestInputTokens and createTokenEstimator both derive their
token estimate from JSON.stringify(x).length, which charges an
ImageContent block's base64 data string at the 3-chars-per-token rate
used for text. A single ~90KB base64 image (a very ordinary screenshot)
was reported as ~30k tokens when real vision-model costs top out around
1.6k, so a handful of images could trip shouldCompact well before the
transcript is actually near budget.

Add createImageAwareReplacer(), a JSON.stringify replacer that blanks
the data field of any {type: "image", data: string} node wherever it's
nested, plus a counter, and use it at both call sites. Add a flat
ESTIMATED_TOKENS_PER_IMAGE (1600) back onto the char-based estimate per
image found, so images still count toward the budget without scaling
with their base64 length.

The catch-fallback path (serializeMessage) already renders images as
"[User image]: mediaType" with no base64, so this was specific to the
primary JSON.stringify path.

* fix(shared): cover AgentImagePart shape and stop fallback double count

createImageAwareReplacer only matched ImageContent's {type:'image',
data:string}. AgentMessage's image part (agent.ts) carries its payload
under 'image' instead, and agent-message-codec.ts populates it with the
same base64 string; that shape reaches estimateRequestInputTokens via
gateway.ts on every streamed request and was still billed at
CHARS_PER_TOKEN. Blank the 'image' field too when 'data' isn't present.

Separately, both call sites read imageCount() even when the primary
JSON.stringify(..., replacer) pass threw partway through and fell back
to a full re-serialization (which includes images' raw base64). Images
counted before the throw then had ESTIMATED_TOKENS_PER_IMAGE added on
top of their already-included char cost. Only capture the count when
the replacer-driven pass completes.

* test: pin the flat per-image token charge
2026-10-12 22:05:36 +02:00
John Choi a48408909b fix(desktop): keep Cloud setup cards visible (#14954)
* fix(desktop): compact signed-out cloud welcome spacing

* fix(desktop): keep cloud sign-in card near window bottom

* fix(desktop): tighten signed-out cloud onboarding spacing

* fix(desktop): use existing setup card for welcome spacing
2026-10-12 10:32:32 -07:00
Dante 03ffa9ec3e fix(sdk): require string old_text in editor schema (#14400)
* fix(sdk): require string old_text in editor schema

* fix(editor): normalize unused null old_text at runtime

* docs: clarify old_text null compatibility rationale
2026-10-12 14:50:40 +02:00
Mark Cockram 62f690d6ca fix: load and watch symlinked rule files (#14912)
* fix: load symlinked rule files from config directories

* fix(core): automatically reload symlinked instruction targets

* fix(core): drop symlink target watching, keep link-path loading

Review feedback on #14912: reading rules through the link path already
fixes the reported bug, and each new session re-reads the rules. Watching
wherever links point is a larger change than the bug needs and carried
loose ends (directory links in the target path, a readlink per file on
every refresh).

- revert the target-directory watching, chain walk and broken-link
  retention from ab59fbca4
- keep stat-based symlink acceptance in the scanners, so a broken link
  is skipped as on main
- DOC.md: edits to a symlink's target are picked up on the next refresh
  or new session
2026-10-12 13:34:17 +02:00
Saoud RizwanandSaoud Rizwan 8eba11b270 fix(schedules): stuck pending runs, silent model failures, stale editor models (#14998)
* fix(schedules): claim queued runs under bun:sqlite

claimDueRuns bound unprefixed named parameters, which bun:sqlite binds
as NULL, so the desktop hub never claimed a queued run and every
scheduled run stayed pending with no error.

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

* fix(schedules): fail runs whose agent turn ends with an error

Provider failures such as a retired model id resolve the turn with
finishReason 'error' instead of throwing, so these runs were recorded
as successful with no error message.

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

* fix(desktop): load live models in the schedule editor

The editor only used the bundled provider catalog, so it kept offering
retired models (e.g. space-bunny) that the live list no longer has.

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

* fix(schedules): skip superseded queued cron occurrences

Once queued runs are claimable again, the backlog that piled up while
claiming was broken would replay every missed slot back to back. Cancel
queued cron occurrences that a newer queued occurrence of the same
schedule supersedes so only the latest runs.

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

* fix(schedules): keep usage and tool calls in error-finish run reports

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

* test(schedules): cover claimDueRuns under bun:sqlite

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-11 16:46:46 -07:00
Bee c3a4057276 fix(llms): portable reasoning off for all providers, and thinking levels for level-only Gemini models (#14991) 2026-10-10 11:09:10 -07:00
Saoud RizwanandSaoud Rizwan bf71bf7cd9 Default to the first Cline recommended model (Sonnet 5.5 fallback) across CLI, extension, and desktop (#14959)
* Default to the first Cline recommended model (Sonnet 5.5 fallback) across CLI, extension, and desktop

- @cline/shared: CLINE_DEFAULT_MODEL_ID is now the anthropic/claude-sonnet-5.5 fallback
- @cline/llms: the cline provider's defaultModelId prefers GENERATED_CLINE_RECOMMENDED_MODELS.recommended[0]
- CLI: resolve the provider's SDK default before falling back to the first catalog entry; drop the hardcoded sonnet-4.6 fallback
- VS Code: openRouterDefaultModelId -> anthropic/claude-sonnet-5.5; cline provider falls through to the SDK default

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

* Default the SDK openrouter provider to anthropic/claude-sonnet-5.5

The VS Code extension's OpenRouter model field is populated from this SDK
default via resolveModelInfo, so the extension-side openRouterDefaultModelId
constant alone did not change the fresh-install selection.

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

* Desktop: adopt the provider catalog default model once the catalog loads

A fresh install booted on CLINE_DEFAULT_MODEL_ID and kept it when the user
skipped onboarding, so the chat bar never picked up the Cline provider's
recommended-list default. Swap the bootstrap fallback for the catalog
default unless a model was already remembered for that provider.

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

* Scope the recommended-model default to the Cline providers

Only cline/cline-pass declare a curated default; other manifests may expose
a placeholder like "default" (ollama, lmstudio), so the CLI keeps falling
back to the first catalog entry for them. The desktop bootstrap swap now also
applies only to new Cline chats, never to hydrated sessions.

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

* Test both branches of the cline default model selection

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

* Simplify: drop the desktop bootstrap swap and connector catalog lookup

The desktop app keeps CLINE_DEFAULT_MODEL_ID as its pre-onboarding default
(onboarding already stores the provider's recommended default), and the
connector only swaps its hardcoded literal for the shared constant.

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

* CLI: only the cline provider uses the SDK default before the catalog

cline-pass's manifest default is just the first bundled model, not a curated
pick, so it keeps the live catalog's own ordering.

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

* Connectors: prefer the provider's declared default over the Cline fallback

Non-Cline connector sessions with no saved model were handed a Claude id.
Use the provider manifest default when it is a real model, skipping the
"default" placeholder that catalog-less local providers expose.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-09 19:14:41 -07:00
Bee f877058449 feat(core): in-chat connector authentication via Composio meta tools (#14755)
* feat(core): in-chat connector authentication via Composio meta tools

Signed-in beta sessions create a meta-tool session through the Cline API
connectors proxy and register COMPOSIO_MANAGE_CONNECTIONS and
COMPOSIO_WAIT_FOR_CONNECTIONS, so the agent can hand the user a Connect
Link when a task needs an app that is not connected yet.

* test(core): verify connector meta-tool usage telemetry

* fix(core): tell the agent when to use connector meta tools

Composio's descriptions are generic, so models answered "I don't have
access to Gmail" instead of offering a Connect Link.

* fix(core): replace Composio meta-tool descriptions with our own

Composio's descriptions require COMPOSIO_SEARCH_TOOLS first, which this
session does not expose, so models refused to start the connection flow.
Also drop the provider's session_id input; the proxy supplies it.

* feat(core): expose Composio search and multi-execute meta tools

Register every meta tool the server allows with Composio's own
descriptions and schemas, which reference each other (search, manage,
wait, multi-execute). Drops the custom descriptions.

* fix(core): require the complete Composio meta-tool flow
2026-10-09 17:50:54 -07:00
BeeandSaoud Rizwan d20b337eb5 feat(desktop): add transcript outline with shared shadcn message scroller (#14948)
* feat(desktop): add transcript outline with shared message scroller

* fix(desktop): avoid repeated outline work during streaming

* fix(ui): preserve transcript position when replacing rows

* fix(ui): respect reduced motion in MessageScrollerButton

The shared scroll-to-end button always passed shadcn's default smooth
behavior. Resolve it from prefers-reduced-motion like the outline does,
keeping an explicit behavior prop as an override.

* test(desktop): cover saved-session row swap and streaming growth

Add a regression test for continuing a saved session: history hydrates
after the provider mounts, a new prompt pins near the top, then the
activity row is swapped for the first answer row in one commit and the
viewport must stay on the new prompt. Make the streaming test grow the
transcript and deliver the resize notification instead of only changing
answer text.

* chore(ui): tidy message-scroller check in smoke-package

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-10-09 12:22:02 -07:00
Dante 8d35dbbc5f fix(desktop): explain missing Git during skill install (#14663) (#14670) 2026-10-09 18:33:34 +02:00
Nemanja MarinkovicandNemanja Marinkovic b006326612 fix(shared): scan .claude/skills in resolveSkillsConfigSearchPaths (#14846)
Co-authored-by: Nemanja Marinkovic <devKid88@users.noreply.github.com>
2026-10-09 14:47:52 +02:00
Darsh Nandu e6ad24c43a fix(cli): apply --thinking in ACP mode (#14894) 2026-10-09 14:01:41 +02:00
dvd233 2f4e36e86f fix(vscode): preserve prose spacing when deleting slash commands (#14883) 2026-10-09 13:57:46 +02:00
Minho YooandMinho Yoo 93e5e455e6 chore: remove the dead subscribeToRelinquishControl stream (#14974)
Its only sender, sendRelinquishControlEvent, was removed as unused in
78b1f3013, so the server-side subscription set is write-only and the
webview callbacks never fire. HistoryView already re-enables Delete All
in the deleteAllTaskHistory .finally().

Claude-Session: https://claude.ai/code/session_01W9Mk18mBNfkPfgg3hYweKq

Co-authored-by: Minho Yoo <13810291+yoo-minho@users.noreply.github.com>
2026-10-09 13:41:17 +02:00
Andreas Keller bcc76e8793 fix(core): preserve resolved model limits for compaction (#14644) 2026-10-09 13:22:36 +02:00
akushonkamen 75111f347f fix(cli): accept agent_error hook events in the hook command (#14945)
The `hook` command's dispatcher had no case for `agent_error`, which the
shared hook schema accepts and the CLI runtime emits when a run fails. A
valid `agent_error` payload fell through to the default branch, so the
command exited 1 without a JSON response. It now returns `{}` like the other
agent events. Adds the first unit tests for the command.

Fixes #14808
2026-10-09 10:48:40 +02:00
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
Stanislav Gelman 95e54e4a02 fix(cli): treat a provider API key from the environment as configured (#14926)
The TUI's startup check now accepts a provider API key from the environment
(e.g. OPENROUTER_API_KEY) when the provider declares exactly one environment
variable, as the runtime does when no key is saved. The key is checked through
isProviderSettingsUsable, so the provider's other requirements still apply.
Providers with several environment variables (Bedrock, Vertex, ...) are
unchanged, and nothing is persisted.

Related to #14923
2026-10-08 16:24:47 +02:00