* 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>
23 KiB
Oversized tool result recovery
Cached text excludes native image data. Cache admission uses the persisted JSON/string model-preview size for the truncation threshold; YAML size controls only cache capacity. Plain strings stay unchanged; structured cached text is serialized as YAML, preserving multiline payloads as literal blocks without automatic wrapping. Original history and tool events keep their existing format. Recovery uses the existing read_files output and per-line limits; disabling that tool does not disable caching or remove recovery notices.
Tools created with createTool may set resultPolicy: "cache-oversized". Core enables this for MCP and Composio tools. Original output remains in history and events; synchronous model preparation sends a bounded preview with a cline://cache/<encoded-session-id>/<result-id>.result.txt URI for cached oversized responses. Use read_files with start_line/end_line to read omitted content. Shell and filesystem search tools do not support these URIs. The stateless agent runtime does not own a cache.
Entries expire after five further model iterations without a cache read, across follow-up turns. Explicit reads refresh expiry; model requests do not. A 16 MiB UTF-8 text limit per session evicts least recently read entries; individually larger results have no recovery URI. Shutdown, history reset, and restore clear the cache, and resume does not regenerate entries. Missing reads instruct the agent to refetch with an appropriate read/query tool without repeating side-effecting actions. Evicting cached text does not remove original conversation output or change earlier recovery notices; URI references remain until the cache is cleared. Cache-miss feedback appears only when an agent attempts to read missing content.
Shared agent review UI
@cline/ui exports presentation-only components for showing a session's changed
files and pull-request status. Configure Tailwind v4 and a token entry point before
importing @cline/ui/components.css, following the
complete styling setup.
Then import the components from the package root:
import {
AgentChangedFile,
AgentChangesPanel,
AgentPullRequestBar,
} from "@cline/ui";
AgentChangesPanel owns the Changes header, count, close action, empty state,
and scroll region. Compose AgentChangedFile children to show collapsible paths,
copy feedback, additions/deletions, host-provided actions, and rendered diff
content. The host retains change collection, clipboard and editor integration,
and conversation focus.
AgentPullRequestBar accepts normalized AgentPullRequestData plus loading and
error state. The host owns refresh, polling, navigation, and telemetry, and
provides its accessible checks popover through renderChecks. Native hosts can
intercept links with onNavigate; web hosts can omit it to render external
anchors. getAgentPullRequestMergeStatus and
summarizeAgentPullRequestChecks expose the same status normalization for other
host presentation.
Fork metadata
createForkSessionMetadata from @cline/core copies metadata, replaces fork ancestry, and removes inherited handoff markers. Callers supply the source ID, timestamp, source, and optional beforeRunCount; titles and session creation remain caller-owned.
Cloud sessions (experimental)
CloudSessionApi and CloudSessionController are exported from @cline/core/cloud.
The API handles REST requests; the controller handles remote Hub connections,
session lifecycle, transcript reconciliation, and approvals. Hosts provide API
URLs and a fresh-token callback; feature gating and account selection stay in the
host. Importing this subpath does not start a local agent.
For ordinary creation, CloudCreationUnconfirmedError means a dispatched request
could not be reconciled. It remains a CloudSessionError with code request_failed;
hosts should direct users to their session list instead of offering a blind retry.
Use subscribe for immutable snapshots and live events, attach/readMessages
to open a session, and send for a follow-up. Attaching a provisioning or failed
session returns its receipt without connecting. detach closes this viewer, not
the remote task. Call dispose when the host shuts down.
Viewers hydrating active runs with readMessages reconcile canonical history at
completion even when they missed the run-start event and earlier content deltas.
create accepts sandboxType: "standard" | "resumable" (default: "standard").
The controller resumes suspended sessions when opened and restores their saved tasks.
Hosts replacing controllers during credential refresh can share the
pendingInitialTasks: Map<string, CloudCreationOptions> constructor option.
It retains first-task approval/thinking/reasoning preferences, including updates
through restoreCreationOptions, until the inner task is found or created (or
the outer session is deleted). dispose preserves an injected map; without one,
the controller owns and clears its pending state. Restoring options alone never
authorizes recreation of a missing established task.
Experimental cloud handoff
loadCloudModels and CloudSessionController.listModels() provide eligible cloud
models. Handoff requires the selected model; it never substitutes another.
Use create({ handoff, ... }) to provision and seed, or seedHandoff(id, seed) for
an existing target. Persist target IDs and dispatch markers through the callbacks;
use recoverOnly after uncertain dispatch to avoid duplicate conversations.
Handoff creation requires onCreating: durably save intent before returning, and reject a previously unconfirmed intent after restart.
Callback errors preserve their original type; they do not prove an earlier POST was rejected or permit clearing its intent.
CloudHandoffSeedRejectedError means no seed was dispatched; clear only the seed marker before retrying the saved target.
verifyHandoffTranscript checks the seeded history; waitUntilReady(id) waits for provisioning.
Voice input models
getLocalTranscriptionModels(providerId, config?) from @cline/core returns the
voice models supported by a provider's transcription transport. Use it to build
voice pickers rather than filtering the bundled chat catalog. Vercel uses its live
model list and advertised streaming tags; unavailable or malformed responses fail
discovery rather than restoring stale bundled models. Voice selection saves and
both batch and streaming execution revalidate through the same service.
isTranscriptionModel from @cline/shared (also exported for browsers) accepts
exact audio-only input and text-only output modalities. An explicit transcription
label does not override additional input or output modalities. Multimodal live
models are classified as realtime, which currently has no built-in transport
support and is excluded from voice and chat pickers. Dedicated transcription can
still use either batch or streaming mode. Classification alone does not prove
that a provider implements the required transport.
createStreamingAudioTranscriptionSession mints short-lived Vercel or single-use
ElevenLabs credentials. Its shared response includes transport and sampleRate;
browser clients must capture PCM at that rate (Google live routes require 16 kHz).
ElevenLabs exposes batch scribe_v2 and live scribe_v2_realtime separately.
SSH remote environments
RemoteEnvironmentService (exported by @cline/core and @cline/sdk) owns SSH
profiles, connection testing, helper installation, authenticated loopback tunnels,
remote commands, status changes, and cleanup. It runs in the client's Node host;
browser clients expose this API through their host transport. No desktop code is
required. OpenSSH config aliases, identity files, and ssh-agent authentication are
supported. Connections use batch mode and require an already-trusted host key in
OpenSSH known_hosts (or knownHostsPath). Before first use, verify the server
fingerprint through a trusted channel and enroll it using your SSH client. Unknown
or changed keys are rejected before inspection, upload, or execution.
import { ClineCore, RemoteEnvironmentService } from "@cline/core";
const environments = new RemoteEnvironmentService({
helperBinaryDirectory: "/opt/my-client/remote-helpers",
onStatusChange: (status) => console.log(status),
});
const profile = await environments.upsert({ name: "Build host", host: "builder" });
const connection = await environments.connect(profile.id);
const core = await ClineCore.create({
clientName: "my-client",
backendMode: "remote",
remote: {
endpoint: connection.endpoint,
authToken: connection.authToken,
workspaceRoot: connection.workspaceRoot,
},
});
try {
// The ordinary session, tools, approvals, and event APIs execute on this host.
// Supply provider credentials in the session config, as for other remote hubs.
console.log(await core.list());
} finally {
await core.dispose();
await environments.dispose();
}
The service also exposes list, upsert, delete, test, disconnect, run,
getConnection, getActive, activateConnection, and getStatuses.
onConnectionLost lets clients retire runtime bindings after a tunnel fails.
Each service instance has a unique remote Hub discovery record, so another
client connecting to the same host cannot stop its Hub.
Connect/disconnect/profile mutations are serialized; concurrent connects reuse
one tunnel. Dispose the ClineCore runtime before disconnecting its environment.
Do not expose the connection's authentication token to a browser or logs.
Profiles default to ~/.cline/data/settings/remote-environments.json, written
atomically with mode 0600. They contain identity-file paths, never private keys.
Options include profilesPath, sshPath, knownHostsPath, process timeouts,
helperBinaryPath, and helperBinaryDirectory. The corresponding helper/SSH
configuration variables are CLINE_REMOTE_HELPER_BINARY,
CLINE_REMOTE_HELPER_DIRECTORY, CLINE_SSH_PATH, and
CLINE_SSH_KNOWN_HOSTS_FILE.
Remote Hubs are CLI-managed. Clients package the self-contained Cline CLI
binary (apps/cli, compiled with Bun) for each remote OS and architecture; the
service stages it on the host and runs cline hub ensure --json --discovery-path <path> ... to start or reuse a loopback Hub, and cline hub stop --discovery-path <path> to stop it. Use remoteHelperBinaryFilename({ platform, arch }) for the bundled filename (cline-<target-triple>). Linux and macOS on
x64/arm64 are supported. The CLI must be built from the same SDK build as the
client; missing binaries produce an explicit error, without installing a
runtime from the network. Agent tools and persistence run remotely; the host
only manages SSH and forwards the authenticated hub connection.
Clients whose own process cannot host the Hub daemon can call
setHubDaemonLauncher({ command: "/path/to/cline" }) so detached Hubs they
start are launched through the Cline CLI.
Provider authentication metadata for host UIs
@cline/shared (including its browser entry point) exports ProviderAuthInfo,
ProviderLocalCli, resolveProviderLocalCli(provider), and
resolveProviderApiKeyOptional(provider). The resolvers accept provider data
(metadata.localCliCommand with optional docsUrl, and metadata.apiKeyOptional);
they perform no registry lookup. Hosts resolve providers through @cline/llms and
then pass that data to the shared helpers. listLocalProviders includes the
resulting facts in each ProviderListItem.auth (localCli, and apiKeyOptional
for local inference servers and cloud-credential providers that run without a
key), allowing browser clients to render authentication guidance and gate sessions
without importing the LLM catalog. ProviderListItem.modelTools likewise
carries provider-level native tool availability for settings indicators.
Concurrent subagent tool calls
spawn_agent and configured subagent_* tools declare
executionMode: "parallel". Consecutive calls to these tools in one model
response run concurrently even when the parent runtime uses its default
sequential mode. Each call still returns its child's completed answer, and tool
results remain in the model's original call order.
A tool's optional executionMode overrides the runtime's toolExecution setting.
Unmarked tools inherit the runtime setting. Sequential calls form ordering
boundaries: read_file → [spawn A, spawn B] → edit_file executes the read first,
then both child runs together, then the edit after both finish. This does not
change the execution mode of tools inside the child agents.
Preparation remains serial for the whole response, before any tool executes.
All before-tool hooks and required approvals therefore complete before the
parallel group starts. A before-tool skip blocks its own call; a before-tool
stop prevents the entire response's execution, as before. Pending approvals
can delay sibling execution. No background-run handles or new concurrency
limit are introduced.
Configured subagent approvals
Configured agents do not expose a tool approval policy setting. The parent’s
subagent_<name> call follows the parent session’s approval policy; the child
executes its available tools without inheriting that policy or approval callback.
Its configured tools allowlist and disabled-tool filtering still apply. Runtime
hooks remain inherited and can block tool execution.
Saving provider credentials
saveLocalProviderSettings is asynchronous; callers must await it before
reloading provider catalogs or continuing onboarding. When a saved
provider has a modelsSourceUrl, credential, header, and base URL updates refresh
its model list before saving the new settings. updateLocalProvider follows the
same rule even when the request omits models and modelsSourceUrl. Endpoint
changes relocate same-origin model sources; separate catalog origins remain
unchanged. Model refresh in saveLocalProviderSettings is best-effort: if it
fails during discovery, the new settings are still saved and the last known
catalog is retained. Settings and catalog persistence errors still reject the save.
Provider-service mutations are serialized per catalog file within the process,
including across manager instances and different providers. Discovery prepares
the update before the complete settings patch is persisted once. If the catalog
write fails, the prior provider settings are restored only while the failed
operation still owns the current settings entry; newer saves and removals are
preserved. A rollback failure is reported alongside the original error.
Explicit updateLocalProvider calls still reject failed model fetches and retain
the prior settings and catalog.
Catalog refreshes replace discovery-owned model IDs while retaining manually
added models, their default selection, and overrides on retained model entries.
models.json records discovery-only IDs in discoveredModelIds; IDs also supplied
explicitly are user-managed. An explicit models update replaces the manual list.
An existing model selection in provider settings does not count as a manual
addition when initializing a source-backed catalog.
Provider capabilities are inherited when registering models, so stored per-model
capability overrides continue to take precedence after refreshes.
Shared UI session rows
@cline/ui exports AgentSessionRow, AgentSessionRowEditor, and
AgentSessionOverview, together with their public props types. These are
presentation primitives for session navigation, rename, and metadata content;
the host retains session data, routing, menus, permissions, formatting, and
interaction policy. Import the shared component stylesheet with the host theme.
AgentSessionRow owns the row geometry, selected/hover appearance, timestamp
placement, and pending/provisioning/running/unread status-dot precedence. Hosts
provide the already-formatted label and timestamp, optional leading and
pinnedIndicator content, and a sibling action. Root DOM props and refs pass
through to the row wrapper for host-owned context-menu or hover-card triggers.
The default control is the desktop native button; its disabled and
onSelect props apply only in that mode. URL or router navigation uses the
mutually exclusive renderControl mode, which receives the shared navigation
className and row children for the host's link or router control. The host
control owns its href, accessibility, disabled behavior, and event handling.
The control union prevents combining renderControl with disabled or
onSelect, and keeps optional actions as siblings so interactive elements are
not nested.
AgentSessionRowEditor supplies the matching edit frame while the host owns
the rename input, focus, Enter/Escape/blur handling, and saving state.
AgentSessionOverview renders a title and [label, value, fullValue?]
metadata rows; the host owns the hover-card lifecycle, positioning, and
metadata formatting. See the session-row adoption guide
for the import, slot, and trigger/ref examples.
Shared context usage presentation (@cline/ui)
AgentContextUsage exposes desktop's context ring and token breakdown through
its children render callback. It adds no wrapper: the host receives
AgentContextUsagePresentation (triggerLabel, ring, and details) and keeps
its own accessible trigger, popover, positioning, focus, and keyboard behavior.
AgentContextUsageProps accepts usage: AgentContextUsageData, optional
costLabel: ReactNode, and the required render callback. Usage contains
tokensIn, tokensOut, cacheReadTokens, and optional contextWindow.
Supply current-request metrics and the model's authoritative context capacity,
not accumulated session token traffic. The component renders nothing when
usage is empty or context capacity is unavailable or nonpositive. Cached tokens
are part of input usage, not additional context consumption.
costLabel is a separate host-formatted cost. Numeric zero is displayed;
previously hidden falsy values remain omitted. Desktop retains its existing
cost formatter and usage source. Import the component and all three public
types from @cline/ui; see UI adoption guidance for
theme setup, styling, and composition examples.
Shared command output and image presentation (@cline/ui)
AgentCommandOutput renders output with a running cursor controlled by
isRunning. Optional children let the host retain ANSI rendering or normalize
control characters. It follows new output initially, pauses when the user
scrolls away, and resumes within 24px of the bottom. Hosts own output collection,
limits, and session identity; remount it when switching commands. tabIndex and
classNames.viewport / classNames.cursor allow host-specific accessibility styling.
AgentImageLightboxContent renders an image and two close controls calling
onClose. The host owns the dialog, positioning, Escape handling, focus management,
and image navigation. backdropTabIndex can exclude the backdrop from a managed
dialog's tab order. Image source validation and resolution remain host-owned;
provider-generated URLs must go through an explicit host trust policy before
rendering. This presentation primitive does not replace GeneratedMediaContent
or its inline-byte validation.
Shell executor errors
The executor returned by createShellExecutor rejects on execution failure.
The following error classes are exported from @cline/core so hosts can
distinguish process outcomes without parsing messages. They are not an exhaustive
list of rejections: cancellation, timeout, and stdin-write failures can reject
with other errors.
CommandExitError— the shell started and exited non-zero.exitCodeis the numeric process exit code andoutputcontains a completion notice and the captured output (subject to the configured truncation limit).CommandTerminationError— the process terminated without a numeric exit code.signalis the terminating signal, ornullif none was reported;outputcontains a termination notice and the captured output. This error has noexitCodefield.CommandSpawnError— the shell process could not be started, so there is no exit code. The message keeps the pre-existingFailed to execute command: …form.codeis the operating system error libuv reported (ENOENT,EACCES,EFTYPEfor a file that is not a valid executable). Because spawn reportsENOENTwith the same message when the executable is not found and when the working directory no longer exists,missingrecords which path was absent at failure time:"executable"or"cwd". It isundefinedfor every other code.codeitself isundefinedif the underlying error has no code.
The public run_commands tool catches executor failures and resolves with
per-command results containing success: false, rather than propagating these
errors as rejected tool calls. For CommandExitError and
CommandTerminationError, the failed result retains output in its result
field. For CommandSpawnError, result is empty and error includes the spawn
failure message. Hosts that need the structured error fields must observe the
executor rejection before this conversion.
Hosts that record command telemetry should label a CommandSpawnError by its
code (and missing) rather than inventing an exit code. The VS Code and
standalone adapters do this in the errorCode dimension, and use the bounded
labels signal and no_exit_code for CommandTerminationError. Only an actual
numeric exit is reported as exitCode.
Symlinked instruction files
Rules and workflows can be symbolic links to regular files. Source paths and fallback names use the link's name and location, not the target's. Broken, circular, and non-file targets are skipped.
UnifiedConfigFileWatcher watches only the directory containing the link, not
the link's target. Edits to a symlink's target are picked up on the next
refresh or new session.
Unknown model completion recovery
The AI SDK adapter maps unified length to max-tokens and missing or unrecognized reasons (including other) to unknown. Explicit stop, tool-calls, content-filter, and error retain their meanings. The agent also treats a stream without a finish event as unknown.
Without tool activity, an unknown response is preserved in history and continued once, with the model-visible user message “Previous turn ended unexpectedly. Continue from where you left off.” The message uses displayRole: "system" and userRunSpan: 0, matching injected hook context so it stays out of live and replayed chat transcripts. The system prompt is unchanged. A second unknown completion fails the run. Queued user instructions are consumed before the continuation, including in the first iteration. Tool calls receive their results through the normal loop; provider-executed actions are not replayed.
Host connector transport
Pass connectorsRequest: (path, init) => Promise<Response> to LocalRuntimeHost
and createLocalHubScheduleRuntimeHandlers to supply connector authentication.
Paths start with /api/v1/connectors; the host supplies the API origin and
authentication. Omit it to retain local account authentication and rollout checks.