Files
cline/sdk/DOC.md
T
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

23 KiB
Raw Blame History

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. exitCode is the numeric process exit code and output contains a completion notice and the captured output (subject to the configured truncation limit).
  • CommandTerminationError — the process terminated without a numeric exit code. signal is the terminating signal, or null if none was reported; output contains a termination notice and the captured output. This error has no exitCode field.
  • CommandSpawnError — the shell process could not be started, so there is no exit code. The message keeps the pre-existing Failed to execute command: … form. code is the operating system error libuv reported (ENOENT, EACCES, EFTYPE for a file that is not a valid executable). Because spawn reports ENOENT with the same message when the executable is not found and when the working directory no longer exists, missing records which path was absent at failure time: "executable" or "cwd". It is undefined for every other code. code itself is undefined if 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.