Compare commits

...

2230 Commits

Author SHA1 Message Date
Dominic Cooney 62f5759eaa fix(cli): declare computer user helper reasoning controls
The computer-user helper built its Anthropic provider config from the
bundled model catalog, which ships no reasoningOptions. The routing
treated claude-sonnet-5 as an unlisted manual-thinking model and sent
thinking.type.enabled, which current Claude models reject: the API
requires thinking.type.adaptive with an effort level.

Declare the helper model's effort controls in the provider config so
the helper always uses adaptive thinking, matching the driver path,
which resolves reasoning controls from the live models.dev catalog.
2026-08-25 02:09:34 -04:00
Dominic Cooney 9e9637b666 Fix sonnet thinking options for computer user. 2026-08-25 02:09:34 -04:00
Mikołaj Kondratek 2cc9722945 fix(cli): warn when a prompt argument disables computer use
Computer-use is wired only into the interactive runtime, so passing a
prompt argument routes to the agent path and silently produces a session
with no `computer` tool. The model then reports having no such tool,
which reads like a backend or configuration fault rather than a
consequence of how cline was invoked.

Warn when CLINE_COMPUTER_USE_PORT is set but the run won't be
interactive. Advisory only -- the run proceeds unchanged.
2026-08-25 02:09:34 -04:00
Cline Agent d455da648f fix(core): preserve current computer screenshot 2026-08-25 02:09:34 -04:00
Cline Agent 14ddef2c43 Make computer user agent abort report instead of terminating the CLI. 2026-08-25 02:09:34 -04:00
Dominic Cooney 8283e0461e Stream driver and user events to the observatory via qbt. 2026-08-25 02:09:34 -04:00
Dominic Cooney 248684a360 Give status updates a since and timeout. 2026-08-25 02:09:34 -04:00
Dominic Cooney af70d33eed Anthropic thinking fixes. 2026-08-25 02:09:34 -04:00
Dominic Cooney 37c3a06ddc Remove display-size overrides; the backend is the sole source of truth.
The CLINE_COMPUTER_USE_DISPLAY_WIDTH/HEIGHT env vars and the
displayWidthPx/HeightPx tool options let configuration disagree with the
real framebuffer, which would corrupt every coordinate the model
computes. Delete the override path: createComputerUseTool() always
queries get_display_info, and construction fails loudly when the backend
is unreachable instead of proceeding with a guessed size.

Tests that avoided a live backend via overrides now use a stub TCP
backend that answers get_display_info, matching the real qbt contract.
The CLI computer-user integration additionally checks Anthropic
credentials before dialing the backend, so a missing key no longer
costs a socket.

Dimensions remain a construction-time snapshot; a mid-session resize
still goes stale. The fix (backend reports dimensions per screenshot,
description stops embedding them) is a wire-protocol change recorded
in the README's 'Not yet done'.

Test plan:
  cd sdk/packages/core
  bunx vitest run src/extensions/computer-use --config vitest.config.ts
  cd apps/cli
  bunx vitest run src/runtime/interactive/computer-user.test.ts
2026-08-25 02:09:34 -04:00
Dominic Cooney 2a4277265b Add CLINE_COMPUTER_USER_MODEL for independent helper model choice.
The driver and the computer user use separate inference: the driver
keeps whatever provider/model the CLI is configured with, while the
helper is always on the direct anthropic provider (the only wire
target that sends the computer-use beta header). The helper model now
resolves through one function: CLINE_COMPUTER_USER_MODEL, then the
Anthropic provider entry's saved model, then claude-sonnet-4-6.

Test plan:
  cd apps/cli
  bunx vitest run src/runtime/interactive/computer-user.test.ts
2026-08-25 02:09:34 -04:00
Dominic Cooney 666825cbce Add the asynchronous computer user.
The raw computer tool put every screenshot and input action in the
driver's context. Now GUI work is delegated to a computer user: a
persistent, interactive helper session on the Anthropic provider that
owns the computer tool plus the normal built-ins, works in the
background, and reports to the driver through steer-injected messages.

Core (sdk/packages/core):
- computer-use: fix the wire contract (key combos travel in `text`,
  matching qwanban's serde types; there is no `keys` field), add
  AbortSignal cancellation and an action-lifecycle observer with one
  guaranteed terminal event per action.
- computer-observability: versioned artifact event contract
  (clientSequence total order, eventId idempotence, action/parent
  correlations, blob refs) plus a recorder that bridges the client
  observer; typed text never enters the stream.
- computer-user: ComputerUserCoordinator state machine (serialized
  transitions, stale settlements ignored by run identity), helper
  collaboration tools (post_driver_update, ask_driver,
  finish_computer_task), versioned helper prompt, and the four
  driver-facing computer_user_* tools.
- CoreSessionConfig.completionPolicy: explicit session policy now wins
  over the builder's submit_and_exit inference, so extraTools-based
  terminal tools can be made mandatory.

CLI (apps/cli):
- createInteractiveComputerUser wires the coordinator to a dedicated
  local-backend ClineCore helper using the Anthropic provider's own
  stored credentials; falls back to the raw computer tool when
  Anthropic is not configured. Driver notifications resolve the live
  session id at call time via sendCurrentTurn.
- Mode switches now rebuild extraTools through one shared derivation
  (buildInteractiveExtraTools) with persistentExtraTools, so the
  computer-user tools survive plan/act switches.

Test plan:
  cd sdk/packages/core
  bunx vitest run src/extensions/computer-use \
    src/extensions/computer-user src/extensions/computer-observability \
    --config vitest.config.ts
  bun tsc -p tsconfig.dev.json --noEmit
  cd apps/cli
  bunx vitest run src/runtime/interactive/computer-user.test.ts \
    src/runtime/interactive/mode.test.ts
  bun tsc --noEmit
2026-08-25 02:09:34 -04:00
Cline Agent d23cb3d02f fix(cli): reject deferred cd commands 2026-08-25 02:09:16 -04:00
Cline Agent e1b0907ec0 fix(cli): refresh workspace resources after cd 2026-08-25 02:09:16 -04:00
Cline Agent e90e9a9e69 feat(cli): add interactive cd command 2026-08-25 02:08:51 -04:00
Dominic Cooney 6a8dfc55bd Make 'zoom' region two points, not a point and a size. 2026-08-25 02:08:14 -04:00
Dominic Cooney 92f62cb6ab Computer use. 2026-08-25 02:08:14 -04:00
Saoud Rizwan 4f5f238407 Desktop app: organize sidebar sessions into Pinned, Scheduled, and Tasks sections (#13528)
* Add Pinned/Scheduled/Tasks categories to desktop app sidebar

Replace the Schedules and Favorites filter-menu options with visible
collapsible category sections in the session sidebar, and rename the
Favorite action to Pin across the sidebar and sessions view.

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

* Grow full history window when Tasks show-more outpaces loaded tasks

loadMoreSessions treats its argument as a limit on all sessions, but the
Tasks show-more count only tracks Task rows, so once pinned/scheduled
rows pushed the loaded total past the requested count the call no-oped
and clicks went dead. Grow the whole history window via
loadOlderSessions instead, and only when the loaded tasks cannot fill
the next page. Addresses Greptile review on #13528.

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

* Auto-fill the Tasks page instead of fetching once per show-more click

A single 50-session window growth can consist entirely of pinned or
scheduled sessions, leaving a show-more click with no visible Tasks
progress. Replace the one-shot fetch with a page-fill effect that keeps
growing the history window until the requested Tasks page fills or
history runs out. Addresses the follow-up Greptile review on #13528.

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

* Halt page-fill retries after a failed history fetch

A failed fetch leaves the task count and has-more state unchanged,
which are exactly the conditions the page-fill effect fires on, so one
failing request would retry and re-toast forever. Halt the effect after
a failure and let the next explicit show-more click retry. Addresses
the third Greptile review on #13528.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-24 22:51:00 -07:00
Saoud Rizwan 8f69880ac4 Hide Channels and Agents sections from desktop app sidebar (#13527)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-24 21:54:59 -07:00
Saoud Rizwan a0c341e93c desktop: sidebar navigation cleanup with New/Schedule/Customize rows and dialog-based search (#13533)
* desktop: clean up sidebar navigation chrome

- Give New Task its own full-width labeled row below the logo row
  instead of an ambiguous icon next to the agenda toggle
- Wire the New Task row to the home action so starting a new task
  clearly takes you home (the logo still works as a fallback)
- Swap back/forward chevrons for browser-style arrow icons and
  bump their size

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

* desktop: sidebar New/Schedule/Customize rows and always-visible search

- Stack New (plus icon), Schedule, and Customize as full-width labeled
  rows below the logo; whole row highlights on hover via sidebarItem
- New starts a fresh task (home), Schedule opens Settings > Schedules,
  Customize opens the Customizations sections (Plugins first)
- Show the session search bar permanently above the sessions list
  instead of hiding it behind a search icon toggle

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

* desktop: move session search into a dialog behind a logo-row icon

- Replace the inline sidebar search bar with a search icon in the
  logo row that opens a cmdk command dialog listing sessions
- Selecting a result opens that session and closes the dialog
- Remove the agenda/tasks toggle the icon replaces, along with the
  now-unreachable sidebar Agenda panel (the welcome screen still
  surfaces agenda tasks)

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

* desktop: load full session history when the search dialog opens

Addresses Greptile review on #13533: the dialog only searched the
currently loaded history batch, so older unloaded sessions could not
be found. Opening search now kicks off loadAllSessions() (the hook's
purpose-built global-search loader), and the empty state reads
'Searching older sessions...' while more history is streaming in.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-24 21:53:47 -07:00
Saoud Rizwan f91af30401 Add Desktop App and Cloud Platform to bug report issue template (#13532)
* Add Desktop App and Cloud Platform to bug report surfaces

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

* Rename Surface Diagnostics field to Diagnostics

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-24 19:49:47 -07:00
Saoud Rizwan 83b2588c9c Disable the agent todo tool and hide the Agenda UI in the desktop app (#13530)
* remove todo tool and Agenda UI, keep schedule-only tasks tool

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

* chore: biome formatting fixes

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

* restore agenda backend; disable todo kind behind a flag instead of deleting

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

* keep agenda automation pump idle while the todo tool is disabled

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

* remove todo tool and Agenda UI altogether (revert the disable-flag hybrid)

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

* restore all agenda code to main state

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

* disable agent todo tool and hide Agenda UI behind flags

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-24 19:35:51 -07:00
Saoud Rizwan 6e09e81a79 Add suggested schedule templates to the desktop Schedules page (#13529)
* Add suggested schedule templates to desktop Schedules page

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

* Fix unreadable selected text in inputs caused by selection utility conflict

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

* Restyle Suggested section label as small gray uppercase

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

* Hide suggested schedule cards that match an existing schedule name

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-24 18:14:01 -07:00
Saoud Rizwan dc43a57fd7 fix(tools): create new files with the platform-native line ending (#13521)
* fix(tools): use platform-native EOL for new files and preserve CRLF in apply_patch updates

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

* simplify to the minimal new-file EOL fix

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

* extract shared normalizeNewFileLineEndings helper

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-24 17:54:54 -07:00
Saoud Rizwan 833cc891b5 chore(vscode): hide per-tool MCP auto-approve checkboxes behind a flag (#13522)
* chore(vscode): remove per-tool MCP auto-approve checkboxes from webview

MCP auto-approval is now governed solely by the global 'Use MCP servers'
toggle; the SDK approval path (shared with the CLI and desktop app) has no
per-tool granularity, so the per-tool and 'Auto-approve all tools'
checkboxes were no-ops that implied control that no longer exists. Remove
them from the MCP settings view and chat tool rows. The autoApprove arrays
in cline_mcp_settings.json and the toggleToolAutoApprove RPC are left
intact for the legacy extension.

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

* chore(vscode): hide per-tool MCP auto-approve checkboxes behind a flag

Keep the checkbox components, handlers, and RPC plumbing intact but gate
rendering behind SHOW_MCP_PER_TOOL_AUTO_APPROVE=false: the SDK approval
path (shared with the CLI and desktop app) is all-or-nothing via the
global 'Use MCP servers' toggle, so the per-tool checkboxes were no-ops.
Flip the flag back on if the SDK gains per-tool approval granularity.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-24 17:47:15 -07:00
Saoud Rizwan a2fb1d15ca fix(core): prevent search_codebase from crashing the process on giant single-line files (#13525)
* fix(core): prevent search_codebase from crashing the process on giant single-line files

searchWithRipgrep buffered all of rg's --json stdout into one string. Each
JSON event embeds the full text of the matched line (--max-columns is
ignored in JSON mode), so searching a directory of serialized trace dumps
(single-line multi-hundred-MB JSON files) accumulated gigabytes of stdout
until string concatenation threw RangeError: Out of memory inside the
stream data handler. That throw is outside the tool's try/catch, so it
escalated to an uncaughtException and killed the CLI/hub daemon.

Parse rg's JSON events incrementally line by line, drop events larger
than 256KB, truncate matched/context lines to MAX_LINE_CHARS, and stop
reading once maxResults is reached. The fallback regex scan now skips
files larger than 10MB (reporting the skip count) and truncates its
context lines the same way.

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

* simplify search_codebase crash fix to a minimal diff

Replace the incremental JSON-event parser with three small guards: stop
buffering rg stdout past 10MB, drop the trailing partial event before
parsing, and slice fallback context lines to MAX_LINE_CHARS. Drops the
fallback file-size skip and skip-count reporting.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-24 17:45:59 -07:00
Saoud Rizwan 8e7a55498b chore(cli): release v3.0.58 2026-08-24 15:44:16 -07:00
Saoud Rizwan 0cfc901589 fix(hub): flush the /shutdown 202 before daemon teardown
The /shutdown handler queued teardown on a microtask, which runs before
the event loop's write phase, so the daemon could process.exit() before
the accepted 202 was handed to the socket. Unix masked it (uv_try_write
lands small loopback writes synchronously); Windows has no such fast
path and lost the race regularly — the recurring shutdown.e2e.test.ts
'socket hang up' failures on windows-latest. Start teardown from the
response's write callback instead, with an idempotent 1s fallback so a
client that vanishes mid-write cannot strand the daemon, and send
Connection: close so the client gets a FIN rather than an abort.

Since the flakiness this compensated for is fixed at the source, restore
maxWorkers: 2 for the Windows core suite (serializing it cost ~3 min of
CI per run), and raise the e2e daemon discovery hang guard 10s→30s —
it guards against hangs, not runner speed.
2026-08-24 15:43:44 -07:00
Saoud Rizwan a5c3181b78 fix(vscode): don't steal last-used provider from ClinePass on credential refresh (#13520)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-24 15:18:04 -07:00
Saoud Rizwan 8cb60caf0c chore(sdk): release v0.0.79 2026-08-24 14:55:30 -07:00
Mikołaj Kondratek c03e452315 fix(sdk): carry root overrides into the Node smoke-test sandbox (#13517)
ci-node-smoke.ts installs the packed SDK tarballs with a plain npm
install in a fresh temp dir, where the repo root package.json overrides
do not apply. When @sap-cloud-sdk 4.9.0 shipped (2026-08-24) it broke
@sap-ai-sdk/ai-api 2.14.0 (via @jerome-benoit/sap-ai-provider in
@cline/llms) with ERR_PACKAGE_PATH_NOT_EXPORTED, failing the smoke step
on every PR even though the root already pins @sap-cloud-sdk/* to 4.6.0.

Copy the root overrides block into the generated sandbox package.json
so the smoke install resolves the same pinned versions as the repo and
future third-party releases cannot break it independently.
2026-08-24 23:49:52 +02:00
Saoud Rizwan 83ff8fd2f5 fix(hub): cap hub-events db size so it can't fill the disk (#13516)
* fix(hub): cap hub-events db size so it can't fill the disk

Row/time retention alone didn't bound disk usage: envelopes carrying
full session snapshots reach hundreds of KB each, so retained rows
could total tens of GB, sweeps only ran hourly, and DELETE never
shrinks a SQLite file. Enforce a 64 MiB size budget in prune() (oldest
rows first, VACUUM to return the space), and also prune after every
16 MiB appended so bursts can't outrun the hourly timer.

Fixes #13505

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

* fix(hub): tolerate VACUUM failure on a full disk

VACUUM needs scratch space and can fail in exactly the state a
ballooned event log causes. The byte-budget deletes already bound live
data, so swallow the error and let the next sweep retry the reclaim
instead of aborting startup pruning and disabling the durable log.

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

* fix(hub): count the size budget in UTF-8 bytes, not characters

envelopeJson.length (UTF-16 units) and SQLite LENGTH() (characters)
undercount multibyte text by up to 3x, which could leave a CJK-heavy
log settled above budget and re-running VACUUM every sweep. Use
Buffer.byteLength and LENGTH(CAST(... AS BLOB)) instead.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-24 14:47:26 -07:00
Mikołaj Kondratek 397a6a3341 fix(vscode): resolve hook workspace identity from the window, not shared global state (#13352)
* fix(vscode): resolve hook workspace identity from the window, not shared global state

Hook discovery, hook cwd selection, and the workspaceRoots metadata passed
to hook scripts all read the workspaceRoots/primaryRootIndex global state
keys. Global state lives in ~/.cline and is shared by every Cline instance
(all VS Code windows, the CLI, the JetBrains plugin), and nothing writes
these keys anymore, so hooks resolved against whatever project some other
or older instance last recorded. With a second window open on another
project, a workspace's .clinerules/hooks scripts were never discovered.

Resolve workspace roots via a single guarded helper backed by
HostProvider.workspace.getWorkspacePaths() (in-process, window-scoped,
same as refreshHooks): blank paths are filtered, a host-bridge failure
degrades to no workspace roots instead of silently disabling global hooks
or skipping blocking PreToolUse guards, and one resolution is threaded
through hooks-dir discovery, cache misses, cwd selection, and hook input
metadata so they can't disagree (previously up to four host lookups per
hook execution — real gRPC round trips in the standalone host). Roots and
hooks dirs are matched on whole path segments with the longest root
winning, so prefix-sharing or nested workspace roots resolve to the right
project. The adapter creates the runner once per event and skips no-op
runners, making creation the single resolution point; the separate
hasHook/getHookInfo checks are removed. The dead workspaceRoots and
primaryRootIndex state keys are dropped, and the four hand-rolled
HostProvider.workspace test stubs are consolidated into one shared
helper.

* test(vscode): add e2e coverage for workspace-scoped hook discovery

Boots real VS Code with the packaged extension against the workspace
fixture, sends a prompt, and asserts the fixture's UserPromptSubmit hook
was discovered from the open window's workspace, executed with that
workspace root as its cwd, and received the same root in its
workspaceRoots input — the end-to-end contract the hook workspace
identity fix establishes.

* test(vscode): isolate the e2e hook fixture from the shared workspace

The UserPromptSubmit fixture hook lived in the shared e2e workspace, so
every prompt-sending spec executed it (hooksEnabled defaults to true) —
and its cold PowerShell spawn on Windows pushed chat.test.ts past the
5s expect timeout. hooks.test.ts now overrides workspaceDir to a
dedicated workspace-hooks fixture, so only the hooks spec pays the hook
spawn.
2026-08-24 23:28:11 +02:00
Saoud Rizwan c9b75155ea fix(cli): remove the $4.99 ClinePass promo copy (#13514)
The $4.99 first-month promo is ending, so the CLI's first-launch "Try ClinePass" dialog should no longer advertise it. Also drops the leftover CLI_PROMO_CODE plumbing, which has been an empty string since the promo-code flow was removed.
2026-08-24 13:09:42 -07:00
Saoud Rizwan 09ee902639 chore(vscode): release v4.1.15 2026-08-23 12:33:23 -07:00
Saoud Rizwan 2b7b01328a fix(vscode): auto-approve all MCP tool calls when the MCP toggle is on (#13498)
* fix(vscode): honor MCP auto-approve settings for SDK tool calls

The SDK extension required both the global 'Use MCP servers' auto-approve
toggle AND each tool's per-tool autoApprove flag before silently approving
an MCP call, while the legacy extension treated them as either/or. Restore
the legacy OR semantics so toggling MCP auto-approve works again.

Also key toolPolicies by the registered SDK tool name (via
defaultMcpToolNameTransform, now exported from @cline/core) instead of raw
server__tool. Servers whose names contain sanitized characters (e.g.
marketplace names like github.com/user/repo) or exceed 64 chars produced
policy keys that never matched the registered tool, so those MCP tools ran
without any approval gate; the live auto-approve lookup now re-applies the
transform instead of string-splitting the name.

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

* Revert "fix(vscode): honor MCP auto-approve settings for SDK tool calls"

This reverts commit 86c568fbba.

* fix(vscode): auto-approve all MCP tool calls when the MCP toggle is on

The SDK extension only auto-approved an MCP call when the global 'Use MCP
servers' auto-approve toggle AND that tool's per-tool autoApprove flag were
both set, so toggling MCP auto-approve appeared to do nothing and users had
to opt in each tool individually. The toggle alone now governs all MCP
tools; the per-tool flag is no longer consulted.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-23 12:12:32 -07:00
Saoud Rizwan be8b984d10 chore(vscode): release v4.1.14 2026-08-23 02:41:04 -07:00
Saoud Rizwan c870116d1d fix(telemetry): emit task.completed from every session teardown path (#13489)
The task.completed fallback lived only inside shutdownSession, but
stopSession/dispose route interactive sessions with a terminal reported
status through releaseSessionRuntime, which never emitted. Truthful
session-status reporting (shipped in 4.1.11) re-routed a large share of
interactive stops onto that branch and silently dropped the event.

Route the emission through a single choke point,
emitTaskCompletedOnTeardown, called from both shutdownSession and
releaseSessionRuntime. The completion criterion no longer reads
session.status: interactive sessions use the recorded final-turn
outcome (lastInteractiveTurnFinishReason), non-interactive sessions
keep the existing input.status === "completed" logic. A new
taskCompletedEmitted flag (also set by the submit_and_exit observer)
enforces exactly one task.completed per session. failSession now
records the errored final turn so a stale "completed" from an earlier
turn can never leak into the teardown emission. Telemetry only; no
user-facing behavior changes.
2026-08-23 02:34:26 -07:00
Saoud Rizwan 4f836ae7d0 test(sdk): give windows-sensitive suites realistic timeouts
Four consecutive SDK publish runs failed on windows-latest, each on a
different test, all of them plain timeouts: two @cline/shared SQLite
tests at the 5s vitest default, core's bash executor at 10s, and the hub
singleton endpoint test at 10s. The 2-core Windows runner spawns forks
and takes SQLite locks slowly enough to blow those budgets under load.

These timeouts guard against hangs; they are not timing assertions (the
one suite that does assert elapsed time, shutdown.e2e, was fixed by
removing file-level parallelism instead). Raise core to 20s and give
@cline/shared an explicit 15s in place of the inherited 5s default.
2026-08-22 16:39:19 -07:00
Saoud Rizwan 6cb653a362 chore(desktop): release v0.0.16 2026-08-22 16:34:23 -07:00
Saoud Rizwan 5077fe8697 fix(core): run hub e2e files serially so daemon timing budgets survive CI contention
singleton.e2e.test.ts (added in #13468) spawns real daemons and runs for
~15s. Vitest's default file parallelism let it run alongside
shutdown.e2e.test.ts, whose assertions are wall-clock bound: discovery
within 10s, exit within 5s, and a 2s shutdown watchdog. On the 2-core
windows-latest runner that contention alone broke those budgets, failing
the shutdown test two different ways across runs — once never observing
discovery, once with the daemon forced to exit before its HTTP 202
flushed (socket hang up). The test passed on Windows before #13468 and
has failed every SDK publish run since.
2026-08-22 15:18:04 -07:00
Saoud Rizwan 2266fe8cf4 chore(cli): release v3.0.57 2026-08-22 13:25:40 -07:00
Saoud Rizwan 21cb8d2525 chore(sdk): release v0.0.78 2026-08-22 13:03:18 -07:00
Saoud Rizwan 68ad354b52 chore(vscode): prepare 4.1.13 release 2026-08-22 12:50:54 -07:00
Saoud Rizwan e098a8ed0d fix(core): stop stored capability lists from silently revoking tool calling for custom models (#13476)
* fix(core): seed tools capability when custom model capabilities are synthesized from boolean flags

For a models.json entry with no explicit capabilities list, toStoredModelInfo
synthesized a capability array purely from boolean convenience flags (e.g.
supportsReasoning: true -> ["reasoning"]). modelSupportsToolCalling fails open
only for a missing or empty list, so the synthesized non-empty list read as an
authoritative denial and silently stripped every tool definition from requests
to custom OpenAI-compatible models (#13463).

Seed "tools" whenever the list was not explicitly authored and the boolean
projections made it non-empty, preserving the fail-open contract. Explicitly
authored capability lists remain authoritative and can still disable tools.

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

* test(core): cover stale catalog capability overrides

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

* fix: treat stored capability lists as non-authoritative for tool calling

The hasExplicitCapabilities guard still let two producers of tool-less
lists through:

- The VS Code legacy-override migration (legacyModelInfoToOverrides)
  persists explicit partial lists like ["prompt-cache"] into models.json
  for custom OpenAI-compatible models, which then read as an authoritative
  "cannot call tools" and drop every tool - same symptom as #13463.
- Any hand- or UI-authored partial list on a non-catalog model.

Stored entries and user-authored provider metadata have no way to declare
"cannot call tools" (there is no supportsTools field, and every writer
that authors a full list includes "tools"), so seed "tools" into any
non-empty list for a language model. Only generated catalog capabilities
remain authoritative - a genuine no-tools catalog model stays that way -
and non-language models (e.g. image generation) never gain a tools claim.

Also make legacyModelInfoToOverrides write "tools" into the arrays it
fabricates, matching the providers.json migration, so models.json stops
being poisoned for older readers.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-22 12:42:09 -07:00
Bee 1de61b178a feat(hub): add drain and upgrade commands with replay support (#13468)
* feat(hub): add drain and upgrade commands with replay support

* handles disconnection

* feat(hub): wire bot profiles, drain, and durable event/run-queue into the live transport

Completes the wiring the previous commits' primitives needed:
HubServerTransport gains isDraining(), hub.drain/hub.status/profile.get
command handling, and replayEventsAfter() (backed by the durable event
log), plus the sequence/sinceSequence wire types they depend on in
shared/hub.ts. run-queue-handlers.ts reads the active bot profile's
plugin roots when executing durable runs.

Also adds hub/profiles/: profile.json (identity/rules/plugins) ->
system prompt composition, --profile / CLINE_HUB_BOT_PROFILE
resolution, and the bundled cline-dad profile with its
cline_hub_support read-only diagnostics tool.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Revert "feat(hub): wire bot profiles, drain, and durable event/run-queue into the live transport"

This reverts commit 6696d5d202.

* fix(hub): dedupe replayed events by eventId, not just sequence

HubEventLogStore.append() returns a new envelope stamped with a
sequence rather than mutating the input, so a pending approval
re-issued sequence-less by subscribe() (it predates any durable-log
append) and its later sequence-stamped copy from the durable log are
two different objects carrying the same eventId. The replay-then-live
buffer in browser-websocket.ts only deduped by sequence, so the
sequence-less copy's guard never tripped and it was delivered a second
time when the buffer flushed after replay.

Track delivered eventIds alongside the sequence cursor; eventId
survives the append/stamp round-trip unchanged, so this dedupes the
exact-same logical event regardless of which copy arrives first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(hub): wire drain, durable event log, and run queue into the live transport

CI on this branch failed bun run build:sdk: browser-websocket.ts,
client/index.ts, and hub-websocket-server.ts (already on this branch)
reference sequence/sinceSequence, HubServerTransport.isDraining(), and
the "hub.drain" command — but the commit that reverted bot profiles
out of this branch also reverted this wiring, since it shared a commit
with the profiles work. That wiring is a hub concern, not a
bot-profiles one; split it back out.

- shared/hub.ts: sequence/sinceSequence types, run.enqueue/run.list/
  hub.drain/hub.status/stream.replay capability, command, and event
  names. profile.get intentionally excluded — stays bot-profiles-only.
- context.ts: isDraining() on HubTransportContext. botProfile field
  intentionally excluded.
- hub-server-transport.ts: eventLog/runQueue fields and start/stop
  lifecycle, publish() appends to the durable log, handleCommand cases
  for run.enqueue/run.list/hub.drain/hub.status, drain-refusal check,
  replayEventsAfter()/lastEventSequence(). startBotProfile()/
  startHubSupportTool() and the profile.get case intentionally
  excluded.
- run-queue-handlers.ts: added without handleProfileGet (needs
  ctx.botProfile, which doesn't exist here).
- hub-upgrades.test.ts: added without its two bot-profile-injection
  tests (they need a resolved bot profile to assert against).

Verified bun run build:sdk exits 0 (the exact CI command) and
bunx vitest run src/hub passes (311/312; the one failure is the
same pre-existing environment-timing flake already present before
this change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(hub): export instance-lock, event-log, and run-queue from the hub barrel

These landed as internal modules only; hub-server-transport.ts and
hub-websocket-server.ts import them by direct path, but nothing
re-exported them from the public @cline/core/hub surface the way
sibling discovery/server modules already are.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(hub): wire the instance lock into the daemon entry point

The singleton lock (discovery/instance-lock.ts) and its consumption in
startHubWebSocketServer/ensureHubWebSocketServer were already on this
branch, but the daemon entry point's own half was not: retrying a bind
when a retiring predecessor still holds the lock, and exiting with a
distinct code (3) instead of the generic fatal path when a live Hub
already owns the data directory. Without this, a daemon racing a
retiring predecessor could fail outright instead of waiting the lock
out, and losing the singleton race looked identical to a crash.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(hub): address drain/upgrade review findings (#13478)

- cline hub upgrade: check idleness at least once (--wait 0 works), reject
  non-numeric --wait, and un-drain on every abort path so an aborted
  upgrade can never leave the hub refusing new work
- add cline hub drain --off and the off query param to requestHubDrain so
  POST /drain?off is reachable from shipped code
- HubEventLogStore/HubRunQueue: WAL journal mode + busy_timeout, and stamp
  sequences from lastInsertRowid instead of SELECT MAX(sequence)
- HubInstanceLock.acquire: degrade to an unheld lock when SQLite is
  unavailable instead of refusing hub startup; only BUSY/LOCKED still
  raises HubLockHeldError
- ensureHubWebSocketServer: retire an unusable discovered hub through the
  shared retireDiscoveredHub (busy hubs are attached to, drain precedes
  shutdown, discovery cleared only when the hub actually retired)
- replay adapter: advance the cursor past eventId-deduped events, cap
  replay pages, stop when the cursor stalls, and drop the dedupe set after
  the buffered flush so it cannot grow for the socket lifetime

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

* fix(hub): derive the singleton e2e challenger cwd portably

The challenger's working directory was derived by round-tripping the
discovery path through a file: URL and stripping the last pathname
segment. On Windows that yields a POSIX-style '/C:/...' path, which is
not a valid spawn cwd, so the spawn fails ENOENT before the singleton
lock is ever contested and the Windows SDK test job goes red.

The data dir is simply the discovery file's parent: use dirname().

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

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-21 21:49:21 -07:00
Saoud Rizwan e7ed29109b ci(vscode): make combined nightly manual-dispatch only
The PublishNightly environment gained required reviewers, so each cron
run parked on approval, held the workflow's concurrency group, and
silently cancelled every scheduled run queued behind it. 20 consecutive
scheduled nightlies died this way between 2026-07-31 and 2026-08-21;
the only nightlies that shipped in that window were manual dispatches.

Drop the cron rather than leave a trigger that cannot succeed unattended.
2026-08-21 17:01:56 -07:00
Bee 9316de6bb5 fix: propagate Langfuse session telemetry (#13473)
* fix telemetry session propagation

* feat telemetry client version metadata

* fix(core): address Langfuse review feedback — hub client identity + delegated agent session grouping (#13475)

* fix(core): rebuild hub session client identity from request headers

Hub-backed sessions do not transport extensionContext (it is local-only),
so the daemon's runtime built traces without the clientName/clientVersion
metadata even though the hub client bakes X-CLIENT-TYPE / X-CLIENT-VERSION
into the session's provider headers. Reconstruct extensionContext.client
from those headers during local runtime bootstrap so hub-backed Langfuse
traces carry the same client identity as local runtimes, and the daemon's
header re-resolution stops clobbering the original X-CLIENT-TYPE.

* fix(core): propagate parent distinctId/sessionId to delegated agents

Delegated agents (spawned sub-agents, configured agents, teammates) were
built without distinctId and sessionId, so their Langfuse traces had no
userId or sessionId and did not group with the parent user or session.
Thread the host-resolved distinctId through RuntimeBuilderInput and the
root sessionId through the delegated-agent config provider, and copy both
onto the delegated AgentConfig.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-21 16:34:21 -07:00
Tomás Barreiro fb58e340a2 Add feature flags to the desktop app (#13289)
* Add feature flags to the app

* React to account updates

* Address comments

* use a per-app file
2026-08-22 00:25:30 +02:00
Saoud Rizwan db6d18a98a chore(vscode): prepare 4.1.12 release 2026-08-21 13:53:56 -07:00
yzxcj797 2ea460fa46 Treat an empty preserved capability list as unspecified when seeding tools (#13465)
* Treat an empty preserved capability list as unspecified when seeding tools

toSdkModelInfo guarded the tools seeding with a strict
preservedCapabilities === undefined check, but modelHasCapability —
the runtime's own reader — treats undefined AND length === 0 as
"unspecified". A custom OpenAI-Compatible model whose stored
capabilities field is a defined-but-empty array (a config carried over
from before the field existed, or one round-tripped through a boundary
that defaults it to []) skipped the seeding; the first boolean
projection to run afterwards (e.g. supportsReasoning) then populated
the array, the runtime gate read the non-empty, tool-less list as
authoritative, and every tool definition was silently dropped from the
session (#13463).

The guard now covers the empty array too, matching the reader's
unspecified semantics.

* test: satisfy the store's isModelInfo gate so the empty-capabilities case actually reaches knownModels

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

---------

Co-authored-by: yzxcj797 <yzxcj797@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-21 13:42:01 -07:00
Saoud Rizwan 7d366ce7d4 fix(vscode): remote config MCP settings (#13466)
* fix(vscode): enforce enterprise MCP controls on the Customize marketplace

The unified Customize marketplace replaced the old MCP marketplace
without carrying over enterprise remote-config enforcement: the catalog
RPC returned every MCP entry and installs were never policy-checked,
so orgs with mcpMarketplaceEnabled=false or an allowedMCPServers
allowlist saw (and could install) all marketplace MCP servers.

- Filter MCP entries out of getMarketplaceCatalog when the marketplace
  is disabled, and restrict entries to the allowlist when configured
  (matching entry id, display name, installed server name, or source
  repo URL, mirroring legacy GitHub-URL allowlist ids)
- Reject installMarketplaceEntry requests that violate the policy
- Map the published catalog's repo/homepage fields onto
  sourceUrl/homepageUrl so URL-based allowlists can match
- Update the enterprise MCP server controls docs

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

* refactor: simplify MCP marketplace policy enforcement

Fold the policy check into marketplace-helpers, drop the dedicated
test suite, and trim the docs edit to the strictly necessary line.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-21 12:40:01 -07:00
Saoud Rizwan fb60f9e5fd chore(desktop): release v0.0.15 2026-08-20 22:42:08 -07:00
Saoud Rizwan 9e0015b78b chore(vscode): prepare 4.1.11 release 2026-08-20 22:07:44 -07:00
Saoud Rizwan 4a128d5f76 docs(cli): drop the tasks tool from the v3.0.56 notes, it is desktop-only 2026-08-20 21:38:16 -07:00
Saoud Rizwan b5cfe88846 chore(sdk): release v0.0.77 2026-08-20 21:38:02 -07:00
Bee 355ce11ae2 refactor: centralize client tool availability (#13451) 2026-08-20 21:34:38 -07:00
Haley Park 05da55857a feat(desktop): reskin first-run onboarding (#13441) 2026-08-20 20:48:34 -07:00
Haley Park 35583ee8bb feat(desktop): interactive welcome hero graphic (#13399)
* feat(desktop): add interactive welcome hero

* feat(desktop): support composable welcome hero variants
2026-08-20 20:47:52 -07:00
Saoud Rizwan a8d260f1bd docs(cli): scope the v3.0.56 release notes to CLI-visible changes 2026-08-20 19:52:20 -07:00
Saoud Rizwan 59ee1ea80e chore(cli): release v3.0.56 2026-08-20 19:39:45 -07:00
Saoud Rizwan bd218aa7bb chore(sdk): release v0.0.76 2026-08-20 19:23:35 -07:00
cline-cloud[bot] 80b3b0348e docs: remove duplicate GLM-5.3 rows in ClinePass tables (#13449)
Co-authored-by: cline-cloud[bot] <276134852+cline-cloud[bot]@users.noreply.github.com>
2026-08-20 19:13:56 -07:00
Renee Huang 401cf5e12a docs: simplify Open Cline step in installing guide (#13405) 2026-08-20 18:32:58 -07:00
Bee 48feb02ae9 chore(deps): update Langfuse packages and bump app versions (#13443)
* chore(deps): update Langfuse packages and bump app versions

Update @langfuse/otel to v5.10.1 and add @langfuse/vercel-ai-sdk v5.9.1 for improved observability with Vercel AI SDK.

Bump versions for @cline/code to 0.0.14 and @cline/ui to 0.2.0-next.6, updated via bun.lock.

Other Changes:
Added optional userId to AgentRuntimeConfig.
Propagated userId, sessionId, conversationId, runId, iteration, provider, and model context into AI SDK telemetry.
Added AI SDK 7 runtimeContext with explicit includeRuntimeContext.
Added stable OTEL_SERVICE_NAME=cline-sdk.
Added runtime metadata assertions in agent tests.

* add taskId

* Revert "add taskId"

This reverts commit f20d31d96d.
2026-08-20 18:17:33 -07:00
Saoud Rizwan eef7958cad fix(core): report truthful session status so desktop checkpoint restore stops wedging (#13418)
* fix(core): keep hub session status truthful across queue-drained turns

Queue-drained turns settle only through the event stream, but the hub
runtime host mistranslated their lifecycle in two ways:

- session.updated events carrying only a snapshot (persistence updates)
  defaulted the projected status to "running". When one trailed the
  final idle update after a turn, clients that track busy state from
  status events (the desktop sidecar's workspace restore gate) stayed
  busy forever. Use the snapshot's real status and emit nothing when
  neither source reports one.
- the per-run agent.done dedup was only reset by run.started, which the
  daemon-side queue drain never publishes, so a drained turn's done was
  swallowed as a duplicate of the previous turn's. Reset the dedup on
  session.pending_prompt_submitted, and suppress stale run.completed
  events that land inside a drained turn's window so they can neither
  emit a phantom done nor consume the drained turn's dedup slot.

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

* test(desktop): cover restore unlock after an event-settled queued turn

Exports the sidecar's core-session event handler so the queued-turn
lifecycle (busy via status events, cleared by the done agent event,
restore allowed afterwards) is testable end-to-end.

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

* fix(core): start interactive sessions without a prompt as idle

The runtime host reported every new session as "running" until its
first turn ended. Interactive hosts (the desktop app) start sessions
with no prompt and dispatch turns through separate send calls, so a
created-but-never-prompted session stayed "running" forever — wedging
clients that gate workspace operations (checkpoint restore, message
edit) on active turns.

Interactive no-prompt starts now begin idle, start emits the session's
actual status (resumed sessions no longer masquerade as running), and
markTurn* transitions keep tracking in-memory status for lazily
persisted sessions so the first turn still reports running -> idle.

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

* style: format hub-runtime-host test filter

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

* refactor: drop the drained-turn done bookkeeping, keep the minimal fix

The stuck restore is fully explained by the two status defects (fabricated
"running" from snapshot-only session.updated events, and never-prompted
interactive sessions reporting "running"). The done-dedup machinery for
queue-drained turns addressed a separate cosmetic gap (queued turns emit no
chat_done, pre-existing) and required fragile run-window heuristics, so it
is removed to keep this change reviewable. Sidecar test now settles the
queued turn through the status event, matching the shipped mechanism.

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

* docs(sdk): document the truthful session-status contract

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-20 18:17:19 -07:00
Saoud Rizwan 04e08187f3 fix(vscode): show the diff edit view for multi-line edits in CRLF files (#13417)
The edit preview computed proposed content with an exact old_text match, but
the SDK executor normalizes old/new text to the file's own line endings before
matching (#12305) - reads strip CR, so models emit LF-only text even for CRLF
files. Any multi-line old_text in a CRLF file therefore failed the preview's
match: the diff edit view silently never opened while the executor applied the
edit. Single-line edits (no line break in old_text) were unaffected, which is
why the diff view appeared to trigger inconsistently.

Mirror the executor's EOL normalization (and its literal $-sequence insertion)
in the preview computation.

Fixes #13296

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-20 14:55:19 -07:00
Saoud Rizwan fed502e3cf fix(vscode): honor the classic truncation range when migrating legacy tasks (#13419)
Classic Cline truncated long conversations by omitting an index range of
api_conversation_history from every API request (keep the first
user-assistant pair, drop everything through the range end, strip
orphaned tool_results from the first kept message). The range was
persisted on the history item while the full history stayed on disk.

legacyApiHistoryToSdkMessages ignored conversationHistoryDeletedRange
and converted the entire file, so resuming a migrated long task handed
the SDK an untruncated working context that could exceed the model's
context window by millions of tokens - every request failed with
'prompt is too long' and every compaction restarted from the full
history (#12996, confirmed by the reporter: the task was migrated from
an older version and broke after a restart, with each compaction
starting from ~3M tokens).

The migration now replays exactly what the classic extension sent:
slice out the deleted range and drop orphaned tool_results, mirroring
ContextManager.getTruncatedMessages (see origin/main). Malformed ranges
fall back to the full history (previous behavior).

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-20 13:33:24 -07:00
Mikołaj Kondratek e685ebfd04 fix(core): stop Windows CI worker crashes from the agenda spec watcher (#13428)
* fix(core): watch agenda task specs via the resolved long path

fs.watch on a path with 8.3 short components (e.g. C:\Users\RUNNER~1
temp dirs) trips a libuv assertion in fs-event.c on Windows and aborts
the whole process. Since the agenda task manager landed, every hub
server test spins up its spec watcher on such a path on hosted Windows
runners, killing the vitest worker and failing the sdk-test Windows job
on every branch. Resolve the specs dir with realpathSync.native before
watching so libuv only ever sees the long form.

* test(ui): stub ResizeObserver for @pierre/diffs in tool-diff tests

jsdom does not implement ResizeObserver, so every ToolFileDiff render
logged a ReferenceError from @pierre/diffs to stderr. Tests still
passed; this just silences the noise the same way the constructable
stylesheet shim does.

* fix(core): skip the agenda spec watcher when the dir does not resolve

Falling back to the raw path on realpath failure would reintroduce the
Windows short-path abort; log and go without the watcher instead.
2026-08-20 22:26:02 +02:00
Mikołaj Kondratek 9b9a067fb8 fix(hooks): collect PostToolUse hook output and honor its control (#13298)
* fix(hooks): collect PostToolUse hook output and honor its control

tool_result (PostToolUse) hooks ran fire-and-forget with stdout
ignored, so their entire JSON output — contextModification and cancel —
was discarded. Legacy awaited PostToolUse, injected its
contextModification into the conversation, and honored cancel.

- Run tool_result hook commands blocking (same 120s default timeout as
  tool_call) in both the hook-config-file layer and the agent-hook
  subprocess layer.
- Map their output: cancel stops the run with the hook's error message
  as the reason; otherwise context is injected via afterTool
  appendContext.

This restores legacy blocking semantics: tool results now wait for
tool_result hooks, but only in sessions that have one configured.

Ref: https://linear.app/cline-bot/issue/CLINE-2987

* fix(hooks): bound tool_result hook wait and isolate cancel reason

Address review findings:
- The agent-hook subprocess layer forwarded an unset timeoutMs
  unchanged, so a tool hook command that never exits would block the
  agent indefinitely. Default both tool_call and tool_result to the
  120s bound the hook-config-file layer already used.
- A cancelling hook's error message was folded into the same context
  field as other hooks' injectable context, so merging controls could
  leak unrelated hook context into the cancellation reason. Carry it as
  a separate cancelReason, and surface it as the stop reason for
  beforeTool cancels too.

* fix(hooks): prefer errorMessage as a cancelling hook's stop reason

When a cancelling hook returns both contextModification and
errorMessage, the context-first parse precedence made the injectable
context the cancel reason and discarded the actual error. Parse the two
fields separately: errorMessage wins as the cancel reason (matching
legacy), and a lone errorMessage still folds into injectable context
for non-cancelling hooks as before.

* fix(vscode): honor PostToolUse hook cancel and contextModification

The adapter awaited PostToolUse hooks but discarded their output
entirely. Map cancel to a stop control (with errorMessage as the
reason) and contextModification into the runtime appendContext channel,
matching the PreToolUse mapping and legacy semantics.

* fix(hooks): whitespace-only errorMessage no longer suppresses the cancel reason

A cancelling hook returning meaningful context alongside a blank
errorMessage lost both: the parsers selected the whitespace as the
reason and the result mappers trimmed it away. Require a non-blank
errorMessage before it wins, so context serves as the fallback reason.
Apply the same fallback in the extension adapter's stop mapping.
2026-08-20 21:09:51 +02:00
Mikołaj Kondratek 8fe5a196c4 fix(hooks): deliver tool hook contextModification to the model (#13297)
* fix(hooks): deliver tool hook contextModification to the model

On the next engine, a tool_call (PreToolUse) hook's contextModification
was parsed into HookControl.context and then silently dropped: the
runtime beforeTool/afterTool result contract had no channel for
injecting conversation context. Legacy consumed it (ToolExecutor /
ToolHookUtils pushed <hook_context> blocks into the next user turn), so
this was a regression of documented behavior.

- Add appendContext to AgentBeforeToolResult/AgentAfterToolResult.
- AgentRuntime collects appendContext across hooks during an
  iteration's tool executions and appends one <hook_context> user
  message after the tool results, keeping tool-result parts contiguous.
- Map HookControl.context into appendContext in both subprocess hook
  layers (skipped when the hook cancels, matching legacy, where the
  message doubled as the error).
- Truncate injected context at 50KB per hook output, matching legacy.
- Concatenate appendContext across merged hook layers.

tool_result (PostToolUse) hooks still run detached with stdout ignored;
making them blocking so their context can be collected is a follow-up.

Ref: https://linear.app/cline-bot/issue/CLINE-2987

* fix(hooks): stamp tool identity on injected hook context blocks

Contexts are batched into one message after the tool results, and
parallel tool execution collects them in completion order, so position
alone cannot attribute a block to its tool call. Add tool_name and
tool_call_id attributes to each <hook_context> block.

* fix(hooks): sanitize hook context block markup

Attribute values (tool_name, tool_call_id) are stripped of quote/angle
characters and embedded </hook_context> closers in hook output are
neutralized, so neither provider-supplied ids nor hook text can corrupt
or spoof a block's stamped identity.

* fix(hooks): neutralize forged opening hook_context tags in hook output

The previous sanitization only neutralized closing tags, so hook output
could still open a forged <hook_context> block claiming another tool's
identity. Escape both opening and closing embedded tags with one rule.

* fix(hooks): hide injected hook context from user-facing transcripts

Stamp the injected hook-context user message with displayRole 'system'
(the compaction-summary convention) so it reaches the model but does
not render as a user bubble in live or replayed transcripts. Without
this, resuming a session showed the raw <hook_context> block as if the
user had typed it.

* fix(hooks): neutralize case-variant embedded hook_context tags

The tag-neutralization regex was case-sensitive, so hook output could
still smuggle a forged tag as <HOOK_CONTEXT>. Match case-insensitively.

* fix(vscode): map PreToolUse contextModification into runtime appendContext

The extension's hooks adapter bridged file hooks into the SDK runtime
but forwarded only cancel/errorMessage, so a PreToolUse hook's
contextModification never reached the model. Map it into the runtime's
appendContext channel; HookFactory already truncates it at 50KB.

* fix(vscode): hide hook-injected context from replayed transcripts

Live sessions never rendered the injected <hook_context> user message,
but session reload replayed it as a user bubble (and post-resume turns
kept doing so). Treat these messages as synthetic in the user-message
mapping: honor the displayRole 'system' stamp the runtime sets, with a
text-prefix guard for paths where metadata is unavailable. This also
keeps edit/regenerate ordinal mapping aligned with visible bubbles.

* fix(hooks): run file hooks through exactly one layer per host

The VS Code extension registered two independent hook execution layers:
its own hooks adapter (config.hooks) and the SDK core's file-hook
extension from the runtime bootstrap. When both discover the same hook
files, every hook executes twice per event — and with context injection
wired, each contextModification would be injected twice.

Add a 'hooks' runtime config extension kind (in the default set, so the
CLI keeps core file hooks unchanged) and gate the bootstrap's file-hook
extension on it. The extension excludes 'hooks' at session start, so
its adapter — which also provides the hook status UI and the
hooksEnabled setting — is its single execution path.

* fix(vscode): discover hooks from the session workspace, not only global state

Hook discovery read workspaceRoots from global state shared across
every Cline instance, so another window repointing it made workspace
hooks silently stop being discovered. With the extension's adapter now
the single hook execution layer, that meant no hooks at all.

HookFactory takes an optional sessionWorkspaceRoot and unions that
root's .clinerules/hooks into discovery (and into cwd resolution), fed
from the session config's cwd. Shared-state discovery still works, so
behavior in the single-window case is unchanged.

* fix(hooks): keep sanitized hook attribute values distinguishable

Replacing every markup delimiter with the same underscore could
collapse two tool call ids that differ only by such a character into
identical stamps. Escape each delimiter with a distinct token instead.

* fix(hooks): make hook attribute sanitization injective

Escaping the underscore itself turns the attribute escaping into a
uniquely decodable code, so no two distinct tool call ids can collapse
to the same sanitized stamp (previously an id containing a literal
escape token could collide with an id containing the delimiter).

* fix(vscode): reconstruct hook status rows when replaying transcripts

hook_status messages are emitted live but never persisted, so reloading
a session dropped every hook row. The injected <hook_context> blocks
carry the hook source and tool name, so the replay translator now
rebuilds a completed hook status row from each block. The injection is
also no longer treated as a user turn boundary, so the final turn's
completion retag is unaffected by it.
2026-08-20 20:41:53 +02:00
Mikołaj Kondratek e70b3ffc4b ci(vscode): upload E2E failure recordings from the right path (#13427)
The job sets working-directory: apps/vscode, but that default applies to run
steps only, not to `uses:` steps. Since #10961 moved the extension under apps/
and added that default, the artifact path has resolved against the repo root,
matched nothing, and every failing run logged "No files were found with the
provided path: test-results/playwright/" instead of uploading recordings.

Widen to test-results/ so Playwright's error-context snapshots ship alongside
the videos.
2026-08-20 20:10:12 +02:00
Haley Park e6f4d5fef7 feat(desktop): refresh app icons and branding (#13400) 2026-08-20 08:42:50 -07:00
Saoud Rizwan 16875140fb fix(ui): update packed-Tailwind smoke contract for the picker's max-h-64 (#13421)
The ui-publish smoke check pins a set of Tailwind candidates the packed
sources must emit; #13410 grew the SearchCombobox options list from
max-h-56 to max-h-64, so the publish run failed on the stale candidate.
All other pinned candidates verified against the current sources.
2026-08-19 22:19:14 -07:00
Saoud Rizwan 7903c76812 feat(desktop): recommended-feed badges and descriptions in provider settings (#13416)
* feat(ui): sectioned model picker support in SearchCombobox

Adds option sections with headers, badges (NEW/Free pills), keyboard
navigation (arrows/Home/End/Enter with active-row tracking and
aria-activedescendant), substring match highlighting, a configurable
panel width, a trigger chevron, and a cleaner borderless search row.
All additions are backwards compatible; bumps @cline/ui to
0.2.0-next.6.

* feat(desktop): recommended and free model tiers in the composer picker

The composer's model selector showed raw provider/model ids and listed
the entire catalog alphabetized by id. It now labels providers and
models by display name and, for the cline provider, leads with the
Recommended and Free tiers from the recommended-models feed (NEW/Free
badges, descriptions) ahead of an All models section — matching the
CLI's featured picker and the kanban selector. cline-pass gets
Subscribed/Free tiers. A new list_cline_recommended_models sidecar
command exposes @cline/core's fetchClineRecommendedModels (display-ready
names, bundled offline fallback); feed ids resolve against the catalog
with a unique-slug fallback for Vercel/OpenRouter alias spellings, and
unresolvable entries are dropped rather than rendered unselectable.

* fix(desktop): widen the provider trigger for display names

Provider labels are now display names (e.g. "Cline Usage-Billing"),
which truncated badly at max-w-28.

* chore(desktop): drop unused featured-models test helper

* style(desktop): align workspace/branch picker search rows with the model picker

The composer's workspace/branch popover and the welcome screen's
workspace and branch pickers used a boxed inner search shell that now
clashed with the model picker's borderless search row sitting next to
them. Behavior unchanged.

* feat(ui): center the selected option when SearchCombobox opens

Opening a long list previously scrolled the selection just into view at
the panel edge; it now lands centered, and keyboard/hover navigation
falls back to minimal nearest-edge scrolling.

* style(desktop): picker row contrast, transparent search fields, centered open

The workspace/branch pickers' rows had a nearly invisible
surface-hover-lighter hover; rows now hover with surface-hover and mark
the current entry with the accent background plus check, matching the
model picker. The search inputs drop the Input base class's
dark:bg-input/30 tint that rendered a gray box inside the panel in dark
mode. Opening a picker now centers the current workspace/branch via a
shared scroll helper instead of starting at the top of the list.

* fix(ui): visible option hover/selected states and no scroll-jump on hover

The option row stacked bg-transparent with the conditional state
backgrounds; at equal specificity the later-sorted bg-transparent
utility won, so hover/selected rows rendered with no background at all.
The background classes are now mutually exclusive.

Mouse-driven active-row changes also reused the keyboard scroll-into-
view effect: hovering a row at the panel edge scrolled it into view,
which moved the list under the cursor and re-triggered hover — an
endless jump. Scroll mode is now per-source: center on open, nearest
for keyboard/typing, none for hover.

* fix(desktop): show only subscribed and free tiers in the cline-pass picker

The ClinePass offer is exactly the feed's subscribed + free tiers, but
stale bundled/cached catalog entries (e.g. a nemotron model) leaked
into an "All models" tier. Match the CLI's featured picker: hide
catalog leftovers, and only fall back to the full catalog when the
subscribed bucket is empty so a subscriber is never limited to free
models offline.

* fix(ui/desktop): strengthen the selected-row highlight in light mode

The selected row used the semantic accent surface (violet step 3),
which is nearly white in light mode. SearchCombobox and the desktop
workspace/branch pickers now highlight the selected/current row with
accent step 4 (with a fallback to --accent), which reads clearly in
both themes without touching the shared --accent token that shadcn
hover states depend on.

* fix(desktop): fit full provider display names in the composer trigger

"Cline Usage-Billing" — the default provider — truncated to
"Cline Usage-Bi…" at max-w-36; the trigger now allows up to max-w-56,
which fits the longest built-in provider names.

* style(ui/desktop): animate picker panels open like the shadcn dropdowns

The thinking-effort Select (shadcn/Radix) animates open while the
model/provider/workspace/branch pickers popped in instantly. All picker
panels now share the same open treatment — 150ms fade + slight zoom,
sliding from the trigger side. SearchCombobox uses a self-contained CSS
keyframe (consumers may not ship tw-animate-css); the desktop's custom
panels use the app's tw-animate utilities. Both respect
prefers-reduced-motion.

* chore(desktop): drop stale eslint-disable comments in picker search rows

This repo lints with biome; the jsx-a11y/no-autofocus disables were
inert leftovers. Flagged in review.

* refactor(core/desktop): stamp recommended-feed tiers onto ProviderModel in the SDK

Review feedback on the composer picker: tier joining should live where
the SDK serves model lists so each client doesn't fetch and join the
recommended-models feed itself (the CLI and now the desktop each did).

ProviderModel gains description and featured ({tier, rank, tags});
getLocalProviderModels overlays the feed's recommended/free tiers onto
cline models and subscribed/free onto cline-pass via
applyClineFeaturedModels, matching feed ids through the
Vercel/OpenRouter alias rules. The feed access is a new cached wrapper
(getCachedClineRecommendedModels, 5-minute TTL, in-flight dedupe) —
this path runs on every picker open, and the bundled offline fallback
is cached too so offline users don't re-pay the 5s timeout per list.

The desktop webview now reads tiers straight off the models: the
list_cline_recommended_models sidecar command, the webview feed fetch,
and its unique-slug alias matching are all deleted. toProviderModel
also carries ModelInfo.description generally.

* feat(desktop): recommended-feed badges and descriptions in provider settings

Review suggestion on #13410: the provider settings page has room for
more model detail than the composer's picker. The cline/cline-pass
provider cards now refresh their model list through
list_provider_models (the catalog snapshot deliberately skips the
recommended-feed overlay so the startup catalog fetch never blocks on
the feed) and render Recommended/Free tier badges plus feed tags (NEW)
next to the model name, with the model description underneath. The
refreshed list also surfaces the live entries instead of the bundled
snapshot.

* fix(ui): hand focus back to the combobox trigger on selection, close on Tab

Selecting an option (Enter or click) unmounted the focused search input
without a new focus target, dropping keyboard users' focus to <body> —
only Escape restored it. And since the search input is the panel's only
tabbable element, Tab always moved focus outside the component while
leaving the popup open behind the new focus target.

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

* fix(desktop): keep the composer model selection inside the picker's visible offer

The active/remembered model was validated against the provider's full
catalog while the picker can intentionally hide models (the ClinePass
offer is exactly its subscribed/free tiers), so a stale remembered model
could become the selection while being absent from the dropdown.

Remembered and default selections (including on provider switch) now
resolve against the picker's visible options, and an explicitly
configured model that falls outside the offer stays active but is
surfaced under a 'Current model' section so the selection is always
visible and re-selectable.

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

* fix(desktop): scope the settings featured model list to its provider and revision

The fetched featured list was unscoped component state: switching
between cline and cline-pass reused the component instance, so the
previous provider's models stayed visible while the new request was
pending (or forever, when it failed), and the retained copy shadowed
later provider.modelList updates — adding a second custom model
submitted the stale list as the complete configuration and dropped the
first addition.

The fetched list now only applies to the provider and modelList
revision it was fetched for (falling back to the catalog snapshot
otherwise and refetching on membership changes), and add-model submits
the union of the displayed and configured ids so an update can never
silently unconfigure existing entries.

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

* feat(core): stamp featured tiers onto the provider catalog synchronously

listLocalProviders deliberately skipped the feed overlay so the catalog
never blocks on the network — but that left the composer's very first
picker open after a cold boot rendering an untiered flat list until the
per-provider fetch landed. Blocking was never required: stamp tiers from
a synchronous peek at data already in memory (the cached live feed when
fresh, else the bundled fallback, whose recommended ids resolve against
the bundled cline catalog). The per-provider model-list path still
refreshes with live feed data moments later.

* fix(core): harden featured-tier matching and the feed cache reset

Review findings on the tier overlay:

Vendor-prefix mismatches now match by unambiguous id slug (two-pass, so
a catalog carrying both spellings of a model stamps one row, and a slug
shared by two feed entries stamps nothing) — the bundled fallback feed's
vendor-prefixed ids can otherwise miss cline-free/-prefixed catalog
entries, leaving them untiered in degraded mode.

resetClineRecommendedModelsCacheForTests now bumps a generation so an
in-flight feed request resolving after a reset cannot repopulate the
cache it just cleared.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 22:13:40 -07:00
Saoud Rizwan b060cecc05 refactor(core/desktop): stamp recommended-feed tiers onto ProviderModel in the SDK (#13415)
* feat(ui): sectioned model picker support in SearchCombobox

Adds option sections with headers, badges (NEW/Free pills), keyboard
navigation (arrows/Home/End/Enter with active-row tracking and
aria-activedescendant), substring match highlighting, a configurable
panel width, a trigger chevron, and a cleaner borderless search row.
All additions are backwards compatible; bumps @cline/ui to
0.2.0-next.6.

* feat(desktop): recommended and free model tiers in the composer picker

The composer's model selector showed raw provider/model ids and listed
the entire catalog alphabetized by id. It now labels providers and
models by display name and, for the cline provider, leads with the
Recommended and Free tiers from the recommended-models feed (NEW/Free
badges, descriptions) ahead of an All models section — matching the
CLI's featured picker and the kanban selector. cline-pass gets
Subscribed/Free tiers. A new list_cline_recommended_models sidecar
command exposes @cline/core's fetchClineRecommendedModels (display-ready
names, bundled offline fallback); feed ids resolve against the catalog
with a unique-slug fallback for Vercel/OpenRouter alias spellings, and
unresolvable entries are dropped rather than rendered unselectable.

* fix(desktop): widen the provider trigger for display names

Provider labels are now display names (e.g. "Cline Usage-Billing"),
which truncated badly at max-w-28.

* chore(desktop): drop unused featured-models test helper

* style(desktop): align workspace/branch picker search rows with the model picker

The composer's workspace/branch popover and the welcome screen's
workspace and branch pickers used a boxed inner search shell that now
clashed with the model picker's borderless search row sitting next to
them. Behavior unchanged.

* feat(ui): center the selected option when SearchCombobox opens

Opening a long list previously scrolled the selection just into view at
the panel edge; it now lands centered, and keyboard/hover navigation
falls back to minimal nearest-edge scrolling.

* style(desktop): picker row contrast, transparent search fields, centered open

The workspace/branch pickers' rows had a nearly invisible
surface-hover-lighter hover; rows now hover with surface-hover and mark
the current entry with the accent background plus check, matching the
model picker. The search inputs drop the Input base class's
dark:bg-input/30 tint that rendered a gray box inside the panel in dark
mode. Opening a picker now centers the current workspace/branch via a
shared scroll helper instead of starting at the top of the list.

* fix(ui): visible option hover/selected states and no scroll-jump on hover

The option row stacked bg-transparent with the conditional state
backgrounds; at equal specificity the later-sorted bg-transparent
utility won, so hover/selected rows rendered with no background at all.
The background classes are now mutually exclusive.

Mouse-driven active-row changes also reused the keyboard scroll-into-
view effect: hovering a row at the panel edge scrolled it into view,
which moved the list under the cursor and re-triggered hover — an
endless jump. Scroll mode is now per-source: center on open, nearest
for keyboard/typing, none for hover.

* fix(desktop): show only subscribed and free tiers in the cline-pass picker

The ClinePass offer is exactly the feed's subscribed + free tiers, but
stale bundled/cached catalog entries (e.g. a nemotron model) leaked
into an "All models" tier. Match the CLI's featured picker: hide
catalog leftovers, and only fall back to the full catalog when the
subscribed bucket is empty so a subscriber is never limited to free
models offline.

* fix(ui/desktop): strengthen the selected-row highlight in light mode

The selected row used the semantic accent surface (violet step 3),
which is nearly white in light mode. SearchCombobox and the desktop
workspace/branch pickers now highlight the selected/current row with
accent step 4 (with a fallback to --accent), which reads clearly in
both themes without touching the shared --accent token that shadcn
hover states depend on.

* fix(desktop): fit full provider display names in the composer trigger

"Cline Usage-Billing" — the default provider — truncated to
"Cline Usage-Bi…" at max-w-36; the trigger now allows up to max-w-56,
which fits the longest built-in provider names.

* style(ui/desktop): animate picker panels open like the shadcn dropdowns

The thinking-effort Select (shadcn/Radix) animates open while the
model/provider/workspace/branch pickers popped in instantly. All picker
panels now share the same open treatment — 150ms fade + slight zoom,
sliding from the trigger side. SearchCombobox uses a self-contained CSS
keyframe (consumers may not ship tw-animate-css); the desktop's custom
panels use the app's tw-animate utilities. Both respect
prefers-reduced-motion.

* chore(desktop): drop stale eslint-disable comments in picker search rows

This repo lints with biome; the jsx-a11y/no-autofocus disables were
inert leftovers. Flagged in review.

* refactor(core/desktop): stamp recommended-feed tiers onto ProviderModel in the SDK

Review feedback on the composer picker: tier joining should live where
the SDK serves model lists so each client doesn't fetch and join the
recommended-models feed itself (the CLI and now the desktop each did).

ProviderModel gains description and featured ({tier, rank, tags});
getLocalProviderModels overlays the feed's recommended/free tiers onto
cline models and subscribed/free onto cline-pass via
applyClineFeaturedModels, matching feed ids through the
Vercel/OpenRouter alias rules. The feed access is a new cached wrapper
(getCachedClineRecommendedModels, 5-minute TTL, in-flight dedupe) —
this path runs on every picker open, and the bundled offline fallback
is cached too so offline users don't re-pay the 5s timeout per list.

The desktop webview now reads tiers straight off the models: the
list_cline_recommended_models sidecar command, the webview feed fetch,
and its unique-slug alias matching are all deleted. toProviderModel
also carries ModelInfo.description generally.

* fix(ui): hand focus back to the combobox trigger on selection, close on Tab

Selecting an option (Enter or click) unmounted the focused search input
without a new focus target, dropping keyboard users' focus to <body> —
only Escape restored it. And since the search input is the panel's only
tabbable element, Tab always moved focus outside the component while
leaving the popup open behind the new focus target.

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

* fix(desktop): keep the composer model selection inside the picker's visible offer

The active/remembered model was validated against the provider's full
catalog while the picker can intentionally hide models (the ClinePass
offer is exactly its subscribed/free tiers), so a stale remembered model
could become the selection while being absent from the dropdown.

Remembered and default selections (including on provider switch) now
resolve against the picker's visible options, and an explicitly
configured model that falls outside the offer stays active but is
surfaced under a 'Current model' section so the selection is always
visible and re-selectable.

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

* feat(core): stamp featured tiers onto the provider catalog synchronously

listLocalProviders deliberately skipped the feed overlay so the catalog
never blocks on the network — but that left the composer's very first
picker open after a cold boot rendering an untiered flat list until the
per-provider fetch landed. Blocking was never required: stamp tiers from
a synchronous peek at data already in memory (the cached live feed when
fresh, else the bundled fallback, whose recommended ids resolve against
the bundled cline catalog). The per-provider model-list path still
refreshes with live feed data moments later.

* fix(core): harden featured-tier matching and the feed cache reset

Review findings on the tier overlay:

Vendor-prefix mismatches now match by unambiguous id slug (two-pass, so
a catalog carrying both spellings of a model stamps one row, and a slug
shared by two feed entries stamps nothing) — the bundled fallback feed's
vendor-prefixed ids can otherwise miss cline-free/-prefixed catalog
entries, leaving them untiered in degraded mode.

resetClineRecommendedModelsCacheForTests now bumps a generation so an
in-flight feed request resolving after a reset cannot repopulate the
cache it just cleared.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 22:10:41 -07:00
Saoud Rizwan 4d1bafc443 feat(desktop/ui): recommended and free model tiers in the composer model selector (#13410)
* feat(ui): sectioned model picker support in SearchCombobox

Adds option sections with headers, badges (NEW/Free pills), keyboard
navigation (arrows/Home/End/Enter with active-row tracking and
aria-activedescendant), substring match highlighting, a configurable
panel width, a trigger chevron, and a cleaner borderless search row.
All additions are backwards compatible; bumps @cline/ui to
0.2.0-next.6.

* feat(desktop): recommended and free model tiers in the composer picker

The composer's model selector showed raw provider/model ids and listed
the entire catalog alphabetized by id. It now labels providers and
models by display name and, for the cline provider, leads with the
Recommended and Free tiers from the recommended-models feed (NEW/Free
badges, descriptions) ahead of an All models section — matching the
CLI's featured picker and the kanban selector. cline-pass gets
Subscribed/Free tiers. A new list_cline_recommended_models sidecar
command exposes @cline/core's fetchClineRecommendedModels (display-ready
names, bundled offline fallback); feed ids resolve against the catalog
with a unique-slug fallback for Vercel/OpenRouter alias spellings, and
unresolvable entries are dropped rather than rendered unselectable.

* fix(desktop): widen the provider trigger for display names

Provider labels are now display names (e.g. "Cline Usage-Billing"),
which truncated badly at max-w-28.

* chore(desktop): drop unused featured-models test helper

* style(desktop): align workspace/branch picker search rows with the model picker

The composer's workspace/branch popover and the welcome screen's
workspace and branch pickers used a boxed inner search shell that now
clashed with the model picker's borderless search row sitting next to
them. Behavior unchanged.

* feat(ui): center the selected option when SearchCombobox opens

Opening a long list previously scrolled the selection just into view at
the panel edge; it now lands centered, and keyboard/hover navigation
falls back to minimal nearest-edge scrolling.

* style(desktop): picker row contrast, transparent search fields, centered open

The workspace/branch pickers' rows had a nearly invisible
surface-hover-lighter hover; rows now hover with surface-hover and mark
the current entry with the accent background plus check, matching the
model picker. The search inputs drop the Input base class's
dark:bg-input/30 tint that rendered a gray box inside the panel in dark
mode. Opening a picker now centers the current workspace/branch via a
shared scroll helper instead of starting at the top of the list.

* fix(ui): visible option hover/selected states and no scroll-jump on hover

The option row stacked bg-transparent with the conditional state
backgrounds; at equal specificity the later-sorted bg-transparent
utility won, so hover/selected rows rendered with no background at all.
The background classes are now mutually exclusive.

Mouse-driven active-row changes also reused the keyboard scroll-into-
view effect: hovering a row at the panel edge scrolled it into view,
which moved the list under the cursor and re-triggered hover — an
endless jump. Scroll mode is now per-source: center on open, nearest
for keyboard/typing, none for hover.

* fix(desktop): show only subscribed and free tiers in the cline-pass picker

The ClinePass offer is exactly the feed's subscribed + free tiers, but
stale bundled/cached catalog entries (e.g. a nemotron model) leaked
into an "All models" tier. Match the CLI's featured picker: hide
catalog leftovers, and only fall back to the full catalog when the
subscribed bucket is empty so a subscriber is never limited to free
models offline.

* fix(ui/desktop): strengthen the selected-row highlight in light mode

The selected row used the semantic accent surface (violet step 3),
which is nearly white in light mode. SearchCombobox and the desktop
workspace/branch pickers now highlight the selected/current row with
accent step 4 (with a fallback to --accent), which reads clearly in
both themes without touching the shared --accent token that shadcn
hover states depend on.

* fix(desktop): fit full provider display names in the composer trigger

"Cline Usage-Billing" — the default provider — truncated to
"Cline Usage-Bi…" at max-w-36; the trigger now allows up to max-w-56,
which fits the longest built-in provider names.

* style(ui/desktop): animate picker panels open like the shadcn dropdowns

The thinking-effort Select (shadcn/Radix) animates open while the
model/provider/workspace/branch pickers popped in instantly. All picker
panels now share the same open treatment — 150ms fade + slight zoom,
sliding from the trigger side. SearchCombobox uses a self-contained CSS
keyframe (consumers may not ship tw-animate-css); the desktop's custom
panels use the app's tw-animate utilities. Both respect
prefers-reduced-motion.

* chore(desktop): drop stale eslint-disable comments in picker search rows

This repo lints with biome; the jsx-a11y/no-autofocus disables were
inert leftovers. Flagged in review.

* fix(ui): hand focus back to the combobox trigger on selection, close on Tab

Selecting an option (Enter or click) unmounted the focused search input
without a new focus target, dropping keyboard users' focus to <body> —
only Escape restored it. And since the search input is the panel's only
tabbable element, Tab always moved focus outside the component while
leaving the popup open behind the new focus target.

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

* fix(desktop): keep the composer model selection inside the picker's visible offer

The active/remembered model was validated against the provider's full
catalog while the picker can intentionally hide models (the ClinePass
offer is exactly its subscribed/free tiers), so a stale remembered model
could become the selection while being absent from the dropdown.

Remembered and default selections (including on provider switch) now
resolve against the picker's visible options, and an explicitly
configured model that falls outside the offer stays active but is
surfaced under a 'Current model' section so the selection is always
visible and re-selectable.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 21:46:07 -07:00
Saoud Rizwan 74a8e06e41 Desktop: unify Plugins, MCP, and Skills into one Plugins hub with a dedicated Marketplace page (#13411)
* Unify desktop plugins, apps, MCP, and skills into one Plugins hub with a Browse directory mode

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

* Open the marketplace directory as a modal over the Plugins hub instead of swapping the page

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

* Rename directory to Marketplace: Browse Marketplace button, Marketplace modal title with icon, search placeholder

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

* Fix search input focus ring clipped by the Marketplace modal scroll container

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

* Address Greptile review: keep selected tag chip visible when its count drops to zero, and remount installed tab when a marketplace install completes after the modal closed

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

* Track marketplace modal mutation flag in a ref so a close click racing a queued render cannot skip the inventory remount

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

* Make Marketplace its own settings page under Customizations and restore Channels as a standalone page

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

* Remove icon from Marketplace page header for consistency with other settings pages

* Notify mounted inventory views when the marketplace invalidates the cache so late install completions refresh the Plugins hub

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 21:42:02 -07:00
Saoud Rizwan 94beb6c5f1 Fix code actions failing with "command not found" on VS Code 1.134 (#13402)
* Fix @ file mentions breaking on paths with spaces

Quote mentions generated by getFileMentionFromPath (Add to Cline /
Fix / Explain / Improve commands) when the relative path contains
spaces, so the mention regex no longer truncates the path at the
first space. Also quote the path part of workspace-prefixed mentions
(workspace:/path with spaces) inserted from the @ context menu, which
previously bypassed quoting because the value does not start with '/'.

Fixes #13338

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

* Fix import ordering in mentions test (biome organize imports)

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

* Reduce fix to minimal scope

Revert the webview quoting refactor and extra tests; keep only the
getFileMentionFromPath quoting fix with a single regression test.

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

* Normalize mention paths to posix separators for Windows

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

* Fix code actions failing with 'command not found' on VS Code 1.134

Code action commands carried arguments (expandedRange, diagnostics),
which routes them through VS Code's CommandsConverter cache. VS Code
1.134 disposes the cached entries before the clicked action executes,
so every lightbulb action failed with 'Actual command not found,
wanted to execute cline.addToChat'.

Drop the arguments so the command id is passed through directly, and
recover the context in the handler instead: getContextForCommand now
expands an empty selection by 3 surrounding lines (matching the old
provider behavior) and gathers document diagnostics intersecting the
range when none are passed explicitly.

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

* Scope gathered diagnostics to the selection/cursor

Match the old CodeActionContext.diagnostics behavior: only include
diagnostics intersecting the range the action was requested for, not
the surrounding lines the text gets expanded to.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 18:36:21 -07:00
Saoud Rizwan 8720363ba3 Fix @ file mentions breaking on paths with spaces (#13391)
* Fix @ file mentions breaking on paths with spaces

Quote mentions generated by getFileMentionFromPath (Add to Cline /
Fix / Explain / Improve commands) when the relative path contains
spaces, so the mention regex no longer truncates the path at the
first space. Also quote the path part of workspace-prefixed mentions
(workspace:/path with spaces) inserted from the @ context menu, which
previously bypassed quoting because the value does not start with '/'.

Fixes #13338

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

* Fix import ordering in mentions test (biome organize imports)

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

* Reduce fix to minimal scope

Revert the webview quoting refactor and extra tests; keep only the
getFileMentionFromPath quoting fix with a single regression test.

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

* Normalize mention paths to posix separators for Windows

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 17:59:16 -07:00
Saoud Rizwan f291a269a6 fix(desktop): don't show "No sessions found" while session history is still loading (#13414)
* fix(desktop): don't show 'No sessions found' while session history is still loading

Replace the isLoadingHistory flag with hasLoadedHistory, set only once the
backend has actually answered a list_discovered_sessions request. The sidebar
and Sessions view now keep their loading state until that first definitive
response, so the empty-state copy can no longer appear while history is still
being fetched (or while a failed fetch is being retried).

Also retry a failed initial fetch on the 2s event cadence instead of stranding
the UI until the 12s periodic poll, which is what stretched the misleading
empty state to ~10 seconds after a webview reload when the websocket lost the
race with the page load.

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

* fix(desktop): stop history fast-retry from re-arming after hook unmount

A failed initial fetch that settles after the hook unmounted could schedule a
new retry timer after cleanup had already cleared the refs, leaving the
abandoned hook polling the backend every 2s. Guard scheduleRefresh with a
disposed ref set by the mount effect's cleanup.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 17:57:49 -07:00
Saoud Rizwan 06013b9c08 fix(desktop): remove settings gear hover state while Account screen is open (#13408)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 17:53:47 -07:00
Saoud Rizwan c14cc2c696 fix(desktop): work summary undercounts wall time when pre-tool thinking attaches to the answer (#13413)
* fix(desktop): anchor work summary duration on the answer row, not attached pre-tool reasoning

The collapsed 'Worked for Xs' row undercounted wall time whenever a turn's
assistant message contained thinking + tool_use with no narration text: the
canonical projection emitted the reasoning-only row after the tool row (both
stamped before the tool executed), the webview attached that row to the final
answer, and collapseCompletedWork used the answer's earliest attached
reasoning timestamp as the end anchor - excluding the entire tool execution
(e.g. 'Worked for 5s' for a turn with an 8s command).

- webview: end the work span at the answer row's own timestamp, clamped to
  the last collapsed row so a fallback answer bubble with a synthetic early
  timestamp cannot shrink the duration either
- sidecar: flush pending thinking before a tool_use row so rehydrated
  transcripts keep the live-stream order (thinking before its tool call) and
  pre-tool reasoning no longer rides on the next answer

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

* fix(desktop): keep interleaved thinking between the tool calls it separates

Address Greptile review: when one assistant message interleaves thinking
between multiple tool_use blocks, each reasoning segment now projects at its
own position (attached to a text row from its own segment when present,
otherwise as its own row) instead of merging into the first reasoning row,
which displayed later thinking before a tool call it actually followed.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 17:52:02 -07:00
Saoud Rizwan 0a9f45a9c6 fix(ui): stack code block lines when streamdown lineNumbers is off (#13412)
streamdown renders each Shiki token line as a bare inline span with no
newline text between non-empty lines, and only applies its block line
class when lineNumbers is on. With lineNumbers off (the desktop app's
config) every multi-line fenced block collapsed into one run-on line.
Make the direct line spans under code-block-body display: block in the
shared markdown.css; empty lines keep their height via their lone "\n"
child under white-space: pre.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 17:45:41 -07:00
Saoud Rizwan 4a63821d57 fix(desktop): treat ClinePass as OAuth-managed in the chat credential gate (#13404)
* fix(desktop): treat ClinePass as OAuth-managed in chat credential gate

ClinePass shares the Cline account OAuth credentials (its auth handler
stores under the "cline" provider), so the webview never sees a plain
API key for it. The chat pre-flight check only exempted cline/oca/
openai-codex, so switching to ClinePass while signed in via OAuth
blocked with "Missing API key" even though the sidecar resolves the
stored access token fine (which is why the CLI worked).

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

* style: format helpers.test.ts with biome

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 16:50:39 -07:00
Bee ff14ab601f feat: allow agents to create scheduled tasks (#13331)
* feat(core, desktop): add durable todo agenda

* fix(desktop): secure todo approvals and track tool usage

* fix(desktop): clean up failed approval delivery

* fix(desktop): authenticate approval connections

* fix(desktop): cancel approvals on broadcast failure

* fix(desktop): authenticate development approvals

* fix(desktop): harden development approvals

* test(core): make task paths cross-platform

* fix(desktop): serialize approval readiness

* refactor(core): unify todo and schedule tools

* feat(core): distinguish user todos from agent suggestions

* fix(core): hide tasks tool in yolo mode

* fix(core): enforce schedule workspace scope

* fix(core): bind schedule scope to hub connection

* fix(core): establish task scope at hub startup

* fix(core): scope task automation by workspace

* test(core): normalize workspace path expectations

* test(core): serialize Windows CI workers

* fix(core): reject unregistered schedule authority

* fix(desktop): guard task execution commands

* fix(core): avoid polynomial regex in mention parsing

* fix(core): address schedule tool review feedback

* fix(core): bind websocket clients to hub workspace

* fix(core): flatten tasks tool input schema

* fix(core): authorize multi-workspace hub clients

* test(core): type hub transport authority mock

* fix(cli): register a workspace client for remote schedule commands (#13398)

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-19 16:45:19 -07:00
Mikołaj Kondratek a8841bf96c fix(llms): surface provider-executed tool activity as observational events (#13300)
* fix(llms): surface provider-executed tool activity as observational events

Provider-executed tool parts (e.g. every tool the Claude Code CLI runs
inside its own session) were dropped by the model-tool guard added for
web search: only declared model tools were re-emitted, everything else
hit continue with nothing yielded. Those sessions modified the workspace
with no tool activity in runtime events, transcripts, or the UI.

Route all providerExecuted parts onto the observational path instead:
emit execution-tagged tool-call-delta and tool-result events, matched by
tool-call ID for providers that omit the flag on the result half. They
stay out of AgentRuntime's execution/approval loop, and the runtime
already persists them as modelToolActivities and projects them for
display.

The AgentModelEvent tool-result variant widens toolName from
ModelToolName to string to carry the provider's own tool names.

* fix(agents): keep turns that are only provider-executed tool activity

A turn consisting solely of observational tool activity has an empty
assistant content array - the activity lives in message metadata, since
projecting it into content would replay tool_use blocks the model never
gets results for. The empty-content guard threw on such turns, erroring
the run and losing the activity from the transcript. Count model-tool
activity as content for the emptiness check (error finishes still
throw); replay stays safe through the codec's empty-content placeholder.
Also drop the trailing text delta from one gateway test so the tool-only
stream shape stays covered end to end.
2026-08-20 00:46:09 +02:00
Saoud Rizwan 878faf0a95 Rename desktop app from "Cline Code" to "Cline" (#13401)
* Rename desktop app from Cline Code to Cline

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

* Format touched Rust test assertions

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 15:29:00 -07:00
Saoud Rizwan 36397f47eb ci: tidy workflow cache config and job permissions (#13403)
Publish workflows now always do clean npm installs (no dependency
cache in their test gates), the e2e workflow's cache keys are
exact-match only, and the e2e job drops an id-token permission it
never used.
2026-08-19 14:31:41 -07:00
Saoud Rizwan 3f0c5cdc92 ci: stop over-long changelogs from silently dropping release Slack posts (#12955)
Slack section blocks reject text longer than 3000 characters. The Slack
action logs that rejection as ##[error] but does not fail the step, so an
over-long changelog drops the release announcement while the run stays
green — cline@3.0.50 (3272 chars) published to npm, tagged, and cut a
GitHub release with no Slack post and nothing red to notice.

Every publish workflow pasted the changelog section verbatim into one
section block, so all six were exposed; the SDK, desktop, and extension
sections were only 150-350 chars under the ceiling.

Add a slack_content output alongside content: unchanged when the section
fits, otherwise trimmed on a line boundary with a link to the full
release notes. Only the Slack payload uses it — GitHub release bodies and
the desktop updater manifest still get the whole section.
2026-08-19 13:34:40 -07:00
Saoud Rizwan d9bb22883d fix(shared): run PowerShell commands with fail-fast error semantics (#13358)
* fix(shared): run PowerShell commands with fail-fast error semantics

The run_commands PowerShell wrapper never set $ErrorActionPreference, so
the default 'Continue' applied: a pipeline erroring per item (e.g. a
malformed Where-Object over Get-ChildItem -Recurse) emitted one error
record per enumerated file - tens of thousands of stderr records on
large trees, looking like a hang - and could still resolve as SUCCESS
with exit 0.

Prepend $ErrorActionPreference='Stop'; to the script content executed
by the ScriptBlock so the first error terminates the command with a
non-zero exit and a single error message. Concatenated on the same line
as the user command so error line numbers stay unshifted.

Fixes #13285

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

* fix(shared): set the fail-fast preference in the bootstrap scope

Setting $ErrorActionPreference='Stop' by string-prepending it into the
scriptblock source displaced a leading param(...) from its mandatory
first-statement position, so scripts beginning with a param block failed
with CommandNotFoundException. Preference variables are dynamically
scoped, so setting Stop in the -Command bootstrap gives the invoked
scriptblock identical fail-fast semantics while keeping the user script
byte-identical (param works, error positions unshifted) and drops the
doubled-quote escaping.

* docs(shared): document the fail-fast tradeoffs in the PowerShell wrapper

Stop promotes every non-terminating error, not only per-item pipeline
floods: partial-result commands (recursive listings over access-denied
junctions) now stop at their first error, and Windows PowerShell 5.1
turns in-script stderr redirection of succeeding native commands fatal.
State this in the wrapper comment as a deliberate tradeoff, with the
GitHub Actions precedent and the per-command opt-outs.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
Co-authored-by: Mikołaj Kondratek <19799111+mkondratek@users.noreply.github.com>
2026-08-19 12:11:41 -07:00
Ara 398c1a1b8f fix(llms): display billed gateway cost (#13385) 2026-08-19 21:03:32 +02:00
Renee Huang 8a64372b54 docs: show DeepSeek V4 peak and off-peak pricing (#13312)
* docs: update DeepSeek V4 average pricing

* docs: show DeepSeek peak and off-peak pricing

* docs: add GLM-5.3 reference pricing (same as GLM-5.2)

* docs: add GLM-5.3 to ClinePass models table
2026-08-19 11:14:22 -07:00
Bee dfa34ecea8 fix(desktop): strip user_input envelope when copying a user message (#13369)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Bee <abeatrix@users.noreply.github.com>
2026-08-19 01:11:42 -07:00
Bee 98d3e52a02 fix(clients): filter non-chat models from chat pickers (#13317)
* fix(clients): filter non-chat models from chat pickers

* fix(clients): align chat model eligibility
2026-08-18 23:42:08 -07:00
Saoud Rizwan f80e6a5df8 chore(desktop): release v0.0.14 2026-08-18 23:02:41 -07:00
Saoud Rizwan 9cf60cd43a fix(desktop): finalize queued turns on chat_done with canonical history reconcile (#13330)
Turns that settle through the event stream (queued prompts, including the
first prompt of a fresh session) resolve their send() RPC early, so nothing
cleared the streaming shimmer or reconciled live-streamed content against
the persisted transcript at turn end. A turn whose deltas were incomplete
stayed visually streaming forever and only healed when a later non-queued
send rehydrated history.

chat_done (and chat_session_ended / the queue-drain double check) now clears
the active assistant streaming id and schedules a short-delayed
read_session_messages + applyCanonicalHistory, guarded by turn epoch,
session id, and in-flight send submissions so it never clobbers a newer
turn or duplicates the blocking send path's own finalization.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-18 22:44:55 -07:00
Bee 2fd8d0383a fix(desktop): align system prompt with session mode (#13361)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-18 22:40:41 -07:00
Saoud Rizwan 411282296c Use fixed selection chevron in account dialog to match other dialogs (#13364)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-18 22:23:54 -07:00
Saoud Rizwan 3705aec28f fix: skill slash commands load via the skills tool instead of expanding into the user message (#13327)
* fix(desktop): show typed slash command instead of expanded skill markdown

The sidecar expands /skill and /workflow tokens into their instructions
before dispatching, so the runtime's persisted transcript only contains the
expanded text. After a turn (and when reopening a session) the webview
re-hydrates from that history and rendered the whole SKILL.md body as the
user's message; queue events echoing the expanded prompt could also add a
second user bubble, and fresh sessions were titled with the markdown's first
line. The CLI never shows this because its TUI keeps the typed text in its
own transcript and only sends the expanded prompt to the model.

Mirror that separation inside the desktop sidecar's display boundaries:

- history projection (readSessionMessages) inverts user text that starts
  with a configured command's instructions back to '/name remainder',
  which also repairs sessions recorded before this fix
- queue snapshots and chat_queued_prompt_start events echo the typed
  prompt recorded at expansion time, so the webview's optimistic-bubble
  re-key matches again
- an untitled session sent an expanded prompt gets titled from the typed
  command instead of the instructions' first line

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

* fix(desktop): don't overwrite a mid-turn rename with the typed-command title

The untitled check ran before dispatch, so renaming a fresh slash-command
session while its first turn was running got clobbered by the post-turn
typed-command title. Re-check at write time and only replace a missing title
or the one the runtime auto-derived from the expanded prompt.

Also documents the inherent prefix-inversion ambiguity flagged in review:
text hand-typed with a command's exact instructions persists byte-identically
to that command's expansion, so stored history alone cannot distinguish them.

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

* fix(desktop): stop expanding skill commands; let the skills tool load them

Pasting the skill body into the prompt is why the transcript could ever show
it: the desktop webview re-hydrates from the runtime's persisted history, so
whatever the sidecar splices into the user message renders as if the user
typed it. The runtime already registers the skills tool, whose description
requires the model to invoke it whenever the user references a slash command
— so send the typed /skill text through and let the tool deliver the
instructions as a tool result (previously they arrived twice: pasted and via
the tool). The persisted user message, session title, and queue entries are
then simply the typed command, which deletes the typed-prompt registry, the
queue event/snapshot rewriting, and the title machinery from the previous
approach.

Workflows are not served by the skills tool and keep textual expansion, so
the read-time display inverter stays: it collapses expanded workflow prompts
— and skill prompts persisted before this change — back to the typed
/command in the history projection.

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

* feat(core): option to keep skill slash commands typed for the skills tool

resolveRuntimeSlashCommandFromWatcher (and the hub snapshot proxy) accept
expandSkillCommands: hosts whose sessions register the skills tool pass
false so the typed /skill goes through and the model loads the instructions
as a tool result, keeping the persisted transcript as what the user typed.
Workflows always expand — the tool does not serve them. isSkillsToolAvailable
exposes the catalog check hosts use to decide (yolo preset and the skills
tool toggle leave textual expansion as the only delivery path).

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

* fix(cli): skill slash commands load via the skills tool instead of expanding

The TUI user-command wrap and buildUserInputMessage now keep a typed /skill
as-is when the session's mode/toggles register the skills tool, matching the
desktop app; workflows keep expanding, and yolo (zen) keeps expanding skills
because its preset has no skills tool. This also fixes CLI resume/history
surfaces showing the skill body: the persisted user message is now the typed
command.

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

* fix(vscode): keep configured skill slash commands typed for the skills tool

expandSlashCommands no longer splices a configured skill's instructions into
the model text; the SDK session's skills tool delivers them as a tool result
(previously they arrived twice). Builtin pseudo-skills like /deep-planning
are not served by that tool and keep expanding, as do workflows.

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

* refactor(desktop): use the shared skill-expansion option in the sidecar

Replaces the sidecar's workflow-detection dance with core's
expandSkillCommands option and gates on isSkillsToolAvailable, restoring
textual expansion where the tool is missing (yolo mode or the skills tool
toggle) — a gap in the previous desktop-only change.

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

* refactor(desktop): drop the display inverter for expanded transcripts

Accepted trade-off to keep the change minimal: sessions recorded before
skills switched to the skills tool, workflow sends (deprecated), and
yolo-mode skill sends persist expanded instructions and now render that text
as-is instead of being collapsed back to the typed /command at projection
time.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-18 22:22:36 -07:00
Saoud Rizwan 67e5115b85 fix(cli): make TUI dialog colors follow theme changes live (#13355)
* fix(cli): make TUI dialog colors follow theme changes live

Dialog content previously read the static palette constant, so open
dialogs (including the theme picker itself) kept the default dark-blue
accents while scrolling through theme previews. Add getDialogPalette /
useDialogPalette, which resolve dialog colors from the active theme's
dialog accents and re-render on every theme change, and migrate all
dialog-rendered components to it.

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

* feat(cli): derive dialog panel background from the active theme

Dark themes now lift their own background one OKLAB step for the dialog
surface, so panels keep the theme's hue instead of the library's fixed
#262626. DialogThemeSync pushes the surface into the dialog container
for new dialogs and repaints open panels, so the surface also follows
live theme previews. Light themes keep the neutral dark panel to match
the dark accent fallback and the light-on-dark dialog text.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-18 22:16:26 -07:00
Bee 61b95a62ee feat(desktop): stream run command output (#13179)
* feat(desktop): stream run command output

* fix(sdk): clean up detached command logs

* fix(sdk): reap detached logs after hub restarts

* fix(sdk): preserve live detached command logs

* fix(desktop): harden live command progress

* fix(sdk): recover detached logs for local hosts

* fix(desktop): reconcile command output tool rows

* fix(sdk): retain logs for surviving commands

* fix(core): prevent PID reuse from retaining detached logs

* fix(core): preserve detached logs on probe failures

* fix(core): retain detached logs during probe outages

* fix(desktop): resolve leftover merge conflict in messages projection test

Combine both sides of the assertion: main's incremented per-block
createdAt projection and this branch's toolCallId/hookEventName meta.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-18 21:10:18 -07:00
Saoud Rizwan 6cc0328424 docs: add GLM-5.3 to ClinePass models and reference pricing (#13357)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-18 20:25:00 -07:00
Saoud Rizwan 90b32cb41d fix: normalize Gemini custom base URLs for legacy host-root values (#13329)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-18 20:21:51 -07:00
Saoud Rizwan 3c433d5a90 fix: run_commands object form without args routes through the shell instead of failing with ENOENT (#13336)
* fix: run_commands object form without args routes through the shell

The structured { command, args? } form of run_commands was always spawned
directly with shell: false. When a model emitted a full command line in
command with no args (e.g. { command: "echo hello" }), spawn failed with
ENOENT for any command containing a space, breaking command execution for
the whole session.

Direct exec now only applies when a non-empty args list is provided; the
object form without args is routed through getShellInvocation like the
string form. Schema descriptions are tightened so models put arguments in
args instead of embedding them in command.

Fixes #13279

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

* chore: trim structured-command schema descriptions

The union schema is only used for lenient validation of input the model
already sent; its descriptions never reach a model prompt. Keep them
short instead of restating executor behavior.

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

* chore: simplify direct-exec comment in shell executor

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

* revert: keep original structured-command schema description

The description never reaches a model prompt and the executor now handles
both shapes, so the wording change was cosmetic noise.

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

* fix: gate direct exec on args key presence, not array length

Review feedback: an explicit empty args array is intentionally structured
input and stays direct exec; only an object with no args key is treated
as a full shell command line. Matches the key-presence rule already used
by the VS Code host's formatCommandForTerminal. Also replaces the
empty-args shell test (which was PowerShell-incompatible) with a test
pinning the direct-exec contract.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-18 19:52:52 -07:00
Saoud Rizwan 76ac1c7f55 ci(ui-publish): build @cline/shared before ui typecheck (#13354)
@cline/ui's generated-media imports @cline/shared/browser, which resolves to
shared's dist output. The build-shared step sat after typecheck/test/build,
so the first ui-publish dispatch since #13025 failed at Typecheck UI with
TS2307. Move the step to right after install.
2026-08-18 18:01:10 -07:00
Saoud Rizwan be56c505e4 feat(ui): share the markdown pipeline, chat polish, and ThinkingBlock across products (#13323)
* feat(ui): share the markdown pipeline, chat polish CSS, and ThinkingBlock

The desktop app and the cloud dashboard both consume @cline/ui yet rendered
assistant output differently, because Markdown policy and the thinking-trace
row lived app-side. This moves the shareable parts into the package:

- components/markdown (new export): the lazy Shiki code highlighter (GitHub
  light/dark, pinned language set) and agentMarkdownControls — the standard
  Streamdown configuration. streamdown/shiki/@shikijs/* become optional peer
  dependencies, mirroring @pierre/diffs.
- components/markdown.css: the desktop's chat polish moves in — chat-scale
  headings, outside list markers, single quiet code blocks with a
  hover-revealed copy control, table cards. Kept unlayered so it beats
  Streamdown's layered Tailwind utilities without !important.
- ThinkingBlock + formatThoughtLabel in agent-chat: the standard thinking
  row (brain icon, Thinking/Thought-for-Ns label, streaming shimmer, rail
  presentation, capped scrollable body). The shimmer and the
  reasoning-hover-suppression rule move into agent-chat.css; triggers gain
  the color transition the desktop applied locally.

Version bumps to 0.2.0-next.5 for the dashboard to pick up.

* refactor(desktop): consume shared markdown and thinking primitives from @cline/ui

The local Shiki highlighter, Streamdown controls, chat markdown polish CSS,
streaming-title shimmer, and reasoning hover-suppression rule are deleted in
favor of the @cline/ui versions (the highlighter test moves to the package's
suite). ReasoningBlock becomes a thin wrapper that hands MemoizedMarkdown to
the shared ThinkingBlock, and formatThoughtLabel re-exports from the package
so grouping code and tests keep their import path.

globals.css now imports @cline/ui/components/markdown.css (unlayered, so the
polish keeps beating Streamdown's layered utilities); the app keeps only what
is genuinely app-specific: link/image policy in markdown.tsx, selectability
rules, accent palettes, and the view-enter transition.

* style(ui/desktop): make thinking-trace prose legible

Thinking body text rendered too faint: plain muted-foreground plus the
desktop's font-thin weight. The shared thinking content now leans 75% of the
way back toward the body text color (still slightly de-emphasized), and the
desktop drops the thin font weight.
2026-08-18 17:52:17 -07:00
Saoud Rizwan 3f9c9c3f33 feat(ui/desktop): collapse finished runs into a work summary and remove hover-state dead space (#13315)
* feat(ui): add WorkActivity collapsed-run summary and float message actions as a pill

WorkActivity/WorkActivityTrigger/WorkActivityContent fold a finished agent
run's working rows (tool calls, thinking traces, narration) behind a single
"Worked for 4m 12s · 14 tool calls" disclosure built on the shared animated
disclosure primitives, with formatWorkActivityLabel/formatWorkDuration
exported for consumers.

Message hover actions no longer rely on the transcript reserving blank space
below each message: the action row is now a self-backed pill (border,
blurred background, shadow) that floats over whatever follows, so
conversations can pack rows tightly without hover chrome colliding with the
next message.

* feat(desktop): collapse finished runs into a work summary and tighten chat spacing

collapseCompletedWork post-processes the grouped transcript: once a run ends
on assistant text with no further tool calls, its working rows fold into one
expandable WorkActivity row while the final answer stays visible. Runs are
delimited by user messages; the trailing run only collapses when the session
has stopped running and actually produced an answer, so live streams and
cancelled/failed tails keep their rows. Assistant messages carrying images
or media are treated as deliverables and never collapse.

The conversation list gap drops from gap-8 to gap-4 now that hover actions
are self-backed pills that need no reserved space, and user messages add
their own top margin so turn boundaries stay visually distinct.

* refactor(ui/desktop): work summary label wording, flat expansion, stable in-run rhythm

Feedback round on #13315:

- Label reads "Worked for 4m 12s and made 14 tool calls" instead of joining
  with a dot; without a duration it falls back to "Made N tool calls".
- Expanded work rows render at transcript level — no rail or extra indent —
  since tool rows and thinking traces already carry their own nesting when
  expanded. The work content keeps the tight working-row rhythm.
- Live working rows (thinking traces + tool calls) now group into a 'run'
  render item with the same tight 0.25rem rhythm, so there is no oversized
  gap under a "Thought for Ns" row and every row keeps its exact position
  when the finished run folds into the work summary. A trailing
  answer-in-progress stays outside the group at transcript level, and pure
  prose spans keep normal spacing.
- The transient "Thinking..." indicator moves inside the transcript column
  and mirrors a trigger row's geometry, so the first real row replaces it in
  place with no jump.

* style(ui/desktop): hover-pill metrics, right-pointing work chevron, scroll and spacing fixes

Another feedback round on #13315:

- Hover action pill: +2px internal padding, a trailing inset after the
  timestamp (it sat flush against the pill border), and more clearance
  between the message content and the pill (2px -> 6px; the hover bridge
  grows to match).
- The work summary chevron points right while collapsed and continues
  counterclockwise to point up when expanded.
- Conversation bottom padding drops pb-20 -> pb-8: the composer sits below
  the scroller, so the padding only needs to clear a pinned action pill.
- Sending a message scrolls back to the bottom even if the reader had
  scrolled up (new AutoScrollOnSend on the user-message count, which ignores
  optimistic-bubble re-keying; @cline/ui now exports useConversation for
  this).
- An assistant answer directly under its run's working rows pulls itself
  0.5rem closer than the full transcript gap.

* style(desktop): leave a visible gap between a pinned action pill and the composer

pb-8 exactly matched the pill's ~40px footprint, so the last row's hover
actions sat flush against the composer top; pb-12 restores ~8px of daylight.

* style(desktop): widen the gap between the pinned action pill and the composer to ~24px

pb-12 left only ~8px of daylight under the pill; pb-16 reads comfortable
without reverting to pb-20's dead space.

* fix(desktop): keep the thinking indicator at the working-row offset mid-run

The indicator matched a trigger row's geometry but sat a full transcript gap
(1rem) below the last working row, while the tool/thinking row replacing it
joins the tight run group at 0.25rem — a visible upward jump. When the last
transcript item is working rows (or streamed assistant output), the
indicator now pulls up to the same tight offset; only at the start of a run,
under the user message, does it keep the normal gap.

* style(ui): calm the hover actions surface per team feedback

Borderless rectangle instead of the bordered pill: radius drops to
var(--radius), the side padding goes entirely (the icon buttons carry their
own hit areas), and the vertical padding halves. Blurred background and
shadow stay so it remains legible over following content.

* feat(ui/desktop): full-band hover reveal and iOS-style disclosure easing

The hover actions only appeared while the pointer was inside the message
box itself. The invisible bridge under each message now spans the full
height of the band the floating actions occupy (full row width), so
hovering anywhere in that strip reveals them. Sibling row types
(.cline-chat-tool, .cline-chat-work, and the desktop's run/tool groups)
become position: relative so they paint above the bridge — their own
content keeps its hover and clicks, and the bridge only wins in the band's
genuinely empty space.

All expandable rows (work summary, tool panels, thinking) open and close on
a 240ms symmetric ease-in-out cubic-bezier instead of the 60ms snap, with
chevron rotation on the same curve. Reduced-motion still disables both.

* revert(ui/desktop): drop the full-band hover reveal; quicken disclosure easing to 180ms

The full-band hover bridge (and the position: relative changes that made it
safe) is reverted per feedback — back to the narrow bridge that only spans
the gap under the message. The iOS-style ease-in-out on disclosures stays
but speeds up from 240ms to 180ms.

* fix(ui): recover live tool diffs that mount as a blank pierre skeleton

Live-streamed edit rows could show an empty diff for the whole run, with the
diff only appearing after the collapsed work row was expanded (fresh mount).
Root cause, confirmed by driving a live session and inspecting the element:
React StrictMode double-invokes @pierre/diffs' ref callback; the first
instance's async highlight work aborts on its immediate cleanup, and the
second instance adopts the abandoned half-rendered shadow tree as if it were
complete prerendered output — zero height, no code, no theme stylesheet,
permanently. A rendered diff always carries style[data-theme-css] in its
shadow root, so ToolFileDiff now checks for it shortly after mount and
remounts FileDiff (bounded attempts) when missing; the fresh host element
takes the normal render path and recovers within ~400ms. Verified live: the
diff now renders during the run.

* fix(desktop): keep interrupted runs expanded even with partial trailing text

The trailing-run collapse gated on 'ended with assistant text', which
misread a Stop that landed mid-answer as a finished run and folded the tool
calls the user wants to inspect. The gate is now the terminal status itself:
only completed (or restored-idle) sessions collapse the trailing run;
cancelled/failed/error tails keep their rows regardless of partial text.
(Greptile P1 on #13315 — matches the PR's stated rule.)
2026-08-18 17:46:57 -07:00
Saoud Rizwan cf07572a07 feat(desktop): show provider web-search support under the settings toggle (#13328)
* feat(desktop): show provider web-search support under the settings toggle

The global Web search toggle silently does nothing unless the session's
provider offers native web search, which made the setting read as if it
worked with any provider. The desktop General settings row now explains
that only providers with built-in web search honor it, and shows a live
status line: which connected providers are ready to use it (no extra
setup needed), or an amber warning with a link to the Models section
when none of them support it.

Support is resolved in the webview via a new providerOffersModelTool
helper in @cline/llms (browser export), sharing the same builtin-manifest
source of truth as the runtime's supportsModelTool attachment check.

* fix(desktop): address review — refetch web-search status on catalog invalidation, clarify per-model support

Greptile P2: the one-time catalog fetch could race an in-flight provider
save and show stale status; the row now refetches when the provider
catalog cache is invalidated (fired after saves complete).

Greptile P1: the ready line implied every model on the provider works;
Vertex excludes Claude routes, so the copy now scopes the promise to
models that support it.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-18 17:20:55 -07:00
Mikołaj Kondratek a5ac26f279 fix(vscode): clear task-scoped settings overlay when task view is cleared or switched (#13310)
* fix(vscode): clear task-scoped settings overlay when task view is cleared or switched

Toggling an auto-approve setting while a task is open writes
autoApprovalSettings into the StateManager's task-settings overlay
(updateAutoApprovalSettings -> setTaskSettings). The SDK controller never
cleared that overlay on clearTask/showTaskWithId (the legacy controller
did), so after New Task the stale overlay kept shadowing global settings
in getGlobalSettingsKey(): toggle RPCs were accepted into global state,
but every posted state still carried the overlay's old version, which the
webview rejects as not newer - the auto-approve checkboxes froze forever.

Restore legacy parity in SdkTaskControlCoordinator: drop the overlay
(persisting pending writes first) in clearTask() and before installing a
different task's proxy in showTaskWithId().

Fixes #13260

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

* changeset

* test(vscode): add end-to-end regression test for auto-approve freeze after New Task

Wires the real StateManager, the real updateAutoApprovalSettings handler,
and the real SdkTaskControlCoordinator.clearTask() together with the
webview's version gate modeled on ExtensionStateContext, pinning the
end-to-end invariant behind #13260: checkbox toggles must keep reaching
the webview after a mid-task toggle followed by New Task. Verified the
test fails when the clearTaskSettings() call is removed from clearTask().

* fix implicit any in regression test

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-18 16:54:59 -07:00
Bee 508a5322af feat(desktop): native notifications (#13166)
* feat(desktop): native notifications

* macos target

* fix(desktop): isolate macOS dev app identity

* fix(desktop): address notification review feedback

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-18 16:46:25 -07:00
Max 38f8260bc3 fix(vscode): SDK remote-config parity — refresh coordination, session gating, and fail-closed opt-out (#13226) 2026-08-18 16:17:24 -07:00
Saoud Rizwan eeaed357ef fix(ci): lock the legacy publish workflow to the legacy-extension branch (#13350)
The branch dispatch input was a free-form string with no validation. Both
jobs checked it out and ran full npm lifecycle scripts from it: the publish
job next to VSCE_PAT/OVSX_PAT (and npm run publish:marketplace executes a
script from that same ref with the PATs in env), and the test job with NO
environment approval at all while inheriting the workflow-level
contents/packages/checks/pull-requests write grants. A dispatch pointing at
e.g. refs/pull/N/head would run outside-contributor code with the
marketplace keys behind one approval, or with a repo-write token behind
none.

Remove the input and hardcode the protected legacy-extension branch, drop
the workflow-level permissions to contents: read, and elevate only the
publish job to contents: write (tag push + GitHub release). The branch
input's default was legacy-extension, so normal publishes are unchanged.
publish-extension skill dispatch command updated to match.
2026-08-18 16:12:04 -07:00
Saoud Rizwan bca9b64206 fix(ci): always build the legacy bundle from the legacy-extension branch (#13349)
The combined-VSIX workflow took legacy-ref as a free-form dispatch input
with no publish-time validation (next-ref has one: publish requires main).
Any typed ref — a PR merge ref, an unprotected branch — would be built
into the published VSIX by the environment-less build job, and the publish
environment approver only ever sees an opaque prebuilt artifact, so the
approval protected the marketplace PAT but not the shipped bytes.

Remove the input entirely and hardcode the protected legacy-extension
branch, which makes that branch's protection rules load-bearing for
releases. The tested-sha pinning between test-legacy and build is
unchanged. publish-extension skill dispatch command updated to match.
2026-08-18 16:00:35 -07:00
Mikołaj Kondratek 8a038022a4 fix(vscode): point provider signup URLs at their API key pages (#13337)
* fix(vscode): point Mistral signup URL at the general API keys console

The Mistral provider's signup link led to the Codestral console, which
issues Codestral-scoped keys that fail with 401 on api.mistral.ai — the
endpoint the provider actually calls. Point it at the general API keys
page instead.

Fixes #13288

* fix(vscode): deep-link DeepSeek and Fireworks signup URLs to their API key pages

Both pointed at marketing homepages; link straight to the key-creation
pages instead, matching the rest of the registry and the desktop app's
provider-key-urls map.
2026-08-18 16:53:54 +02:00
JasmineLCY c2e293e293 fix(vscode): preserve LiteLLM input token limits (#13293)
* fix(vscode): preserve LiteLLM input token limits

* fix(vscode): prefer live LiteLLM model metadata

* fix(vscode): generalize private catalog metadata

* test(vscode): preserve llms exports in vscode lm mock
2026-08-18 12:00:33 +02:00
Saoud Rizwan b9efa96826 fix(vscode): continue the surviving session on resume instead of rebuilding with the original task text (#13175)
* fix(vscode): stop resubmitting the original task text on bare resume (#12975)

A bare Resume after Stop rebuilt the session from task history and
injected historyItem.task into the resumption prompt as 'New
instructions from the user'. The model treated the already-completed
original request as fresh instructions and re-executed it (e.g. re-ran
all terminal commands after stopping a queued follow-up turn).

The preserved conversation history is the source of truth on resume, so
the fallback prompt now just asks the model to reassess the history and
continue, matching the legacy resume prompt which also never resent the
original task. User-typed text still takes precedence when provided.

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

* fix(vscode): continue the surviving idle session on follow-ups instead of rebuilding

Stopping a turn keeps the session alive, but every idle follow-up (bare
Resume after Stop, and typed follow-ups after a completed turn) tore
that session down and rebuilt it from persisted task history before
sending. Continue the matching idle session in place instead, the same
way the CLI reuses the live session after an abort. Rebuilding from
history now only happens when no live session matches the displayed
task (task opened from history, extension host reload).

A bare resume still needs a prompt to start a turn, so it sends the
neutral [TASK RESUMPTION] prompt (shared with the rebuild fallback and
hidden from the transcript); user-typed content is echoed and sent
as-is. If the send lands while the abort is still settling, the runtime
auto-queues it and drains once the abort completes.

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

* refactor(vscode): consolidate follow-up send paths in SdkFollowupCoordinator

Now that idle follow-ups continue the live session in place, the
two-mode sendToActiveSession helper was redundant: its non-queued branch
duplicated continueIdleSession minus the bare-resume prompt. Split it
into a single-purpose queueToActiveSession and fold the idle no-task
send into continueIdleSession, flattening askResponse's decision tree
to: queue onto a running turn, continue a matching live idle session,
rebuild from history, or abandon.

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

* refactor(vscode): reuse the existing neutral resumption prompt for bare resumes

Drop the newly invented long resumption wording in favor of the phrase
that already existed as the no-history fallback and that the transcript
hiding logic and test fixtures recognize: '[TASK RESUMPTION] Please
continue where you left off.' The net change to resumeSessionFromTask
against main is now just deleting the branch that resubmitted
historyItem.task as new instructions.

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

* fix(vscode): stop resubmitting the original task text on bare resume (#12975)

A bare Resume after Stop rebuilds the session from task history and
injected historyItem.task into the resumption prompt as 'New
instructions from the user'. The model treated the already-completed
original request as fresh instructions and re-executed it (e.g. re-ran
all terminal commands after stopping a queued follow-up turn).

Bare resumes now always use the neutral prompt that already existed as
the no-history fallback; user-typed text still takes precedence. This
matches the legacy resume prompt (responses.taskResumption), which only
ever included user-supplied text as new instructions.

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

* fix(vscode): hide synthetic prompts from the queued-prompt echo

A send that races a settling abort is auto-queued by the runtime, so a
bare Resume can reach the pending_prompt_submitted echo carrying the
synthetic [TASK RESUMPTION] prompt. Echoing it leaked model-facing text
as a visible user bubble and shifted the visible-user-message ordinals
that edit/regenerate mapping relies on. Filter synthetic prompts with
isSyntheticUserPrompt, keeping user attachments visible (matching
isSyntheticSdkUserMessage semantics).

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-17 19:10:59 -07:00
Saoud Rizwan d4b415f8ab fix(desktop): trim the persisted transcript on checkpoint restore (#13259)
A restore that reuses the source session id rolled the workspace back but
left the persisted transcript describing the discarded turns, so the chat
kept showing turns whose file changes had just been reverted.

Before #13075 the restore reply carried the trimmed messages and the
webview rendered them directly. Now the webview always re-reads through
read_session_messages, which prefers the persisted file over the live
session, so the trimmed history the sidecar puts on the live session is
never read. Persist it as well.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-17 18:03:52 -07:00
Bee 6b3f034bce fix(desktop): align usage table columns CLINE-2996 (#13325)
* fix(desktop): align usage table columns

* fix(desktop): show usage link for empty history
2026-08-17 17:30:08 -07:00
Saoud Rizwan 05a6974ef8 feat(desktop): surface beta channel identity in-app (#13322)
Beta builds (prerelease versions from desktop-experimental, shipped as
'Cline Code Beta') now identify themselves everywhere users look: a Beta
pill in the sidebar footer, the product name in the sidebar hover card,
an About row in Settings > General with version + channel, the runtime
window title, and the tray menu/tooltip (via package_info, which carries
the overlay's productName).

Channel detection is a pure version-string check (-beta suffix) in the
new webview/lib/app-channel.ts — the version is baked into package.json
at build time and reported by the sidecar's get_process_context, so it
works in both the Tauri shell and web dev mode with no new plumbing.
Stable builds render no channel UI at all.
2026-08-17 17:24:22 -07:00
Saoud Rizwan 5ad2dd5fc8 feat(desktop): beta release channel from desktop-experimental branch (#13321)
* feat(desktop): add beta release channel from desktop-experimental branch

Adds a 'channel' input (stable|beta) to desktop-publish.yml. Beta releases
are tagged desktop-vX.Y.Z-beta.N on the desktop-experimental branch, built
with the tauri.beta.conf.json overlay (Cline Code Beta / bot.cline.app.beta,
side-by-side install with stable), published as prerelease GitHub releases,
and served by a separate rolling desktop-beta update feed. Both channels
dispatch from main so the PublishDesktop signing gates are unchanged.

Guards: stable channel now rejects prerelease tags (previously a beta tag
could clobber desktop-latest and auto-update every stable install onto it),
feed selection is fail-closed and cross-checked in the release job, and the
build asserts the compiled binary embeds exactly its own channel's feed URL.
Changelog extraction is exact-version now that stable and beta sections
interleave across branch merges.

Process doc in apps/examples/desktop-app/EXPERIMENTAL.md; publish-desktop
skill now asks stable-or-beta.

* docs(desktop): warn against renaming the desktop-latest feed

* docs(desktop): document the code-trust model for publish approvals

The beta dispatch-from-main invariant protects the workflow definition, not
the checked-out tag's build scripts, which run with signing secrets in scope
for stable and beta alike. Make explicit that the PublishDesktop reviewer
approval is the trust gate for that code, and that desktop-experimental
therefore needs main-grade merge controls.
2026-08-17 17:24:01 -07:00
Bee 87be867599 fix(desktop): make routine selectors clickable (#13324) 2026-08-17 17:05:33 -07:00
Bee 26cb0ec9eb test(llms): use Google language operation (#13318) 2026-08-17 15:35:56 -07:00
Haley Park eed78103a4 feat(ui): AskQuestion component redesign (#13236)
* feat(ui): support explicit follow-up question submission

* Fix Enter handling for question options

* Strengthen question keyboard regression test
2026-08-17 09:32:03 -07:00
Haley Park 456f86bb3a style(desktop): session hover cards styling (#13256)
* style(desktop): simplify session hover cards

* docs: add hover card screenshots

* chore: remove PR screenshot assets

* style(desktop): address hover card review
2026-08-17 09:31:56 -07:00
Haley Park e86d988234 feat(ui): add animated reasoning and tool disclosures (#13254)
* feat(ui): add animated disclosure presentation

* docs: add disclosure screenshots

* docs: remove PR screenshots

* fix(ui): support inert across React versions
2026-08-17 09:31:41 -07:00
Fnine59 041afb718b fix(vscode): restore Gemini custom base URL (#13247) 2026-08-17 13:16:43 +02:00
Bee 8bbdde2a5c feat(llms): add model-driven image generation (#13025)
* feat: add image generation support

* fix(llms): preserve mixed image model behavior

* fix(llms): validate generated image models

* fix(llms): preserve mixed image response streaming

* fix(llms): preserve runtime tool ownership

* fix(llms): address image generation review feedback

* fix(desktop): relay images for attached hub sessions

* chore(llms): regenerate provider and model catalog

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(vscode): preserve SDK model capabilities across the catalog boundary

The new modelSupportsToolCalling gate treats a populated capability list
without "tools" as authoritative. But the VS Code host round-trips model
metadata through the legacy ModelInfo shape, and toSdkModelInfo
reconstructed capability arrays from the legacy booleans alone — which
have no "tools" projection. Every model with any capability flag set
came back as "cannot call tools", so sessions registered zero tools and
the file-edit e2e failed on all platforms (the editor tool call resolved
to "Unknown tool" and the edit never reached disk).

Fix, following the modalities-passthrough pattern so stacked capability
PRs can reuse it:

- Preserve the SDK capability list verbatim on legacy ModelInfo at the
  catalog boundary (adaptSdkModelInfo); union user overrides into it
  without ever fabricating a list from overrides alone.
- Seed toSdkModelInfo from the preserved list, and when none survived,
  emit an explicit "tools" signal (honoring legacy supportsTools=false)
  so reconstructed arrays can never silently disable tool calling.
- Add a shared modelHasCapability(model, capability,
  {assumeWhenUnspecified}) helper: missing or empty capability lists
  carry no signal and each check declares its own default. Future
  capability gates should route through it instead of reading
  model.capabilities directly.

Verified: file-edit e2e (Single Root + Multi-Roots) passes locally;
shared/core/llms/model-catalog/session-factory suites and typechecks
pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(llms): refresh generated model catalog

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 12:58:53 -07:00
Bee da05eeb02d feat(desktop): add microphone transcription input (#13023)
* feat(desktop): add voice input

* fix(desktop): harden voice transcription input

* fix(llms): scope voice transcription models

* fix(desktop): guard batch voice transcripts

* refactor(voice): defer chat model filtering

* fix(desktop): invalidate stale streaming transcripts

* fix(desktop): preserve batch transcription lifecycle

* chore(llms): refresh voice model catalog

* Mic Icon

* Auto

* test(desktop): align speech input icon assertions
2026-08-14 10:57:29 -07:00
Saoud Rizwan 2be49cf91b chore(desktop): release v0.0.13 2026-08-14 10:18:33 -07:00
Haley Park b851cd86d1 docs(ui): expand agent component stories (#13235) 2026-08-14 09:49:22 -07:00
Saoud Rizwan 3e0aac53a2 chore(vscode): prepare 4.1.10 release 2026-08-14 01:44:30 -07:00
Saoud Rizwan ad442cbb6a chore(cli): release v3.0.55 2026-08-14 00:39:01 -07:00
Saoud Rizwan 225f65cc0e chore(sdk): release v0.0.75 2026-08-14 00:16:53 -07:00
Saoud Rizwan 8a619a9ea6 test(llms): decouple Vertex web-search coverage from the catalog default
The Vertex case asserted that a bare providerId resolves to a model
without web search, which only held because the generated catalog's
Vertex default happened to be a Claude route. models.dev has since moved
that default to gemini-3.7-flash, which does support native web search,
so the assertion failed on the next catalog regen while the behavior it
guarded was unchanged.

Drop the catalog-dependent case and cover the default-model fallback
against a synthetic manifest instead, where the excluded route is stated
by the test rather than inherited from upstream data.
2026-08-14 00:16:48 -07:00
Saoud Rizwan 2e46676952 style: apply biome formatting to files that drifted on main 2026-08-14 00:16:43 -07:00
Bee 102e08f5f4 fix(core): reclaim idle plugin sandbox processes (#13227)
* fix(core): reclaim idle plugin sandboxes

* fix(core): centralize sandbox idle shutdown

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-13 23:40:07 -07:00
Saoud Rizwan 942f76e8e7 feat: add web search settings toggle to VS Code extension and desktop app (#13245) 2026-08-13 23:18:18 -07:00
Saoud Rizwan 57eb181bab fix(cli): say nothing when the Hub is only finishing an update (#13249)
The outdated_hub notice reports a state the user cannot act on: this CLI
is already the newer build, the Hub is behind only because retiring it
would kill the sessions it is serving, and the swap happens on its own at
the next launch. A toast that interrupts to say "no action needed" is
still an interruption, and the desktop surface already concluded the same
thing by rendering nothing for this reason.

It also could not deliver the message it existed for. Toast caps at
maxWidth = Math.min(44, width - 4), and the 61-character string did not
wrap, so what actually rendered was "Update finishes the next time Cline"
- a sentence cut off before the reassuring half. Identical at 120 and 200
columns, so widening the terminal did not help.

The classification stays in core and still earns its keep at this call
site: outdated_hub is what stops the update-and-restart prompt from
firing at someone who has nothing to update. Only the rendering goes.
The build_mismatch direction, where the user does have something to do,
is untouched.
2026-08-13 23:01:34 -07:00
Saoud Rizwan 63e9c99031 fix(cli): stop streaming markdown from flashing raw text on every chunk (#13248)
Render assistant markdown with internalBlockMode="top-level" so each
top-level markdown block gets its own renderable. The default coalesced
mode merged the entire message into one block that was rebuilt and
re-highlighted on every streamed chunk, flashing settled headings and
links back to raw uncolored markdown (visible ###, unconcealed syntax)
until the async tree-sitter highlight landed, and re-wrapping rows so
the transcript jumped vertically.

Top-level blocks are reused by token identity, so settled content never
re-renders; only the trailing unstable block updates per chunk. Pass
tableOptions style=grid to keep the bordered table rendering coalesced
mode used by default.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-13 22:57:35 -07:00
Bee afab68f3a3 fix(core): defer replacing a Hub that is serving live sessions (#13231)
* fix(core): stop concurrent Hub installs from retiring each other

Two Cline installations on different builds would shut each other's Hub
daemon down in a loop, and every session died with an abnormal socket
close (code=1006) as its daemon was killed mid-handshake.

The retire decision was a one-sided predicate: each client independently
asked "may I reuse this Hub?", and two clients on differing builds both
answered no. #13177 added build-epoch ordering to break the tie, but left
every unordered case - missing epoch, missing build id - retiring as
before, so any pair involving a build from before epochs were embedded
still looped.

Derive the decision from a total order instead. compareHubBuilds orders
two builds by embedded epoch, then core release version, then build id,
and is antisymmetric by construction, so at most one side of a pair can
ever decide to retire. A Hub that is newer or cannot be ordered is
attached over the compatible wire protocol and left to the build-mismatch
watcher to prompt about. Genuine protocol incompatibility still replaces.

Identity is now read from the same fields on both sides. Filling in a
coreVersion locally that the wire record omits made a build's identity
depend on which role it was playing, and the two directions of a pair were
then decided by different tiers with both concluding they were newer -
a second, independent way to produce the loop.

Also:
- Scope the development Hub owner by build id, so differing dev builds run
  their own daemon side by side instead of contending for one record.
  Production keeps its singleton.
- Break the circuit after repeated retirements of the same URL, bounding
  any future ordering bug to a stale-build prompt rather than an
  unusable Hub.
- Report `cline doctor fix` honestly: separate processes that survived a
  kill from ones that appeared while the fix ran, name the live parent
  respawning a daemon, and mark a startup lock held by a running process
  as held rather than leaked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(cli): only blame a live parent for processes seen during doctor fix

The advice printed under "started during fix" asserted that every such
process was respawned by a live parent, and told the user to go stop it. A
process can also start on its own mid-repair - someone opening a new
session - and then the instruction points at an unrelated process, or at
none at all.

Derive the wording from whether a live parent actually exists: name it
when every process has one, state the facts when none do, and split the
list when it is mixed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(core): order the builds in the stale-discovery hub server case

The case stubbed two build ids and expected the second to replace the
first, but supplied nothing that says which came first: no epochs, and both
servers report the same core version. Ordering therefore fell to the
build-id tiebreak, where "new-build" sorts before "old-build" and the
replacement was judged the older of the two.

Give the case the epochs its name implies, and add the missing sibling for
an unorderable pair, which is attached to rather than retired - the
behavior that keeps two installations from shutting each other down.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(core): defer replacing a Hub that is serving live sessions

Retiring a Hub kills its established WebSockets, so replacing one under a
running session ends that turn with an abnormal close (code=1006). The
replacement is correct - the newer build should own the Hub - but the
timing is not the user's to absorb mid-turn.

Defer instead while the Hub reports live sessions: the newer client
attaches to the older Hub over the compatible wire protocol, and the swap
happens once those sessions end. Attaching rather than spawning matters -
a second daemon would race the busy one for the port.

Deferring silently would be worse than the interruption it avoids, because
a long-lived session pins the Hub to old code indefinitely with nothing to
show for it. The build-mismatch watcher only ever prompted in the
direction where updating the client resolves the mismatch; its own comment
notes that older Hubs "are retired and replaced automatically, so
prompting would only flash a stale dialog", which stops being true once
replacement can be deferred.

Add the missing direction as `outdated_hub`, reported only when a mismatch
survives consecutive checks - an idle older Hub is replaced within moments
of being seen, so a single sighting would flash exactly the stale dialog
the original comment warns about. The CLI and desktop dialogs render it as
information rather than an update prompt: nothing to install, the Hub
swaps itself when the sessions end.

The direction is decided by compareHubBuilds rather than reusability,
because a Hub that is newer and one that carries too little metadata to
order are both "reusable" but need opposite advice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(core): key the outdated-Hub check by daemon instance, not build

The consecutive-sighting check that keeps a routine replacement from
flashing an informational dialog was keyed by build id. Two daemons from
the same build share one, so an outdated Hub replaced by another daemon of
the same older build satisfied the check and reported exactly the churn the
check exists to hide.

Carry a hubInstanceId on the mismatch event - the Hub's own id, falling
back to pid and start time - and key the pending sighting by it. A
replacement instance now restarts the count instead of confirming it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(core): source Hub instance identity from the discovery record

The instance id added in the previous commit was read from the probe
response, but the watcher probes without an auth token and `/health`
deliberately reports only build and address fields - no hubId, pid, or
startedAt. So the id was always undefined in production and the check it
guards still conflated two daemons of the same build. The test missed it by
injecting a hubId into a mocked probe, a shape `/health` never returns.

Take identity from the discovery record instead, which every daemon version
writes with all three fields and which a replacement daemon rewrites as its
own. The probe is still preferred when it does carry an id, since that is
the process just spoken to.

The tests now use the real `/health` payload shape and vary identity through
the discovery record, including the pid-and-start-time fallback for records
written before Hubs carried an id.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(core): confirm the Hub record still describes the daemon just probed

Instance identity is read from discovery before the probe and build data
comes back after it, so a daemon replaced between those two steps was
described with its predecessor's identity - the replacement then satisfied
the prior daemon's pending sighting and emitted the notification the
consecutive-instance check exists to suppress.

Re-read discovery after the probe and report nothing when the record no
longer describes the same daemon. A Hub mid-swap is churn; the next check
sees whatever it settles into.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* revert(core): drop the watcher instance-identity hardening

Reverts the three follow-up commits that keyed the outdated-hub
consecutive-sighting check by daemon instance (42a83beae, 931431371,
9d634f7b3). They guarded one scenario - a different daemon of the same
outdated build swapping in between two watcher ticks - where the only
consequence is an informational dialog showing one interval early or
late. The unauthenticated probe carries no instance fields in
production, which is why the first attempt needed two more patches; the
original reason+buildId consecutive-sighting suppression from this PR's
base commit already covers the case that matters (not flashing a dialog
for a hub that is mid-replacement).

* fix(core): only count sessions that stopping the hub would actually harm

hasActiveHubSessions treated every non-terminal status as busy. But a
session's hub-side runtime outlives its client: a TUI that is killed or
crashes never stops its session, which then sits in the hub with no
participants and a status that never reaches a terminal state. Under the
defer-while-busy rule that pinned the displaced hub as "serving
sessions" forever - it was never retired, every new CLI kept attaching
to the old build, and the outdated-hub dialog recurred with a promise
("replaced once those sessions end") that could never come true.
Verified empirically: a cleanly detached+disposed client leaves its
session status "running" indefinitely.

Busy now means: someone is attached (participants), or a turn may be
executing hub-side (running/pending, which covers headless and scheduled
runs). An idle session with a confirmed-empty participant list is
resumable persisted state, not live work. Hubs from core < 0.0.75 omit
the participants field entirely, so idle stays conservative (busy)
there - an attached client cannot be ruled out.

updatedAt-freshness was considered and rejected as the discriminator:
the sessions row only updates on status transitions, so a single long
agentic turn looks stale while genuinely executing.

* fix(core): gate hub busyness on attached participants only

Simplifies the busy-check to the one signal that cannot go stale:
participants are live socket subscriptions the hub drops the moment a
client's connection closes, so a crashed client can never leave a ghost
that counts as busy. Session status is deliberately not consulted - a
client killed mid-turn strands its session in a non-terminal status
forever, and QA reproduced that pinning an outdated hub as "serving
sessions" until reboot. This replaces the earlier status+participants
heuristic (and drops the aging bound it was growing) with the rule the
deferred-update design stated from the start: the hub is busy while a
client is connected to a session, and replaceable otherwise.

The accepted cost: a participant-less background run executing at the
exact moment of a hub swap dies with the old hub. Rare, and its next
scheduled tick runs normally on the replacement.

* fix(cli): tell the truth about when the outdated Hub is replaced

The outdated-hub dialog and toast said the Hub is replaced "once those
sessions end". It is not: nothing retires a hub except a fresh launch
running the ensure path, so a user who quits the busy session and
watches sees the old hub stay put and concludes something is stuck
(observed in hands-on QA). Say what actually happens - the newer build
takes over the next time Cline starts after those sessions end.

* fix(cli): speak to users, not architecture, in the pending-update notice

"Cline Hub is running an older build" assumes the reader knows what the
Hub is and why builds differ. The user-relevant facts are only: your
update is not fully active yet, your work is safe, and it finishes by
itself. Say exactly that, in both the TUI and desktop dialogs and the
toast, with the version tucked in parentheses for bug reports.

* fix(cli): drop the outdated-hub dialog for a single quiet toast

The dialog interrupted the user to say that nothing is wrong and no
action is needed - the ideal number of modals for that message is zero.
The TUI now shows one info toast ("Update finishes the next time Cline
starts. No action needed.") and the desktop app shows nothing for the
outdated_hub reason; both dialog components return to their shipped
update-and-restart form, which still appears for the build_mismatch
direction where the user genuinely has something to do. The watcher
keeps reporting outdated_hub - surfaces decide, core informs.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-13 19:46:55 -07:00
Saoud Rizwan d3d3bd8749 fix(cli): defer auto-update install until no CLI is attached to the hub (#13233)
* fix(core): bridge protections for updates landing under pre-3.0.55 clients

Three pieces, each proven against real released artifacts:

- postinstall shield: CLI versions <= 3.0.54 restart the hub daemon after a
  background auto-update even while it serves live sessions, and their
  fingerprint check then rejects every replacement hub, bricking the running
  TUI. That code is on users' machines and cannot be patched — but it runs
  only after the install completes, and it bails out harmlessly when no hub
  discovery record exists. The newly installed package's postinstall sets
  the record aside so the old updater never fires.
- superseded-record fallback: the set-aside record is also the only source
  of the auth token and pid the next new-build launch needs to retire the
  displaced hub (a port probe carries neither); ensure reads it back.
- bind retry: a hub retired on the fixed port can hold it ~2s after acking
  shutdown (watchdog force-exit); the replacement daemon retries EADDRINUSE
  for up to 5s instead of dying and leaving no hub at all.

* fix(cli): defer auto-update install until no CLI is attached to the hub

Installing while cline processes run swaps the npm package under them:
their respawn paths break on the new build fingerprint, and the updater
then restarted the hub daemon out from under live sessions (the 'Hub
connection closed (code=1006)' incident). Guarding the restart treats the
symptom; the fix is to never install under a running process.

The startup check now only records that an update is available. The
install runs at process exit, and only when the hub confirms no other
cli* client is attached — desktop sidecars and connectors ship their own
binaries, so only cli* clients make the swap unsafe. With nothing old
running at install time, no hub restart is needed at all: the next launch
retires the stale hub through the existing ensure path. Deletes
restartHubServerIfRunning, ensureCliHubServerAfterUpdate, and their
support code; manual 'cline update' still installs immediately and now
just notes that the update applies on next start.

* fix(cli): apply deferred update from the entrypoint exit sequence

The CLI entrypoint always terminates with an explicit process.exit(),
which never emits beforeExit — the hook the deferred installer waited on,
so it would never have run (caught by review). Invoke applyDeferredUpdate
directly from the entrypoint's exit sequence after disposeAll(), where
every normal termination passes; crash paths deliberately skip it. Also
clear the pending update once an install spawns so the apply is
idempotent.

* test(cli): isolate unit tests from the real ~/.cline

A full vitest run could leave a real hub daemon running against the
developer's actual ~/.cline discovery record (observed while validating
this PR: a daemon spawned from the globally installed cline binary,
attached to the real data dir). Point CLINE_DIR, CLINE_DATA_DIR, and
CLINE_HUB_DISCOVERY_PATH at a per-worker temp dir and disable auto-update
before any test file loads; subprocesses inherit the isolation via env.

* fix(core): discard the superseded discovery record once consumed

The set-aside record is one-shot recovery metadata, but nothing deleted
it, and it feeds a pid into retireDiscoveredHub's SIGTERM. Weeks later a
launch that finds no live record (routine after any retirement) could
read the stale file and signal whatever process the OS recycled that pid
onto (review finding by @abeatrix). Unlink it at every ensure resolution
that ends with a live, verified hub; failure paths keep it for the next
attempt.

* fix(cli): harden the exit-time update gate

Three review findings on the deferred-apply path:

- A wedged hub could stall an otherwise-finished CLI for tens of seconds
  via the hub client's default timeouts; the whole exit-time query is now
  bounded to 3s, with timeout counting as attached (never install unless
  the hub positively confirms).
- Sub-second commands exited before the startup version check resolved
  and silently dropped the update every time for one-shot-only usage;
  exit now grants the in-flight check a 250ms grace.
- client.list can lose a TUI's registration during transport churn while
  its session connection survives, so an empty client list is not proof
  of safety; cross-check sessions with participants. Participants rather
  than session status: finished sessions linger idle forever and must
  not pin updates, and participant-less scheduled runs live in the hub
  process, which the binary swap does not touch. Verified live: a
  session-holding client invisible to client.list defers the install,
  and the gate opens once it disconnects.

* docs(cli): fix stale beforeExit reference in the exit-gate comment

* style(cli): apply biome formatting to update deferral code

* fix(cli): let doctor see a hub whose record the update shield set aside

During the shielded update window the discovery record is renamed to
.superseded so pre-3.0.55 updaters cannot restart a busy hub. Doctor
read only the primary record, so in that window it reported the live
daemon - the one serving the user's still-open old session - as a stale
hub daemon and advised 'cline doctor fix', which kills it and reproduces
the exact 1006 incident the shield exists to prevent (found by QA).
Doctor now falls back to the set-aside record the same way the ensure
path does, and doctor fix clears the set-aside file along with the
primary record so a deliberate reset does not leave stale retirement
metadata pointing at a recyclable pid.

* fix(core): keep shielded sessions on one Hub authority (#13244)

* fix(core): recover shielded busy hub discovery

* chore(core): instrument shielded hub recovery

* fix(core): recover shielded hubs with attached clients

* fix(cli): recognize shielded hubs in doctor

* refactor(core): keep shield recovery minimal

* fix(core): retain shared Hub idle helper semantics

* chore(core): align busyness helper with the #13231 wording

The participants-only hasActiveHubSessions here duplicates the change on
bee/hub-lifecycle (this branch needs its semantics for the participant
gate). Matching that version byte for byte lets the two merges resolve
cleanly instead of conflicting. Also restores the module-registry reset
comment this branch dropped - it documents a real local-vs-CI gotcha.
2026-08-13 19:39:44 -07:00
Bee afab86dcbd fix(core): stop concurrent Hub installs from retiring each other (#13230)
* fix(core): stop concurrent Hub installs from retiring each other

Two Cline installations on different builds would shut each other's Hub
daemon down in a loop, and every session died with an abnormal socket
close (code=1006) as its daemon was killed mid-handshake.

The retire decision was a one-sided predicate: each client independently
asked "may I reuse this Hub?", and two clients on differing builds both
answered no. #13177 added build-epoch ordering to break the tie, but left
every unordered case - missing epoch, missing build id - retiring as
before, so any pair involving a build from before epochs were embedded
still looped.

Derive the decision from a total order instead. compareHubBuilds orders
two builds by embedded epoch, then core release version, then build id,
and is antisymmetric by construction, so at most one side of a pair can
ever decide to retire. A Hub that is newer or cannot be ordered is
attached over the compatible wire protocol and left to the build-mismatch
watcher to prompt about. Genuine protocol incompatibility still replaces.

Identity is now read from the same fields on both sides. Filling in a
coreVersion locally that the wire record omits made a build's identity
depend on which role it was playing, and the two directions of a pair were
then decided by different tiers with both concluding they were newer -
a second, independent way to produce the loop.

Also:
- Scope the development Hub owner by build id, so differing dev builds run
  their own daemon side by side instead of contending for one record.
  Production keeps its singleton.
- Break the circuit after repeated retirements of the same URL, bounding
  any future ordering bug to a stale-build prompt rather than an
  unusable Hub.
- Report `cline doctor fix` honestly: separate processes that survived a
  kill from ones that appeared while the fix ran, name the live parent
  respawning a daemon, and mark a startup lock held by a running process
  as held rather than leaked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(cli): only blame a live parent for processes seen during doctor fix

The advice printed under "started during fix" asserted that every such
process was respawned by a live parent, and told the user to go stop it. A
process can also start on its own mid-repair - someone opening a new
session - and then the instruction points at an unrelated process, or at
none at all.

Derive the wording from whether a live parent actually exists: name it
when every process has one, state the facts when none do, and split the
list when it is mixed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(core): order the builds in the stale-discovery hub server case

The case stubbed two build ids and expected the second to replace the
first, but supplied nothing that says which came first: no epochs, and both
servers report the same core version. Ordering therefore fell to the
build-id tiebreak, where "new-build" sorts before "old-build" and the
replacement was judged the older of the two.

Give the case the epochs its name implies, and add the missing sibling for
an unorderable pair, which is attached to rather than retired - the
behavior that keeps two installations from shutting each other down.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 19:39:18 -07:00
Bee 6d7e745fb7 feat(core, llms): add provider-aware web search tools (#13075)
* feat(core, llms): Cline custom provider & web search

* fix(llms): preserve reasoning model token parameter

* fix(llms): keep ClinePass provider options on the wire in the shared Cline provider

The shared Cline provider hardcoded the AI SDK provider name to "cline",
but the openai-compatible model reads request-body passthrough options from
providerOptions[<name>]. Option routing emits ClinePass options under the
"cline-pass"/"clinePass" buckets, so gateway reasoning (extended thinking
budgets) silently stopped reaching the wire for cline-pass after it moved
off the generic openai-compatible module.

Thread the gateway provider id through as the provider name, and restore
strictJsonSchema: false for the new "cline" provider-options target so the
wire format matches the previous openai-compatible behavior. Add cline-pass
coverage at both the option-routing and request-body levels.

* feat(sdk): persist provider-executed tool activity (#13077)

* feat(core, llms): Cline custom provider & web search

* fix(llms): preserve reasoning model token parameter

* feat(sdk): persist provider-executed tool activity

* fix(vscode): restore state proto and settings section reverted by merge

The merge of origin/bee/websearch into this branch resolved conflicts by
keeping this branch's pre-#13126 copies of apps/vscode files, which
deleted the auto_approve_all_toggled = 174 proto field (without reserving
the number) and dropped a formatting line in FeatureSettingsSection.tsx.
Neither file is in scope for this PR. Restore both to main's content so
the proto source matches the checked-in generated code again.

* chore(vscode): match main byte-for-byte in FeatureSettingsSection.tsx

The pre-commit biome hook strips a blank line that exists on main, which
kept this out-of-scope file in the PR diff. Commit the exact main content
with --no-verify so the PR no longer touches apps/vscode at all.

---------

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

* refactor(llms): key ClinePass provider options to the shared cline bucket

Both Cline gateway ids (cline and cline-pass) are served by the same
shared "cline" AI SDK provider and hit the same Cline API, so threading
the gateway provider id through as the AI SDK provider name (78dc6f3e7)
was unnecessary indirection. Revert the name threading and instead
normalize option-routing bucket keys: buildProviderAndAliasPatch now
keys both Cline gateway ids to the shared "cline" providerOptions
bucket, which is the only bucket the openai-compatible model reads for
request-body passthrough.

Also tighten the regression coverage that motivated the original fix:
the previous effort-based test rows were vacuously satisfied through the
portable-reasoning early return (effort reasoning never reaches provider
option buckets by design). The rows now use explicit reasoning budgets,
which do flow through the gateway bucket path, and the wire-level test
composes real provider options end to end instead of hand-feeding
buckets.

* revert(llms): drop the cline strictJsonSchema special case in generic-compatible

Restores buildCompatibleProviderOptions to its pre-78dc6f3e7 state. The
strictJsonSchema passthrough is verified inert for the gateway (nothing
in @cline/llms sets a response format), so keeping a hardcoded provider
target in the generic helper bought nothing. If structured outputs are
ever added, strictness for the cline target can be decided deliberately
then.

* fix(llms): claim native web search for openai-native, not the openai alias

supportsModelTool listed "openai", but that id aliases to
openai-compatible (PROVIDER_ID_ALIASES), whose module has no native web
search. The actual native OpenAI builtin id is "openai-native", which is
served by the OpenAI Responses module that does implement
buildModelTools with provider.tools.webSearch(). Without this, the
web_search tool was never offered to native OpenAI users, and was
wrongly offered for the compatible alias.

* refactor(llms): declare model tools in provider manifests

* feat(sdk): project provider tool activity in session history

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-13 18:12:10 -07:00
Bee fcd5a9e0fb feat(desktop): add app font size bootstrap script (#13232)
* feat: add app font size bootstrap script

- Import and inject APP_FONT_SIZE_BOOTSTRAP_SCRIPT in root layout
- Call syncAppFontSize on app initialization
- Add aria attributes (describedby, label, labelledby, valuetext) to Slider component
- Replace thumb key generation with useId hook for better stability
- Add settings view tests for font size functionality

* feat(desktop): add native zoom menu shortcuts
2026-08-13 14:08:09 -07:00
Saoud Rizwan c8afb44368 chore(vscode): prepare 4.1.9 release 2026-08-13 00:08:09 -07:00
Saoud Rizwan d30cce4cf1 chore(cli): release v3.0.54 2026-08-12 23:13:29 -07:00
Saoud Rizwan 3f39c46026 chore(sdk): release v0.0.74 2026-08-12 22:45:26 -07:00
Saoud Rizwan 274ad3bdb2 chore(desktop): release v0.0.12 2026-08-12 22:37:06 -07:00
Saoud Rizwan e6b1c3fbc8 chore(ui): bump @cline/ui to 0.2.0-next.4
Picks up the shared button primitives (#13164), the one-row-per-tool-call
chat rendering (#13186), the refined session chat layout (#13205), and the
@pierre/diffs hunk renderer (#13201) that landed since 0.2.0-next.3.
2026-08-12 20:40:03 -07:00
Bee 5b22490d41 fix(desktop): filter scheduled Core sessions (#13213) 2026-08-12 18:18:09 -07:00
Saoud Rizwan d03e88e50b Render desktop diff view hunks with the shared @pierre/diffs renderer (#13201)
* Render desktop diff view hunks with shared @pierre/diffs renderer

Replace DiffView's hand-rolled DiffHunk +/- line rows with ToolFileDiff
from @cline/ui (backed by @pierre/diffs), matching the chat tool rows.
Hunks carrying complete new contents (created files) render with real
line numbers; fragment hunks hide them, mirroring ToolCallRow. All of
DiffView's chrome (collapse, copy, open-in-editor, counts) is unchanged.

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

* Make ToolFileDiff syntax palette follow the app theme, not browser preference

@pierre/diffs declares 'color-scheme: light dark' on its shadow :host, so
its light-dark() token colors resolve from the browser's preferred scheme.
Apps themed by the .dark class (desktop app) got the light palette's
near-black text on dark surfaces. Inline colorScheme: inherit on the host
wins over the :host rule and follows the app's color-scheme, which the
@cline/ui theme already flips with .dark. Skipped when a caller pins an
explicit themeType.

Also key diff-view hunks by index so repeated same-shaped hunks (a file
created twice with identical contents) don't collide.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-12 18:06:44 -07:00
Sufiyan Khan 02a1bfd0a8 fix(webview): use editor foreground for diff block text colors (#13200) 2026-08-13 09:41:52 +09:00
Saoud Rizwan 4559f12c66 feat(desktop): temporarily disable welcome prompt suggestions and center input (#13170)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-12 14:59:03 -07:00
Saoud Rizwan 6d6f79f948 Fix Claude Code provider: anchor session on workspace, load user settings, allow edits (#13152)
The Claude Code provider was unusable for agentic work (#13146):
- The claude-code manifest lacked the provider-tools capability, so the
  gateway sent Cline's tool definitions (which the provider drops as
  unbridgeable) while the CLI's native tools stayed enabled with no
  approval plumbing - every write was refused and no prompt appeared.
- ai-sdk-provider-claude-code defaults settingSources to [], so the
  spawned session read neither ~/.claude/settings.json nor project
  settings, silently ignoring user-configured permission rules.
- No cwd was passed, so the session inherited the extension host's
  cwd (/ on macOS) and refused writes outside it.

Changes:
- Mark claude-code with provider-tools (same treatment as the Codex
  CLI provider): stop sending unbridgeable external tools and let the
  CLI execute its own, tagged executionMode=provider for the runtime.
- Forward the session workspace cwd from @cline/core into the
  claude-code gateway provider options and lift it into the agent
  session settings.
- Default settingSources to [user, project] and permissionMode to
  acceptEdits (file edits under cwd auto-approved; command execution
  stays gated by the user's own Claude settings), all overridable via
  explicit defaultSettings.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-12 14:51:37 -07:00
Saoud Rizwan cde49170db fix(desktop): first turn of a fresh session no longer wedges the composer (#13122)
* fix(desktop): don't let a stale queued send response wedge the composer

A fresh session is still busy while its interactive loop starts, so the
sidecar coerces the first send onto the pending-prompt queue and replies
{queued:true} with a queue snapshot taken at enqueue time. The turn itself
runs via the runtime's queue drain and completes through stream events
(chat_queued_prompt_start -> deltas -> chat_done). On cold/slow sidecars the
RPC response lands only after those events; the webview then applied the
stale snapshot and unconditionally set status back to "running", leaving
the composer on "Agent is working..." forever and resurrecting a phantom
queue entry.

Webview: capture the turn epoch at send dispatch; chat_queued_prompt_start
bumps it, so a mismatch when the queued response arrives means the stream
already advanced the turn lifecycle and the response is ignored. Aborts now
resolve the queued branch to "cancelled" like the direct path.

Sidecar: the queued send response no longer routes its enqueue-time snapshot
through applyPendingPrompts, which overwrote the event-maintained
session.promptsInQueue and rebroadcast the stale list to every webview.

Includes deterministic regression tests for the stale-response orderings
plus temporary [P0DBG] debug instrumentation (region-marked, to be removed
after runtime verification).

* fix(desktop): ignore stale hub 'running' status after turn settles

The sidecar core is hub-attached, so chat_session_status events are
asynchronous projections of the hub's session record. A stale 'running'
can trail the stream's chat_done and flip a settled turn back to busy,
wedging the composer on 'Agent is working…' with nothing left to
reconcile. Track the epoch at which the turn settled and drop 'running'
status events until a new turn bumps the epoch.

* chore: remove stray QA screenshot artifacts from repo root

* chore(desktop): remove P0 debug instrumentation and fault injection

Strips all [P0DBG] logging, the /p0dbg sidecar route, the webview log
mirror + heartbeat, and the P0DBG_STARTUP_BUSY_MS /
P0DBG_DELAY_QUEUED_RESPONSE_MS fault-injection paths used to reproduce
the stuck-composer P0. The two real fixes (stale queued-response epoch
guard + stale-running-after-settle guard in the webview, and the
sidecar's non-clobbering queued-send snapshot) and the regression tests
remain.

* refactor(desktop): replace turn-epoch guards with an explicit turn lifecycle

The stuck-composer fixes left the hook with two hand-rolled epoch refs
(turnEpochRef / turnSettledEpochRef) mutated and compared inline across
eight call sites. Extract the rules into a pure TurnLifecycle module that
is now the only writer of the session status:

- a settled turn cannot be reopened: stale hub 'running' projections and
  stale queued-send acknowledgements are dropped by the lifecycle instead
  of by inline epoch comparisons
- async work (send RPC responses, queue reconciliation) captures an opaque
  token and the lifecycle decides whether the world moved on, instead of
  handlers comparing counters
- every status write goes through a named operation (begin, turnStarted,
  settle, projectStatus, apply, reset), so the state machine is explicit
  and unit-testable in isolation

No behavior change: the 5 wedge regression tests and the full hook suite
pass unchanged, plus 10 new unit tests for the lifecycle module itself.

* Revert "refactor(desktop): replace turn-epoch guards with an explicit turn lifecycle"

This reverts commit c480aaabe8.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-12 14:50:39 -07:00
Haley Park e703f7af2a style(desktop): refine session chat layout (#13205)
* style(ui): refine chat message surfaces and actions

* feat(desktop): refine session transcript layout

* style(desktop): refine conversation composer

* style(ui): format chat theme changes
2026-08-12 14:38:50 -07:00
Saoud Rizwan 105d357b12 fix(vscode): stop legacy-task migration backlog telemetry spam (#13185)
* fix(vscode): stop legacy-migration backlog telemetry spam, emit real migration outcomes

* refactor(vscode): slim migration telemetry fix to minimal surface

* fix(vscode): emit legacy migration outcome only after seeded session start settles

The completed event fired at in-memory conversion time, before the
seeded session start persisted the migration, so a start/persistence
failure was misreported as a successful migration and never produced an
error outcome. Conversion now records a pending migration; the followup
and compaction coordinators settle it after the session start resolves
(completed) or rejects (error/session_start_failed).

* fix(vscode): surface seeded-persistence failures in migration outcomes

LocalRuntimeHost.startSession deliberately swallows seeded-message
persistence failures (the in-memory session still works), so a resolved
start was not proof the legacy conversion became durable. The start
result now reports seededMessagesPersistence, and the resume/compaction
coordinators settle the migration from that result: completed only when
the seed write succeeded, error/seed_persistence_failed when the start
resolved but the write failed, error/session_start_failed when the
start rejected. durationMs now spans conversion through settlement.

Adds the core boundary test forcing persistSessionMessages to fail and
asserting the start still resolves with the failure visible on the
result, plus coordinator tests for both failure modes.

* refactor(vscode): drop per-task migration outcome events, keep volume fixes only

Scope the PR down to the zero-behavioral-risk telemetry fixes, per
review: keep the backlog event transition gating and the one-line
migratedSdkTaskCount fix (counting resumed legacy sessions via their
legacyTask metadata), and revert the per-task terminal outcome
plumbing (pending-migration settlement, coordinator hooks, and the
core StartSessionResult.seededMessagesPersistence field) along with
the success->completed outcome rename on the now-uncalled
captureLegacyTaskMigration. The per-task outcome events can land
separately on the observable persistence boundary.
2026-08-12 14:22:28 -07:00
Sufiyan Khan 274e0c574f Fix silently repaired truncated tool-call JSON (#13015)
* fix(llms): reject truncated tool-call JSON with unterminated strings

* fix(llms): scope truncation guard to jsonrepair only and handle single quotes

* fix(shared): keep jsonrepair ahead of bare-object repair for typed literals

Restores main's precedence for inputs both strategies can handle:
{"flag": True} must repair to a typed true, not the string "True".
The truncation guard now gates only the jsonrepair step, which is the
only strategy that can invent a string terminator.

---------

Co-authored-by: Mikołaj Kondratek <19799111+mkondratek@users.noreply.github.com>
2026-08-12 22:32:03 +02:00
Saoud Rizwan 98d883bf6e fix(llms): replace all-empty-text messages with placeholder content (#13204)
A message whose content array held only empty text parts slipped past the
existing empty-content guards in formatMessagesForAiSdk (which cover
content: "" and content: []). The AI SDK then strips empty text parts,
producing {"role":"user","content":[]} on the wire, which strict
providers reject — seen in prod as Vercel 400s for kimi-k3:
"user message must have content".
2026-08-12 13:15:44 -07:00
Saoud Rizwan 6ff6a1ecbb fix(telemetry): emit per-request deltas in task.tokens on SDK surfaces (#13188)
* fix(telemetry): emit disjoint per-request token buckets in task.tokens

SDK usage events follow the AI SDK convention where inputTokens is the
full request input including cache reads/writes. task.tokens forwarded
that value as tokensIn while also reporting cacheReadTokens and
cacheWriteTokens, so every event re-counted the whole (mostly cached)
conversation context and per-task token sums inflated ~5x on
cache-heavy sessions relative to the legacy contract (tokensIn =
uncached input only, disjoint buckets).

task.tokens now subtracts the cache buckets from tokensIn at the
capture site (mirroring the webview's normalizeUsageEvent), defaults
the cache buckets to 0 instead of undefined, and stamps the provider
attribute for parity with the legacy event schema. Event and attribute
names are unchanged.

* fix(core): normalize registered ApiHandler usage to cache-inclusive inputTokens

Review follow-up: two producer contracts shared AgentUsage.inputTokens.
Native AI SDK usage reports the full cache-inclusive prompt size, but the
ApiHandler adapter forwarded classic disjoint chunk.inputTokens unchanged,
so the task.tokens cache subtraction would zero out real uncached input
for a cache-reporting registered handler.

Normalize at the adapter boundary (inputTokens + cacheReadTokens +
cacheWriteTokens) so every producer entering AgentUsage satisfies the
same cache-inclusive invariant, document that invariant on
AgentTokenUsage.inputTokens, and reframe the telemetry clamp as a
defensive guard rather than a supported producer shape. Adds an adapter
normalization test and a boundary test from an ApiStreamUsageChunk
through task.tokens asserting the disjoint buckets round-trip.

* Revert "fix(core): normalize registered ApiHandler usage to cache-inclusive inputTokens"

This reverts commit 9a9aff374a.
2026-08-12 12:53:50 -07:00
Saoud Rizwan 0f8d715dc7 fix(telemetry): report involuntary Cline logouts from the SDK auth service (#13183)
* fix(telemetry): report involuntary Cline logouts from the SDK auth service

The SDK auth service cleared credentials silently when a refresh token was
rejected (invalid grant), both mid-session and during startup restore, so
user.auth_logged_out never captured involuntary logouts on the next bundle.
Emit token_invalid at both credential-clearing sites and restore_error when
startup restore throws, matching the reason vocabulary the legacy bundle now
uses so the same warehouse query measures involuntary logouts across rollout
variants. Startup with no stored session still emits nothing.

* refactor(telemetry): trim logout-reason parity change to the minimum

* fix(telemetry): report Cline invalid-grant logouts as token_invalid in the SDK resolver

getValidClineCredentials is the single owner of the involuntary-logout
event for the Cline provider; normalize its reason to the legacy
extension's LogoutReason vocabulary (token_invalid) so warehouse queries
cover both bundles. The raw OAuth code stays in errorCode. Codex/OCA
paths keep emitting invalid_grant and are unaffected.

* fix(telemetry): let the SDK resolver own token_invalid; keep restore_error for real restore failures

Address review on the SDK-adapter half of the logout-reason split:
- drop both adapter-side token_invalid emissions - the SDK resolver
  already emits user.auth_logged_out on the same telemetry instance, so
  the adapter was double-counting the exact signal being measured
- transient failures refreshing the stored session on startup (resolver
  throws: network/timeout/5xx) no longer book as restore_error; stored
  credentials are kept and the SDK books auth_refresh_soft_failure, so
  an offline startup is not a logout
- single-source LogoutReason in services/auth/types.ts and re-export it
  from the SDK auth service instead of maintaining two parallel enums
- boundary test runs the real getValidClineCredentials and asserts
  exactly one auth_logged_out (reason=token_invalid) total, so a
  reintroduced adapter emission fails the suite
2026-08-12 12:52:01 -07:00
Saoud Rizwan 3d460d7add feat(hub): directional Hub upgrades with update-and-restart prompts in CLI and desktop (#13177)
* feat(hub): prompt update and restart when another install replaces the shared Hub

* feat(hub): make managed Hub build-watch interval configurable via CLINE_HUB_BUILD_WATCH_INTERVAL_MS

* feat(hub): reuse newer managed Hub builds instead of retiring them

Embed a build epoch alongside the deterministic runtime fingerprint so
managed-Hub compatibility can order builds in time. When fingerprints
differ, a Hub produced after the client's own build is attached over the
compatible wire protocol (and the build-mismatch watcher prompts the user
to update) instead of being retired, so concurrent installations converge
on the newest build rather than replacing each other's daemons. Older,
unordered, or metadata-less Hubs are retired and replaced as before.

* refactor(hub): simplify mismatch status derivation and dedupe sidecar event encoding

* fix(cli): only watch for managed Hub build mismatches in hub-attached sessions

Yolo and sandbox sessions force the local backend and never attach to the
shared managed Hub, so a newer Hub owned by another installation must not
interrupt them with the blocking update dialog.

* fix(desktop): stage an app update before hub-mismatch restart

'Update and restart' previously invoked restart_to_apply_update directly,
which only relaunches the current bundle. With no update staged by the
background 2h updater loop, the app came back on the same version, hit the
same newer Hub, and re-prompted immediately.

Add a check_for_update_now Tauri command that runs one updater
check/download/stage cycle on demand and reports the resulting status. The
dialog now stages the update first and restarts only when the updater
reports 'ready'; otherwise it stays open and explains that no update is
downloadable yet (or that the check failed) instead of restarting into the
same version. Addresses the outstanding Greptile P1 on the dialog.

* fix(desktop): reset the no-update hint when a new hub mismatch arrives

Without this, a dialog for a fresh mismatch reopened pre-set to 'Try again'
with the previous prompt's stale hint.

* fix(desktop): serialize updater cycles so overlapping checks cannot clobber a staged update

The periodic update loop and the on-demand check_for_update_now command
run the same check/download/stage cycle against shared state. Without
exclusion, two overlapping cycles could download the same bundle
concurrently, and the later one could overwrite a freshly staged "ready"
status with "idle" or "error" decided from its stale pre-await
ready_version snapshot - making the update dialog deny that a staged
update exists. A tokio::sync::Mutex now serializes whole cycles; the
ready_version snapshot is read under the lock, so it stays authoritative
for the cycle that took it.
2026-08-12 12:33:27 -07:00
Bee 24f66a4673 fix(core): harden Cline Hub daemon lifecycle (#13168)
* fix(core): harden Hub daemon lifecycle

* fix(core): wait for Hub listener before replacement

* fix(core): recover after Hub cleanup errors

* fix(core): assign the close memo handle before socket termination re-enters beginClose

On every shutdown with a connected client, the daemon logged
'unhandledRejection: AggregateError: hub server close failed' and exited
with code 1 instead of 0. Root cause: beginClose() terminated the
tracked WebSockets before assigning closeHandle. terminate() fires close
events whose microtask continuations advance the daemon coordinator's
deferred cleanup into server.beginClose() while the first invocation is
still mid-body, so the memo guard passes twice and a second set of
wss.close()/server.close() calls runs against the already-closing server,
rejecting with 'Server is not running' and spuriously failing the close
aggregate. The rejection then rode the daemon's unhandledRejection
fatal path and escalated the exit code.

Construct the close promises and assign the memo handle first, and only
then terminate sockets and run detach handlers; a re-entrant call now
hits the memo guard. Also observe the /shutdown handler's
fire-and-forget closeServer() so a genuine close failure is reported
solely by the owner's own await on the same memoized promise instead of
the unhandledRejection path.

Verified with a real daemon: shutdown with zero clients stays graceful
(exit 0, ~26ms); shutdown with a held-open authenticated WebSocket now
exits 0 with no unhandled rejection, still bounded by the 2s coordinator
deadline for the genuine Bun listener-close stall, with the discovery
record cleaned. Hub suites (228) and the shutdown e2e (5) pass.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-12 12:33:27 -07:00
Saoud Rizwan 354c80df7b fix(llms): update AI SDK deps so streamed tool calls with non-zero indexes don't crash (hasFinished) (#13123)
* fix(llms): update AI SDK deps to fix streamed tool calls with non-zero indexes

LiteLLM's Anthropic passthrough emits chat-completions tool_call deltas
whose index mirrors the Anthropic content-block index (1 when a text
block precedes the tool call; see BerriAI/litellm#11580).
@ai-sdk/provider-utils 5.0.18 stored streamed tool calls in a sparse
array keyed by that index and crashed at stream flush with
"Cannot read properties of undefined (reading 'hasFinished')",
aborting the agent turn. Upstream fixed this in provider-utils 5.0.21
("Fix streamed tool calls with non-zero, non-contiguous, reused, or
missing indexes.").

Update the ai / @ai-sdk packages so every chat-completions streaming
path resolves @ai-sdk/provider-utils 5.0.25, and drop the root
">=4.0.0" override on @ai-sdk/provider-utils: with intersect semantics
it pinned the workspace to the already-locked 5.0.18 even after parents
began requiring 5.0.25, and it force-upgraded dify-ai-provider two
majors past its declared ^3 range. Each package now resolves the
version line it declares.

Fixes #13119

* test(llms): pin non-zero streamed tool_call index regression (#13119)

Wire-level regression test: an openai-compatible SSE stream whose only
tool_call delta carries index 1 (Anthropic content-block numbering via
LiteLLM) must complete and emit the tool-call part instead of throwing
at flush.

* fix: address review findings from merge-conflict resolution

- Restore apps/vscode/proto/cline/state.proto to main's version: the
  merge commit's pre-commit hook regenerated it with a stale generator,
  deleting auto_approve_all_toggled = 174 and moving a reserved line,
  creating drift against the checked-in descriptor. The deletion was
  never intended.
- Restore FeatureSettingsSection.tsx to main's version (the same hook
  reformatted main's file during the merge).
- Regenerate bun.lock narrowly from main's lockfile without --force so
  the diff contains only the @ai-sdk family and its direct transitives;
  drop the spurious webview-ui-scoped @radix-ui duplicate entries the
  previous install introduced (hoisted resolutions still satisfy
  webview-ui's unchanged ranges; verified with --frozen-lockfile).
- Align @ai-sdk/provider to ^4.0.7 in @cline/llms to match the rest of
  the AI SDK family and avoid parallel provider resolutions.

Revalidated: wire repro streams to finishReason=tool-calls, @cline/llms
suite passes incl. the index-1 regression test, all workspaces
typecheck, SDK builds clean.

* fix: restore FeatureSettingsSection.tsx to main's formatting

The branch's pre-commit biome hook (--semicolons=as-needed, --write
--staged) strips a blank line from this file whenever it is staged,
which is how the unintended diff appeared in the merge commit. Commit
with --no-verify to keep the file byte-identical to main; this PR does
not touch the VS Code webview.
2026-08-12 11:30:42 -07:00
John Choi 8437a01906 fix(hub): correlate run start acknowledgments (#13054) 2026-08-12 11:26:16 -07:00
Haley Park 986f0b010b feat(ui): add shared button primitives (#13164)
* feat(ui): add shared button primitives

* fix(ui): disable composed button links

* fix(ui): block disabled composed capture handlers

* fix(ui): protect composed disabled semantics
2026-08-12 10:57:12 -07:00
Haley Park 1be2d20349 refactor(desktop): extract chat message components to messages/ (#13155)
* refactor(desktop): extract chat transcript logic to messages/

Verbatim moves out of chat-messages.tsx (2,415 -> ~1,300 lines), with no behavior changes.

- Extract shared constants, grouping and reasoning helpers, tool summaries, and tool icons into messages/.
- Add unit tests for the extracted pure logic.
- Update test:chat-ui to include tests under messages/.

chat-messages.test.tsx remains unchanged and continues to pass.

* refactor(desktop): extract chat message components to messages/

Moves MessageBubble, ReasoningBlock, ToolMessageBlock, ToolApprovalPanel
(+ formatApprovalTimestamp and the ToolApprovalRequestItem type), and the
image lightbox out of chat-messages.tsx into their own modules under
messages/. Memo wrappers, comparators, and prop contracts are unchanged;
chat-messages.tsx keeps only the ChatMessages orchestration (~780 lines).
chat-messages.test.tsx is untouched and still passes.

* refactor(desktop): extract chat transcript logic to messages/

Verbatim moves out of chat-messages.tsx (2,415 -> ~1,300 lines), with no behavior changes.

- Extract shared constants, grouping and reasoning helpers, tool summaries, and tool icons into messages/.
- Add unit tests for the extracted pure logic.
- Update test:chat-ui to include tests under messages/.

chat-messages.test.tsx remains unchanged and continues to pass.

* refactor(desktop): extract chat message components to messages/

Moves MessageBubble, ReasoningBlock, ToolMessageBlock, ToolApprovalPanel
(+ formatApprovalTimestamp and the ToolApprovalRequestItem type), and the
image lightbox out of chat-messages.tsx into their own modules under
messages/. Memo wrappers, comparators, and prop contracts are unchanged;
chat-messages.tsx keeps only the ChatMessages orchestration (~780 lines).
chat-messages.test.tsx is untouched and still passes.

* refactor(desktop): extract chat message components to messages/

Moves MessageBubble, ReasoningBlock, ToolMessageBlock, ToolApprovalPanel
(+ formatApprovalTimestamp and the ToolApprovalRequestItem type), and the
image lightbox out of chat-messages.tsx into their own modules under
messages/. Memo wrappers, comparators, and prop contracts are unchanged;
chat-messages.tsx keeps only the ChatMessages orchestration (~780 lines).
chat-messages.test.tsx is untouched and still passes.
2026-08-12 10:39:17 -07:00
Saoud Rizwan a56af4efaf fix(telemetry): stop mirroring per-token stream deltas into telemetry (#13180)
* fix(telemetry): stop mirroring per-token stream deltas into telemetry

Gate assistant-text-delta, assistant-reasoning-delta, and tool-updated
runtime events out of the unconditional telemetry.capture mirror in
AgentRuntime.emit. These fire once per streamed token or tool progress
chunk and accounted for ~97% of all agent.* telemetry volume in the
field with no analytical value. Listeners, hooks.onEvent, and the
run-failed sdk.error reporting are unchanged; the gate is a static
Set lookup so no per-event allocation is added.

* refactor(telemetry): inline stream-delta telemetry gate as a switch

Replace the module-level Set constant with case labels directly at the
capture site; same behavior, less indirection.
2026-08-12 18:20:47 +09:00
Saoud Rizwan 10e5176ed8 refactor(ui): one row per tool call, terminal-style commands, and review fixes (#13186)
* feat(ui): styled label parts, terminal-style commands, and patch fidelity fixes

Label segments: ToolSummary gains labelParts ({text, code?}[]) so
consumers can render code-ish segments (file names, commands, queries,
URLs) in a monospace face. Single commands now read like a terminal
prompt — '$ bun test' — and an untruncated single command no longer
duplicates itself as a detail line.

Review fixes folded in:
- apply_patch preserves hunk boundaries: per-hunk oldText/newText on
  ApplyPatchFile and file items (re-diffing concatenated hunks let a
  deletion in one hunk pair with an addition in another), plus action
  metadata — Delete File labels as 'Deleted x' with no phantom diff,
  '*** Move to:' renames display as 'old → new'.
- run_commands accepts every RunCommandsInputUnionSchema shape (single
  entry, bare arrays, top-level {command,args}, {cmd}).
- makeUnifiedDiff treats empty text as zero lines, so creating an empty
  file or deleting all content no longer reports a phantom +1.
- parseWebFetchInput drops non-string urls instead of stringifying
  objects into labels.
- hoisted a double normalizeValue in the unknown-tool fallback.

* refactor(desktop): render each tool call as its own chat row

Drops the consecutive-call grouping ('Read 3 files · Ran 2 commands')
in favor of one row per tool call — each with its own icon, status,
disclosure, and treatment per kind:

- commands read like a terminal: '$ bun run test' in monospace, with
  the captured output in a capped scrollable mono block on expand and
  '$ '-prefixed detail lines for multi-command calls
- edit rows carry mono filenames, the +/- badge, and their pierre
  diffs pre-expanded (one diff per hunk for multi-hunk patches),
  keeping the user-toggle override from the grouped implementation
- reads/searches/fetches keep inline specifics with mono code segments
  via the shared labelParts

Also fixes the test:chat-ui exit-1 regression flagged in review:
@pierre/diffs' custom element calls CSSStyleSheet.replaceSync, which
jsdom lacks — a prototype polyfill in the suite keeps the real
component in the test tree (and the pre-expand assertions meaningful)
while letting the run exit 0. This suite gates ui-publish.yml.

* fix(desktop): keep the thinking indicator up during quiet turn stretches

The indicator only covered the gap right after a user message, so the
turn looked frozen while the model composed its next step — most
noticeably while streaming tool-call arguments, when neither text nor
a tool row is on screen. It now shows whenever the turn is running and
nothing else is visibly active (no streaming text, no in-progress tool
row, no pending approval/question).

* feat(ui): action-first tool labels

Every row leads with the plain action phrase — 'Ran command',
'Read file', 'Edited file', 'Created file', 'Deleted file' — with the
specifics (command, file name, line range) following as a monospace
segment. The mono segment renders at full size; the previous 0.92em
downscale made it look smaller than the surrounding prose.

* fix(desktop): chat polish — indicator alignment, action spacing, no expanded fade

- The Thinking indicator now mirrors the tool-row trigger metrics
  (min-h-7, py-1, gap-2, 16px icon, font-medium, 8px rhythm) so the
  text no longer shifts when the indicator swaps with an arriving
  tool row.
- The copy/fork/timestamp action row sat 4px up into the message text
  above it (-translate-y-1); it now rests 2px below the message block.
- Expanded reasoning/tool panels rendered at 70% opacity with
  hover-to-unfade; expanded content is what the user is reading, so it
  now renders at full opacity.

* feat(ui): violet active rows, gray finished rows, no green hover

Tool-row colors follow activity: running/pending rows (and the row
spinner) carry the brand violet, finished rows settle into
muted-foreground gray, and hover brightens toward the foreground
instead of hue-shifting to the success green. Errors stay red.

Also: maxInlineChars default raised 60 → 200 so real commands stop
getting truncated (the cap is now only a guard against pathological
payloads; layout handles overflow), and expanded editor rows lead with
the fuller file path above the diff, matching read rows.

* refactor(ui): let layout own label overflow instead of char caps

maxInlineChars now defaults to unlimited — labels carry the full
command/task/question text (whitespace collapsed to one line) and
.cline-chat-tool-label ellipsizes at the container edge via CSS
(nowrap + text-overflow) instead of wrapping. The cap remains as an
opt-in for width-constrained surfaces like TUIs. Since the label can
now be visually cut by layout, single-command rows always carry the
full command in their expanded details.

* fix(ui): drop stale green base color on tool triggers

The redesign moved finished tool rows to muted gray and running rows to
brand violet, but a leftover .cline-chat-tool-trigger { color:
var(--success-text) } rule later in the sheet overrode the gray base, so
every settled row still rendered green.

* fix(desktop): give message actions clear separation from message text

2px below the text read as touching; 6px (translate-y-1.5) gives the
copy/fork/timestamp row visible breathing room.

* feat(ui): spinner replaces the tool icon while a call is in flight

The progress ring used to append to the right of the label, so running
rows sprouted chrome instead of reading as one glyph + label. It now
takes the icon slot and fills the same 1rem box, so the label never
shifts when the icon swaps back in on completion.

* fix(desktop): align thinking indicator with the tool row that replaces it

The indicator sits outside the message column, so it already inherits the
conversation gap; its own mt-2 stacked on top and rendered it 8px lower
than the tool row that swaps in.

* style(desktop): message actions match chat text scale in a lighter gray

Copy/edit/restore/fork icons go from 12-14px to the 16px the rest of the
chat chrome uses, the timestamp moves from 11px to text-sm, and the whole
row renders at 70% muted-foreground so it reads as secondary chrome;
hover still brightens to full foreground.

* style(ui): running tool rows share the thinking indicator's gray

Violet-on-running read as a different system than the muted thinking
state it replaces; the spinner alone now signals activity. The progress
ring draws in currentColor so it stays gray on normal rows and red on
error rows without extra rules.

* style(desktop): nudge message actions down 2px and scale them down a step

Actions row moves from 6px to 8px below the message text; icons go
16px -> 14px and the timestamp text-sm -> text-xs after the previous
bump overshot.

* fix(ui): don't unstick conversation follow when content grows

Stick-to-bottom flipped off whenever a scroll event landed between a
content-height jump (tall diff rows mounting) and the resize observer's
re-pin: the handler read the new distance-from-bottom as the user having
left the bottom. Sticking is now released only by an actual upward
scroll and always restored on reaching the bottom, so the transcript
keeps following while rows stream in.

* feat(ui): user message bubbles on a filled brand-violet surface

The card-colored bubble sat too close to the app background to read at
a glance. New brand-violet-surface tokens (deep enough for near-white
text in both themes) fill the user bubble.

* style(desktop): give the conversation bottom padding above the composer

The last message (and its hover actions hanging below) butted against
the composer border.

* fix(desktop): composer keeps its two-line height when unfocused

Collapsing to one row on blur made the input and the conversation above
it jump on every focus change; the focus-tracking state existed only to
drive that resize.

* refactor(ui): simplify AgentAskQuestion and move it to the brand accent

The 'Follow-up question' heading, intro sentence, and box-in-box nesting
made a one-question prompt read like a form. The question now leads the
card directly (icon + text + option buttons) and the accent shifts from
blue to brand violet, with the section still labelled for assistive
tech.

* fix(desktop): pending questions and approvals render at the end of the transcript

They rendered above the whole conversation like a banner, so a follow-up
question appeared at the top of the chat instead of where the
conversation actually is.

* fix(desktop): keep message actions reachable and make hover/focus feedback instant

The 8px offset under a message was a translated gap — dead space that
dropped the parent's :hover midway to the buttons, hiding them before
they could be clicked. The offset is now padding on the actions element
so the hover chain stays unbroken. Also removes the opacity fade on the
actions row and the composer's focus border transition: both read as lag
rather than polish.
2026-08-11 21:00:04 -07:00
Saoud Rizwan a5611e8f6d feat(ui): shared tool-summary module for consistent tool-call chat rows (#13151)
* feat(ui): add shared tool-summary presentation module

Pure, framework-free tool-call presentation logic under
@cline/ui/components/agent-chat/tool-summary: buildToolSummary and
buildGroupedToolLabel turn raw {toolName, input, result} payloads into
rich row labels (file names with line ranges, inline commands, search
queries, URLs), per-item details, +/- diff counts, per-file unified
diffs (editor old/new text and apply_patch envelopes), team_* labels,
and MCP-aware output text extraction. Merges the desktop app's
buildToolSummary layer with the CLI's tool-parsing/diff utilities so
every @cline/ui consumer renders tool rows consistently.

Exports the new subpath from package.json, extends the packed-tarball
smoke test to cover it, documents the boundary change in ADOPTION.md,
and bumps the package to 0.2.0-next.3.

* refactor(desktop): adopt shared tool-summary for chat tool rows

Replaces ~950 lines of app-local tool extraction (buildToolSummary,
teamSummary, parsers, grouped-label logic) in chat-messages.tsx with
the @cline/ui tool-summary module. Desktop tool rows gain single-call
specifics inline (Read app.tsx (10-80), Ran bun test, Edited util.ts
with +/- badge), line ranges on reads, shortened paths with directory
context in expanded details, per-file unified diffs in the expanded
panel, and grouped labels joined with a middot. Detail keys switch to
index-based to fix duplicate-line key collisions. Icons re-key on the
shared ToolKind classification.

* fix(ui): stop fabricating line positions in fragment tool diffs

Editor str_replace payloads carry old_text/new_text as fragments of the
file, but makeUnifiedDiff treated them as whole files and emitted hunk
headers anchored at line 1, mislocating the change in expanded edit
rows (Greptile P1 on #13151). Fragment diffs now use a neutral
'@@ … @@' separator; only whole-file content (editor create,
apply_patch Add File sections) keeps real hunk positions.

File items also expose the raw oldText/newText (reconstructed from
hunks for apply_patch) plus a fragment flag, so rich diff renderers
can consume the texts directly instead of re-parsing unified output.

* feat(ui): render tool-row edit diffs with @pierre/diffs

Adds @cline/ui/components/agent-chat/tool-diff exporting ToolFileDiff,
a thin wrapper over @pierre/diffs (optional peer dependency) that
renders a tool-summary file item as a syntax-highlighted, theme-aware
unified diff. Fragment diffs hide line numbers instead of showing
misleading ones. The desktop chat renders edit diffs through it, and
tool groups containing an edit diff now open pre-expanded so the diff
is immediately visible.

ADOPTION.md reframes the shared-module story: extracting presentation
logic products would otherwise duplicate is the direction @cline/ui is
headed, with tool-summary and tool-diff as the first two modules. The
packed-tarball smoke test covers the new subpath in both consumers.

* fix(ui): blend tool diffs into the app surface

Two polish fixes to ToolFileDiff from design review:

- Normalize trailing newlines on both sides before diffing so tool
  payload fragments (which rarely end in a newline) don't litter every
  diff with 'No newline at end of file' markers.
- Map @pierre/diffs' background hooks (--diffs-light-bg/--diffs-dark-bg)
  to the host app's --background token (stock white/black fallback),
  so the diff surface and all its color-mixed tints (context lines,
  gutters, separators) derive from the app background instead of
  pierre's pure white/black. Overridable via a new background prop.

Storybook's ToolSummaries story now renders file items through
ToolFileDiff pre-expanded (matching the apps) and adds a multi-file
apply_patch fixture.

* fix(desktop): pre-expand tool groups when edit diffs arrive mid-stream

defaultOpen only applies at mount, but a streaming tool group mounts
with its first (often read) call and gains the edit later, so live runs
never saw the promised pre-expanded diff. Drive the disclosure with
controlled state that opens when a file diff first appears, unless the
user has toggled the row themselves. Covers the streaming path with a
rerender test.
2026-08-11 17:54:52 -07:00
Bee 66883f584c fix(desktop): top-align welcome chat input (#13174)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-11 14:41:41 -07:00
Haley Park e370732545 refactor(ui): migrate fonts to Inter and Geist Mono (#13142)
* chore(ui): replace font dependencies

* refactor(ui): migrate shared typography tokens

* refactor(ui): adopt Inter and Geist Mono in apps

* fix(hub): preserve variable font weight tokens

* feat(ui): tune font weights for dark mode

* docs(ui): add font migration screenshots

* (chore)ui: misc typography adjustments

* fix(hub): make dark-mode font-weight overrides take effect

Tailwind's @theme inline bakes literal values into utilities, so the
.dark --font-weight-* overrides were dead code and dark mode rendered
the heavier light-mode weights. Declare the weights in :root instead so
font-* utilities keep their var() references, matching the @cline/ui
tokens approach. Also rewrap --font-mono to satisfy biome format.

* fix(ui): restore light-mode semibold to 640 and pin weight scales in test

The PR intent is a 480/560/640/640 light scale with 400/500/600/600
dark overrides, and the Hub already uses 640; tokens.css had drifted to
600 for light semibold. Regenerate scoped-tokens.css and assert both
the light and dark weight scales in the theme contract test.

* chore(desktop): remove stray double space in provider header class

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-11 14:39:57 -07:00
Haley Park ee4adda2da refactor(desktop): extract chat transcript logic to messages/ (#13153)
Verbatim moves out of chat-messages.tsx (2,415 -> ~1,300 lines), with no behavior changes.

- Extract shared constants, grouping and reasoning helpers, tool summaries, and tool icons into messages/.
- Add unit tests for the extracted pure logic.
- Update test:chat-ui to include tests under messages/.

chat-messages.test.tsx remains unchanged and continues to pass.
2026-08-11 14:33:27 -07:00
Saoud Rizwan 651829b24a chore(cli): release v3.0.53 2026-08-11 11:32:41 -07:00
Saoud Rizwan 00ba2ff7da chore(sdk): release v0.0.73 2026-08-11 11:00:44 -07:00
Mikołaj Kondratek 3f03159737 fix(vscode): don't discard a successfully refreshed Cline token after expiry (#13139)
getAuthToken captured expiresAt before refreshing, then validated the new
token against that stale value. When the old token was already past expiry
(not just inside the 5-minute buffer), a successful refresh was thrown
away and null returned, so the first call after long idle failed despite
valid credentials. Re-read the expiry from the refreshed auth info.
2026-08-11 18:23:12 +02:00
Mikołaj Kondratek 3087bd3d32 fix(hub): recoverable agent errors must not end the turn in the dashboard (#12962)
* fix(hub): don't forward recoverable agent errors to dashboard peers

Recoverable error events are in-run notices, not turn outcomes: the
MistakeTracker emits one for every recorded mistake (e.g. a plan-mode
guard-blocked run_commands call) while the run continues. The hub
dashboard forwarded every error event to peers, so the webview dropped
out of the sending state and appended an error row mid-turn — the same
host bug fixed for VS Code and the CLI in #12953.

Gate the forward on recoverable, matching those hosts: the tool failure
is already shown inline via the failed tool_event, and the turn's
outcome stays decided by how it actually ends (turn_done or a
non-recoverable error). Recoverable errors are logged server-side.

* fix(hub): forward recoverable flag to peers instead of filtering server-side

Per review: the server is a translation layer between agent events and
the webview protocol, so it should not embed display policy or console
logging. Forward every agent error with its recoverable flag on the
peer message and let each peer decide — the webview keeps recoverable
errors out of the transcript and keeps the turn state, matching how the
CLI gates display on the same flag while the information stays
available to any peer that wants it.
2026-08-11 18:11:40 +02:00
Bee 9b59090967 fix(desktop): reconnecting to stale managed Hub daemons (#13145)
* fix(core): replace stale managed hub daemons

* fix(core): fingerprint hub runtime builds

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-11 01:34:00 -07:00
Saoud Rizwan 7e31fb9e0d chore(vscode): prepare 4.1.8 release 2026-08-10 20:03:53 -07:00
John Simone e0eb0167da feat(vscode): add Fable 5 + custom model IDs to Vertex; drop global-region picker filter (#12461)
* add custom model selection to the vertex provider

* fix race conditions from PR review

* fix linter warnings

* fix test failures

* refactor(vscode): drop Vertex global-endpoint picker filtering

The SDK catalog is live (models.dev), so a static host allowlist of
global-endpoint-capable models lags every model launch and silently hides
new models from users on vertexRegion=global. Remove the allowlist, the
host override that injected supportsGlobalEndpoint, and the picker filter;
show the full catalog for every region.

An unsupported pick now fails loudly at request time: map Vertex's
'model not available in region: global' (and Google's Publisher Model
locations/global not-found body) to recovery guidance in the error row.

Also drop Anthropic's universal pricing from the Vertex Fable 5 overlay —
Vertex bills region-dependently, so the copied price understated recorded
cost; the record now carries no pricing instead of a wrong one.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-10 19:58:20 -07:00
Mikołaj Kondratek d5748b2939 fix: remove stale Double-Check Completion feature tip (#13147)
* fix: remove stale Double-Check Completion feature tip

The rotating feature tips still told users to enable "Double-Check
Completion" in settings, but that toggle was removed in the new UI —
the Features section now offers Auto Compact, Feature Tips, Background
Edit, Checkpoints, Worktrees and Hooks. Following the tip sent users
searching the settings panel for something that isn't there.

Drop the tip. The remaining ten were checked against the current UI and
all still hold, including the "Settings → Features → Feature Tips" path.

* chore: remove dead CLI settings e2e page object and orphaned test

`page-objects/settings.ts` asserted the CLI settings Features tab shows
"Double-check completion" — the same removed setting behind the stale
feature tip. Nothing in the live tui-test suite (apps/cli/src/tests)
imported it; only chat.ts and auth.ts page objects are in use.

Its one importer, apps/vscode/tests/e2e/cli/interactive.test.ts, is a
leftover from the pre-2026-06-02 SDK migration squash: all three of its
imports resolve to files that don't exist, there's no tui-test config in
that tree, and no npm script runs it. It cannot execute.
2026-08-11 00:54:41 +02:00
Mikołaj Kondratek ffd6a6b1db fix: respect user max output tokens in compaction summarizer requests (#13137)
* fix: respect user max output tokens in compaction summarizer requests

The compaction summarizer hardcoded max_tokens to 1024 and the VSCode host
never mirrored the user's Max Output Tokens onto providerConfig, so summary
requests were always capped at 1024 tokens. Reasoning models can spend that
entire budget thinking; the reasoning stream is discarded, so no summary
text arrives and compaction is skipped on every attempt.

- Mirror maxTokensPerTurn onto providerConfig.maxOutputTokens in the VSCode
  session factory so consumers that build handlers straight from it (the
  compaction summarizer) honor the user's setting, matching the CLI.
- Resolve the summarizer output budget from explicit config, then model
  info, then knownModels, before the default; raise the default to 4096.
- Log a diagnostic warning (reasoning chars, incompleteReason, likely
  cause) when the summarizer returns no summary text instead of silently
  skipping.

* fix: clamp summarizer default output budget by model metadata instead of adopting it

Model maxTokens is reported capability, not a product default: without an
explicit configuration the summarizer now requests the 4096 default, lowered
by model metadata when the model reports less, never raised by it. Explicit
values still win as-is.
2026-08-11 00:20:45 +02:00
cline-cloud[bot] 51784a3bf1 docs: add Qwen3.8 Max to ClinePass model list and reference pricing (#13144)
Co-authored-by: Cline <cline@users.noreply.github.com>
2026-08-10 14:52:43 -07:00
Saoud Rizwan 149abb0ddb feat(vscode): remove YOLO mode setting, migrate old users to auto-approve all (#13126)
* feat(vscode): remove YOLO mode setting, migrate old users to auto-approve all

The SDK extension's YOLO toggle was cosmetic: nothing in the approval
path read it, so runs were silently governed by the per-action
auto-approval settings underneath (cline/cline#13114). Instead of
keeping a parallel override system, remove the setting entirely and
make the auto-approve menu the single source of truth:

- drop yoloModeToggled (and the equally dead autoApproveAllToggled)
  from state keys, settings handlers, state posts, telemetry, the
  remote-config yoloModeAllowed transform, and the settings protos
  (field numbers reserved)
- remove the Yolo Mode toggle from Settings -> Features (the whole
  Experimental section, it was the only entry) and the
  "Auto-approve: YOLO" AutoApproveBar takeover
- add a v3 storage migration that folds a previously-enabled YOLO /
  auto-approve-all toggle into autoApprovalSettings by enabling every
  action, so previously-unattended setups keep running unattended;
  the dead keys are cleared from the file store

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

* refactor(vscode): keep dead yolo keys in place instead of clearing them

Current builds never read the removed keys (the state loader only visits
known keys), so deleting them buys nothing - and the file store is shared
with older builds that still know them, so clearing would flip YOLO off
for a user who downgrades. Same downgrade-safety rule as the v1 export.

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

* chore(vscode): rename wasUnattended to shouldEnableAllActions in yolo migration

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

* chore(vscode): drop dead toggleActModeForYoloMode and stale yoloModeAllowed comment

The method was a legacy-controller carryover nothing called, and it set
the mode without rebuilding the session, which is wrong for the SDK
architecture. The comment cited yoloModeAllowed as a live remote-config
example; it no longer maps to anything.

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

* chore(vscode): refresh checked-in proto descriptor_set.pb

The tracked descriptor set had not been regenerated since the repo
move and still advertised long-changed schemas (including the removed
yolo_mode_toggled fields) to gRPC reflection clients. Sync it with the
output of bun run protos.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-09 17:15:37 -07:00
Saoud Rizwan b3cee3f973 chore(desktop): release v0.0.11 2026-08-08 20:53:18 -07:00
Saoud Rizwan c68f553856 chore(vscode): prepare 4.1.7 release 2026-08-08 20:38:09 -07:00
Saoud Rizwan fd794ce3be chore(cli): release v3.0.52 2026-08-08 19:34:11 -07:00
Saoud Rizwan f5f1071af2 chore(sdk): release v0.0.72 2026-08-08 19:00:03 -07:00
Saoud Rizwan 4540390096 desktop: hide git jargon for non-git folders (CLIENTS-100) (#13059) 2026-08-08 15:57:00 -07:00
Saoud Rizwan b590e14b91 desktop: paste clipboard images into the composer (CLIENTS-78) (#13057)
* desktop: paste clipboard images into the composer as attachments (CLIENTS-78)

Pasting a screenshot into the composer did nothing: only drag-and-drop
and the paperclip file picker fed the attachment pipeline. Add an
onPaste handler on the composer textarea that extracts image files from
the clipboard, renames them to timestamped pasted-image-*.png files, and
routes them through the existing onAttachFiles flow. Text pastes are
untouched.

* desktop: only extract clipboard images in formats message serialization supports

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-08 15:56:27 -07:00
Saoud Rizwan 54cc156089 desktop: context-aware welcome suggestions for non-code folders (CLIENTS-98) (#13060)
* desktop: context-aware welcome suggestions for non-code folders (CLIENTS-98)

* desktop: treat pending branch discovery as its own state for welcome cards

The welcome-card classifier read the "no-git" sentinel as a confirmed
non-repo, but page.tsx also used that value for the initial state and
while a workspace switch was awaiting branch discovery, so a git repo
could briefly show the plain-folder cards. Branch state is now null
while discovery is pending: the welcome screen shows no cards until the
folder is classified, and chat-mode cards (which never depend on git
state) still show immediately. Other branch consumers keep the string
contract via a "no-git" fallback.

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

* desktop: carry nullable branch state to all consumers

Propagate the pending-discovery null through ChatInputBar,
WorkspaceSelector, and the welcome workspace controls instead of
coercing to "no-git" at the page boundary, so only display leaves
fall back and the welcome classifier is the single consumer that
distinguishes pending from confirmed non-repo.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-08 15:45:29 -07:00
Saoud Rizwan 6c599d18c3 desktop: surface folder picker failures and add manual path fallback (CLIENTS-73) (#13056)
* desktop: never let 'Add project…' fail silently; add manual folder path entry (CLIENTS-73)

- sidecar picker tries zenity then kdialog on Linux and throws a descriptive
  error when neither exists, instead of returning null (indistinguishable
  from user cancel); picked paths are trimmed of trailing separators
- picker failures now surface as visible error messages in both workspace
  selectors, with a manual path-entry fallback (typed absolute or ~ paths
  in the search box offer an 'Open folder' action)
- failed workspace switches (invalid/nonexistent paths) show an inline
  error instead of silently doing nothing
- validate_workspace_directory expands ~ and returns the resolved path

* desktop: keep workspace menu search/error state through catalog refreshes

The welcome-screen workspace picker reset its search text and error
message whenever onRefreshWorkspaces changed identity, which happens on
every session-history poll. Typing a path or reading an inline error
raced against the timer: the menu would silently wipe mid-interaction.
Hold the refresh callback in a ref so the reset only runs when the menu
actually opens.

* desktop: format welcome-workspace-controls test

* desktop: distinguish picker launch failures from user cancellation

A zenity/kdialog rejection with a non-ENOENT spawn error (EACCES, EMFILE,
ENOMEM) or a crash signal was classified as a user cancel, which skipped
the kdialog fallback and suppressed the inline error - recreating the
silent no-op this branch is meant to eliminate. Only a clean exit code 1
from a dialog that actually opened now counts as cancellation; broken
backends fall through to the next candidate and surface a descriptive
error otherwise. Picker logic moved to sidecar/workspace-picker.ts with
an injectable exec so the classification is unit-tested.

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

* Classify picker launch failures separately from user cancellation

zenity/kdialog failures like EACCES, EMFILE, or ENOMEM were treated as
user cancellation, suppressing the kdialog fallback and the inline
manual-entry error. Only a clean exit code 1 now counts as a cancel;
any other failure falls through to the next backend or throws the
picker-unavailable error. Picker logic moved to sidecar/folder-picker.ts
with an injectable exec so the classification is unit tested.

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

* Revert "Classify picker launch failures separately from user cancellation"

This reverts commit 24d27a004d.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-08 15:43:52 -07:00
Saoud Rizwan 513aacc0e6 fix(core): full-stop semantics and abort-window queue edits for surviving queues (#13100)
* fix(core): preserve queued prompts across user-initiated aborts

Pressing stop while prompts were queued silently destroyed them:
abort() called clearAborted(), which emptied the pending prompt queue
with no way to recover the typed input. The prompts vanished from the
UI, were never sent, and left no trace in session artifacts.

Aborting now only stops the in-flight turn. Queued prompts stay in the
queue and drain once the abort settles, matching the drain behavior
that already existed for self-aborted turns (loop detector / mistake
limit). The thrown-abort path (completeAbortedInteractiveTurn) now
schedules the same drain that runTurn schedules for turns resolving
with an aborted finish.

* fix(core): full-stop semantics and abort-window queue edits for surviving queues

Follow-up to the queued-prompt survival change: aborting a user turn keeps
the queue and auto-runs it, but two gaps remained.

1. No full stop: aborting a queue-initiated turn also kept draining, so
   every Escape consumed one queued prompt and started a fresh provider
   call - a session with queued messages could never be brought to rest.
   Aborting a drained turn now discards the remaining queue: the first
   Escape skips to your queued follow-ups, a second Escape stops the
   queued work too.

2. Queue operations were still rejected while an abort settled: a prompt
   typed right after Escape was silently dropped, and queued prompts were
   briefly uneditable and undeletable even though they were about to
   auto-run. enqueue/update/delete now work during the abort window;
   scheduleDrain/drain still wait for the abort to settle.
2026-08-08 12:37:18 -07:00
Saoud Rizwan ff2f860941 test(core): pin abort + hub-restart durability, and title seeded sessions (#13097)
* test(core): cover abort + host restart + seeded recovery durability

Adds an e2e regression guard for the reported "cancel a turn, lose the
conversation" failure: a cancelled turn, a daemon restart, a
client-side recovery seeded from disk, and a second restart before that
replacement ever runs a turn. Reverting the eager seeded-history
persistence makes the final read come back empty.

Materializing a seeded session at start also left its history row with
no prompt and no title, since there is no first prompt to derive one
from. Seed the title from the inherited transcript using the same
inference listSessionHistory hydration applies, so forks and recoveries
stay identifiable in unhydrated surfaces too.

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

* fix(core): retitle seeded sessions from their first user prompt

Eagerly-materialized seeded sessions kept the interim transcript-
inferred title forever, a behavior change from pre-eager persistence
where a fork's history row was titled by the first post-fork prompt.

The interim title now only covers the window where no turn has run
(previously those rows were simply absent), and the first user prompt
after the seed backfills the row's prompt and retitles it — unless the
user renamed the session in the meantime, in which case only the prompt
column is backfilled. The resident manifest and session metadata are
updated in step so the end-of-turn usage-metadata merge cannot clobber
the title back through a stale in-memory fallback.

The e2e mock's updateSession now mirrors the real persistence-service
contract (row + manifest file), and the durability e2e covers both the
retitle and the rename guard; removing the retitle call fails the
'now add tests' assertion.

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

* refactor(core): collapse seeded-session titling to the old mechanism

The interim transcript-derived title, retitle flags, rename comparison,
and resident-manifest syncing existed only to title forks that never
run a turn - a new nicety, not parity. Dropping it collapses the whole
design back to what rows did before eager persistence: the persistence
service derives the title from the prompt when a row gains one, so the
host only needs to backfill the promptless row with the first user
prompt via updateSession. Renames win automatically because the service
preserves an existing title when no explicit title is passed.

Net production change vs main is a single 20-line backfill block in
executeTurn. The e2e mock's updateSession now models the service's
title semantics (explicit title wins, existing title preserved,
untitled rows derive from prompt), and the durability e2e asserts the
raw row stays untitled until first prompt while history hydration
infers a display title from the transcript.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-08 12:30:30 -07:00
Saoud Rizwan 62a6b5a0b2 fix(core): preserve queued prompts across user-initiated aborts (#13090)
Pressing stop while prompts were queued silently destroyed them:
abort() called clearAborted(), which emptied the pending prompt queue
with no way to recover the typed input. The prompts vanished from the
UI, were never sent, and left no trace in session artifacts.

Aborting now only stops the in-flight turn. Queued prompts stay in the
queue and drain once the abort settles, matching the drain behavior
that already existed for self-aborted turns (loop detector / mistake
limit). The thrown-abort path (completeAbortedInteractiveTurn) now
schedules the same drain that runTurn schedules for turns resolving
with an aborted finish.
2026-08-08 12:20:08 -07:00
Saoud Rizwan 0caf617b50 fix(core): keep a hung MCP server from taking down session creation (#13086)
* fix(core): keep a hung MCP server from taking down session creation

A stdio MCP server that never finishes initializing used to hold its
connect open for the full DEFAULT_MCP_CONNECT_TIMEOUT_MS (doubled across
the newline/framed attempts). MCP tool discovery runs on the
session.create critical path, so that wait blew past the 30s hub command
timeout and the CLI tore the whole interactive session down instead of
just skipping the bad server.

- Bound MCP tool loading during session build with a startup budget that
  is safely under the hub command timeout. Servers that connect in time
  contribute their tools; slower/hung servers are skipped for the session
  (their error still surfaces via the MCP manager) instead of failing
  session creation. Budget is overridable via CLINE_MCP_STARTUP_BUDGET_MS
  for tests.
- Add StdioMcpClient.close() (and optional McpServerClient.close) that
  marks the client disposed so an in-flight connect() aborts its retry
  loop instead of respawning the framed fallback.
- Dispose the manager by closing clients up front, outside the per-server
  operation locks, so a server hung in initialize can no longer stall
  teardown for the full connect budget.

Adds regression tests covering both the non-blocking build and prompt
disposal while a client is hung in connect().

* refactor(core): simplify hung-MCP-server fix to a startup budget

Replace the bespoke per-server race/tracking in loadConfiguredMcpTools
with a small withStartupBudget() wrapper around the existing
Promise.allSettled: a server that exceeds the budget becomes a normal
rejection that the existing loop already logs and skips. The connect
budget, MCP settings display (initialize timeout 30s), and the rest of
the loader are left untouched.

The client close()/manager.dispose() cleanup is kept minimal: it is what
lets teardown abort a still-in-flight connect instead of blocking on the
per-server lock (and clears the pending request timer).

* fix(mcp): cap the default initialize budget at 3s to protect session creation

Supersedes the startup-budget approach on this branch with the simple
constant fix.

MCP initialize runs on the session.create critical path, which the hub
caps at 30s, and connect() can spend the budget twice (newline then
Content-Length framing). The 30s default from #13067 meant a server that
never initializes held session.create for up to 60s, so the hub RPC
timed out and the CLI tore the whole session down and exited.

Return to the pre-#13067 shape with a bigger probe: 3s instead of 1.5s.
That still covers the ~2s starters the old probe killed (#13035) and
keeps the worst case at ~6s per server, far under the hub deadline.
Genuinely slow starters (JVM-based servers like Oracle SQLcl) now need
an explicit timeout in cline_mcp_settings.json, which continues to
override the default in either direction.

Tests: update the slow-start regression tests to the new policy (2s
connects by default, 4s connects with a configured timeout), refresh the
displayed initialize-timeout assertions, and add an invariant test that
keeps the doubled default well under HUB_DEFAULT_COMMAND_TIMEOUT_MS so
the budget cannot silently creep past the session deadline again.
2026-08-08 12:18:11 -07:00
Saoud Rizwan 930575991d fix(desktop): stop opening a session from replacing the remembered model (#13091)
* fix(desktop): stop opening a session from replacing the remembered model

The composer's ModelSelector mirrored every provider/model prop change
into the remembered last selection (localStorage), which seeds new
sessions via getInitialChatConfig(). Opening an existing session drives
those props to that session's config, so merely viewing an old session
silently replaced the user's explicitly picked default model.

The remembered selection is now written only from the explicit picker
handlers (provider select and model select). Passive prop changes, such
as opening a session, no longer touch it.

* fix(desktop): re-seed remembered provider/model on chat reset

reset() kept the previous config's provider/model and only cleared the
session ID, so a chat pane that had hydrated a historical session could
carry that session's model into the next chat. Re-seed provider/model
(and apiKey when the provider changes) from the remembered defaults --
the same source a freshly mounted thread uses -- so reset and remount
behave identically.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-08 12:10:39 -07:00
Saoud Rizwan e40d7d44ae fix(desktop): stop treating leftover plugin install dirs as installed (#13095)
* fix(desktop): stop treating leftover plugin install dirs as installed

isOfficialPluginInstalled() only checked that the marketplace install
directory existed. A failed or interrupted install can leave that
directory behind with no plugin inside, and the next install attempt
then short-circuited with a fake 'already installed' success: the
marketplace button flipped to Uninstall with no error while nothing
actually worked, and the installed-entries listing kept reporting the
broken entry as installed.

The check now requires a loadable plugin module inside the directory
(via discoverPluginModulePaths) before reporting the entry as
installed, so partial directories fall through to a real install
attempt whose outcome is surfaced to the UI.

* fix(desktop): reclaim leftover partial plugin install dirs with --force
2026-08-08 12:06:35 -07:00
Saoud Rizwan e973ce4f33 fix(cli): make queued message text readable on light theme TUI (#13098)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-08 12:00:50 -07:00
Saoud Rizwan 4f25692d70 fix(desktop): canonicalize diff panel paths against the session cwd (#13092)
* fix(desktop): canonicalize diff panel paths against the session cwd

Tool calls address the same file inconsistently across a session: one
edit uses a workspace-relative path (journal.txt), a later one the
absolute path (/tmp/ws/journal.txt). mergeToolDiffs keyed entries by the
raw string, so the same file was listed twice in the diff panel with
split +/- counts and inconsistent naming, most visibly after git was
initialized mid-session and the model switched to absolute paths.

Diff paths are now canonicalized against the session cwd before
merging: entries for the same file collapse into one, files inside the
cwd display as workspace-relative paths, and files outside it display
their resolved path. Without a cwd the previous raw-key behavior is
kept.

* style: collapse editorReplaceEvent signature per biome format

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

* fix(desktop): collapse dot segments and keep root cwd in diff path keys

* fix(desktop): compare Windows diff path keys case-insensitively

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-08 11:55:58 -07:00
Saoud Rizwan b0bba2e5d6 fix(vscode): hide View Changes on completion rows until there are changes to show (#13096)
* fix(vscode): hide View Changes on completion rows until there are changes to show

The button previously always rendered on the latest completion row, faded
and disabled when the count check came back 0 - which covers both 'nothing
changed since your last message' and 'no checkpoint to compare against'
(non-git workspace, repo with no commits, comparison failure). A dead
button with a misleading tooltip in the non-git case is worse than no
button: now the row renders nothing until the host confirms there are
actual changes, and the button is always enabled when shown.

* fix(vscode): reset View Changes state when showViewChanges toggles

Greptile review: a stale positive hasChanges from a previous evaluation
could flash the button before the host confirms the new comparison when
showViewChanges flips false and back true on the same row. Reset to
'still checking' whenever the effect re-runs.
2026-08-08 11:41:41 -07:00
Saoud Rizwan a6e2c0c431 fix(core): never run a foreign compiled plugin-sandbox bootstrap for a source host (#13094)
* fix(core): never run a foreign compiled plugin-sandbox bootstrap for a source host

When @cline/core runs from source (e.g. the desktop hub daemon in dev)
with CLINE_WRAPPER_PATH set, resolveBootstrap() picked the compiled
plugin-sandbox-bootstrap.js from a separately installed CLI platform
package (such as a published version sitting in the package-manager
cache) before falling back to the source bootstrap. That bootstrap
resolves modules against the other installation's layout, so every
plugin failed to load with "Cannot find module '@cline/core'" - and
the settings pipeline swallowed the failure, leaving Settings > Tools
showing "No plugin tools found" and plugins showing no contributions
even though the same plugins loaded fine in chat sessions.

Bootstrap selection now prefers, in order: a compiled bootstrap next to
this module (always matches the host build), the source bootstrap when
the host runs from source, and only then wrapper/executable-derived
bootstraps - which remain the path for compiled binaries where
import.meta points inside the bunfs bundle.

* chore(core): restore untouched settings-service formatting
2026-08-08 11:23:25 -07:00
Saoud Rizwan d011d049a1 fix(core): keep session context durable across aborts and hub restarts (#13078)
* fix(core): keep session context durable across aborts and hub restarts

Users on slow self-hosted endpoints reported sessions losing their entire
conversation after cancelling a long-running request: the TUI still showed
the transcript, but the next turn greeted them like a brand-new session.

Root cause is a stack of two failures:
1. The hub daemon exits on any unhandled rejection that is not an
   AgentRuntimeAbortError, so a floating abort-family rejection from a
   cancelled provider stream kills every resident session.
2. When the CLI recovers the missing session it rebuilds from the persisted
   messages file - but aborting a turn never flushed the transcript, and
   lazy session persistence (SDK 0.0.70) kept seeded history (mode-switch
   restarts, forks, previous recoveries) memory-only until the first
   completed turn. Recovery then seeds an empty session: silent context wipe.

Fixes:
- completeAbortedInteractiveTurn now flushes the transcript to disk, so an
  aborted exchange survives a hub restart.
- Sessions started with initialMessages persist them (and any compaction
  sidecar) immediately; brand-new empty sessions stay lazy, so closing an
  unused runtime still leaves no empty history entry.
- The hub daemon ignores abort-family unhandled rejections (DOMException
  AbortError, Node ABORT_ERR) the same way it already ignores
  AgentRuntimeAbortError, instead of exiting with every session resident.

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

* fix(core): write seeded history atomically with session materialization

Greptile review flagged a residual crash window in the seeded-session
persistence: ensureSessionPersisted created the session row (with an empty
messages file) and only then called persistSessionMessages, so a crash
between the two left a discoverable session whose seeded history was gone.

Close the window by threading initialMessages/systemPrompt through
createRootSessionWithArtifacts: the messages artifact is now written with
the seeded transcript before the session row is committed, so every crash
point leaves either nothing discoverable or complete data. The follow-up
persistSessionMessages call at session start is gone; the seed travels
inside session materialization.

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

* Revert "fix(core): write seeded history atomically with session materialization"

This reverts commit 5a7e0b37f1.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-07 21:44:40 -07:00
Saoud Rizwan e09afced69 fix(core/hub): report queued-turn failures as run.failed (#13074)
Turns drained from the pending-prompt queue resolve their errored
AgentResult inside PendingPromptsController.drain(), which discards it,
and the legacy 'error' agent event had no projection in the hub's
session-event projector — so a failed queued turn never produced any
terminal hub event. Interactive clients (e.g. the desktop app) hung on
'Thinking...' with no error shown.

The projector now publishes run.failed (with the error text and a core
session snapshot) for non-recoverable lead-agent error events, but only
when no RPC-driven turn is awaiting sessionHost.runTurn for that
session — the awaiting run.start handler already publishes the
authoritative terminal event, so this avoids double-reporting a turn
that resolves through both paths.
2026-08-07 20:52:01 -07:00
Saoud Rizwan 3f83be51a2 feat(vscode): fade View Changes button until changes since last message are confirmed (#13076) 2026-08-07 20:16:15 -07:00
Saoud Rizwan 1efe5577e1 fix(core/cli): drain queued prompts after self-aborted turns and surface the stop (#13061)
* fix(core/cli): drain queued prompts after self-aborted turns and surface the stop

When a run ends with finishReason "aborted" without a user abort request
(loop detector hard escalation or the consecutive-mistake safety stop),
runTurn skipped the pending-prompt drain, stranding user-queued messages
forever, and the CLI rendered nothing - the task appeared to silently
stop with queued messages never consumed (#13030).

- core: schedule the drain after every completed turn, including
  aborted/error finishes. User-initiated aborts are unaffected because
  abortSession() already clears the queue, and drain() stops after one
  failed send so an erroring provider cannot spin the queue.
- cli: when a turn comes back aborted without the user having requested
  an abort, append a "Task stopped before completion." status entry
  instead of ending the turn silently.

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

* fix(core): hold queued prompts on error finishes instead of consuming them

Addresses the Greptile P1 review on #13061: a drained prompt whose turn
resolved with finishReason "error" returned normally, so the
exception-only requeue path treated the send as successful - the failed
prompt was consumed and draining continued firing the remaining queue
into a failing provider.

- drain() now stops the chain when a drained send resolves with an
  error finish. The errored entry itself is not requeued (its turn ran:
  the prompt is in the conversation and the error is surfaced), but the
  rest of the queue is held.
- runTurn() no longer schedules a drain after "error" finishes (the
  skip is removed only for "aborted", which is the #13030 fix).
  Held prompts still drain via the existing enqueue/update/delete
  triggers or the next successful turn.
- Two new unit tests cover both layers.

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

* revert(cli): drop the 'Task stopped before completion' status line

Keep the change scoped to the queue-drain fix in @cline/core. The CLI
no longer prints a notice for non-user-initiated aborted finishes;
apps/cli is back to parity with main. When messages are queued, the
drain itself makes the stop visible (the queued message runs); richer
stop-reason surfacing can be a follow-up.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-07 19:27:47 -07:00
Saoud Rizwan 9cbc24d6e5 Restore "View Changes" on completion rows using SDK checkpoints (#13072)
* fix(core): read untracked-at-snapshot files from stash third parent in checkpoint diff

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

* feat(vscode): restore View Changes button on completion rows via SDK checkpoint diff

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

* refactor(vscode): integrate View Changes as a footer inside the completion card

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

* style(vscode): place the View Changes button inside the completion card

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-07 18:11:25 -07:00
Saoud Rizwan 551286d33b fix(cli): preserve binary MCP payloads in expanded TUI tool output (#13071)
* fix(cli): preserve binary MCP payloads in expanded TUI tool output

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

* simplify to minimal payload-preserving fix

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-07 18:10:28 -07:00
Saoud Rizwan 361ac90978 fix(llms): route LiteLLM through Chat Completions instead of the Responses API (#13053)
The litellm builtin spec pinned protocol: "openai-responses", so every
request went to POST {baseUrl}/responses. Self-hosted LiteLLM proxies
commonly implement only /chat/completions, so all prompts failed with
404 Not Found on the SDK path (CLI, and now the Next extension bundle).

Drop the override so litellm inherits the openai-compatible family
default (openai-chat -> /chat/completions), matching every sibling
openai-compatible builtin and the Legacy extension behavior.

Fixes #13003, fixes #10781
2026-08-08 02:06:46 +02:00
Saoud Rizwan 4eb7402334 fix(cli): render MCP tool result text instead of escaped JSON in TUI (#13066)
* fix(cli): render MCP tool result text instead of escaped JSON in TUI

MCP tools return {content: [{type: "text", text}]} which
extractFullOutputText JSON-stringified, escaping newlines into one giant
line that word-wrapped across the whole terminal and never triggered the
line-based collapse. Extract the text parts with real newlines so the
existing collapse works.

Fixes #13038

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

* fix(cli): keep placeholders for non-text blocks in mixed MCP results

Addresses Greptile review on #13066: text-only filtering silently
dropped image/resource/audio blocks from mixed MCP content. Render them
as [type] placeholders instead.

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

* fix(cli): surface non-text MCP block metadata in TUI output

Extract embedded resource text, and include resource/resource_link URIs
and image/audio mime types in placeholders so expanded mixed MCP
results keep identifying metadata.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-07 17:05:22 -07:00
Saoud Rizwan ffb61a865f fix(mcp): give unconfigured stdio servers a 30s initialize budget (#13067)
The stdio MCP client gave servers without a configured `timeout` only
1.5 seconds to answer initialize before killing the process, so
slow-starting servers (e.g. Oracle SQLcl's JVM-based `sql -mcp`) could
never load and were silently skipped at session start.

Raise the default connect budget to 30s, in line with the startup
budget other MCP clients allow. A configured `timeout` still overrides
it in either direction, dead commands still fail fast through the spawn
error/exit path, and the newline -> Content-Length framing fallback is
unchanged.

Fixes #13035

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-07 16:49:28 -07:00
Bee d3616b96ae feat(desktop): route /team prompts through core runtime (#12372)
* feat(desktop): route /team prompts through core runtime

Rewrite desktop `/team` commands as structured user command blocks before sending them to the core runtime. Validate task input and respect the globally disabled Teams tool setting.

Remove legacy agent spawn and team enablement flags from session configuration, and add coverage for prompt rewriting and disabled-tool behavior.

* fix(desktop): preserve team tool defaults

* fix(desktop): display queued /team prompts as their slash form

Queued prompts are stored in their runtime form, so a queued /team
command showed its raw <user_command> envelope in the prompt queue chip
and edit textarea. Fold queue items through formatDisplayUserInput for
display; saving an edit re-resolves the slash form through the sidecar,
so the round trip is lossless.

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

* chore(hub): align builtin tool catalog flags with the desktop sidecar

The desktop sidecar pins enableSpawnAgent/enableAgentTeams when listing
the builtin tool catalog; the hub's parallel listing did not, so the two
would drift if the preset defaults ever change. Pin the same flags in
the hub and cross-reference the two call sites.

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

* chore(desktop): drop inert enableSpawn/enableTeams config leftovers

buildCoreSessionConfig no longer reads these keys, so remove the dead
schema fields, default-config initializers, and chat-test payload
entries. The chat-session regression test still sends them on purpose
to prove legacy flags cannot override the runtime's tool presets.

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

* fix(desktop): reject /team when the mode's tool preset disables teams

The /team guard only checked the global disabled-tools setting, but the
runtime resolves tool availability from the mode's preset, so a preset
without team tools (yolo) would still send the model a spawn-a-team
instruction it cannot act on. Resolve the teams catalog entry for the
session's mode and reject /team when it is unavailable, mirroring the
runtime's own availability logic.

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

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-07 16:45:49 -07:00
Bee 98e5458b09 feat(hub): centralize plugin settings and contributions (#12942)
* fix(desktop): plugin package names

* feat(hub): centralize plugin settings and contributions

* fix(hub): address plugin settings review feedback

* fix(settings): make plugin snapshots host-aware

* fix(core): make host plugin toggles atomic

---------

Co-authored-by: cline-cloud[bot] <cline-cloud[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-07 16:44:02 -07:00
Saoud Rizwan 031b8d94e1 fix(core): surface OAuth authorization for SSE MCP servers on 401 (#13050)
* fix(core): surface OAuth authorization for SSE MCP servers on 401

A 401 from an SSE MCP server never persisted authorizationRequired: the
fetch-boundary UnauthorizedError was consumed by EventSource and re-thrown
as a status-less SseError, so the instanceof check routed it to
markConnectionError and hosts never offered the OAuth connect action.

Give the SSE stream request a raw fetch so a 401 fails the connection with
the SDK's typed SseError(401), and recognize 401s across transports with a
single isMcpUnauthorizedError predicate at every detection site.

* style(core): apply biome formatting to MCP oauth changes
2026-08-07 16:42:43 -07:00
Saoud Rizwan e5bcba8ef9 fix(vscode): settle the turn phase when a mode switch aborts a running turn (#13063)
Toggling Plan/Act while a turn was streaming or waiting on a tool approval
aborted the turn but left the TurnStateTracker on its last live phase: the
aborted session's done event is fenced off as stale once the rebuild
unsubscribes it, so nothing ever settled the phase. The webview then kept
rendering that phase forever - an eternal Thinking spinner with the input
disabled (aborted while streaming), or dead Approve/Run Command buttons wired
to an approval that clearPending had already denied (aborted while awaiting
approval). Users experienced this as 'switched to act mode and nothing
happened / it never wrote the files'.

Mirror cancelTask: after aborting the turn for the mode change, append a
resume_task ask row and set the phase to resumable, so the footer offers
Resume Task with the input enabled in the new mode.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-07 16:41:13 -07:00
Saoud Rizwan 83182c0d96 fix(llms): retry mid-stream network interruptions before any model output (#13052)
* fix(llms): retry mid-stream network interruptions before any model output

* fix(llms): scale network retry backoff by network retry count, not shared attempt number
2026-08-07 16:36:27 -07:00
Saoud Rizwan 40ebd09dfa desktop: native-feel polish, render-path performance, and transition fixes (#13028)
* desktop: native-feel polish and render-path performance fixes

- Suppress the WebView browser context menu on app chrome (keep it for
  editable fields and active text selections)
- Make UI chrome unselectable app-wide; opt chat messages, markdown,
  code, diffs, and error banners back into text selection
- Contain overscroll so inner scrollers don't rubber-band the window
- Lazy-load Settings/Sessions/Onboarding/Diff views out of the entry chunk
- Memoize ChatInputBar and AgentHeader; stabilize their props in the chat
  pane so stream flushes only re-render the affected message bubble
- Stop refocusing the composer textarea on every keystroke (caret flicker)
- Cache slash commands across menu opens (stale-while-revalidate)
- Avoid rebuilding reversed message arrays and ask-question JSX per render
- Drop core info/debug console logging on the streaming hot path behind a
  cline:debug-logs opt-in; remove leftover [webview:delete] debug logs
- SearchCombobox (provider/model picker): Escape closes and restores focus
- Remove unused @vercel/analytics, recharts, embla-carousel deps and the
  unused chart/carousel UI components

* desktop: surface failed-turn errors instead of leaving the chat blank

On a failed run the runtime reports its error string in result.text.
The webview rendered that as an assistant bubble, which the canonical
history rehydration then wiped (the failed turn is never persisted),
so provider errors like a retired model id left the user staring at a
silently empty chat. Route failed-turn text to a persistent error-role
message added after rehydration instead.

* desktop: fade the welcome/conversation swap instead of hard-cutting

Sending the first message replaced the hero layout with the message
grid in a single commit, which read as a white flash. A 180ms enter
animation now plays when either side becomes visible; disabled under
prefers-reduced-motion.

* desktop: render new-chat panes instantly from the last catalog load

Clicking + remounts ChatThreadPane, which refused to render until the
provider catalog (a large fetch) and workspace list resolved again —
about a second of blank pane plus boot spinner on every new chat.
Seed remounts from a module-level snapshot of the last successful
load; the mount effect still refreshes both in the background.

* desktop: invalidate the provider-catalog snapshot with the cache

Seeding remounted chat panes from the last catalog load left a window
where a pane created right after a credential change could act on the
old keys. The snapshot now lives in the catalog module and is dropped
by invalidateProviderCatalogCache(), so credential edits force the
next remount to wait for fresh data.
2026-08-07 16:18:47 -07:00
Saoud Rizwan 7bc18f7e15 Bring back a copy button on turn-final response rows with a subtle header (#13051)
* Add subtle response header with copy button to completion and plan rows

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

* Add changeset for response header copy button

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

* Rename turn-final headers to Completed and Plan

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-07 16:12:43 -07:00
Bee fad8006730 feat(hub): add execution context for scheduled run reports (#12718)
* feat(hub): add execution context for scheduled run reports

Add human-readable headers, schedule metadata, durations, and lifecycle error context to cron run reports. Resolve file-based definitions to real paths while clearly identifying Hub-managed schedules stored in cron.db.

* fix cron report formatting edge cases

* Escape schedule titles in reports

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-07 15:57:33 -07:00
Saoud Rizwan 28f35a2ec8 fix(cli): harden tool input/output formatters against malformed payloads (#13048)
* fix(cli): harden tool input/output formatters against malformed payloads

Tool inputs cross the model/tool boundary and may not match their
TypeScript annotations (e.g. run_commands with { command: null }).
truncate() called str.replace() on such values, crashing the TUI with
'.replace is not a function' and making persisted sessions containing
the payload non-resumable, since hydration replays the same input
through formatToolInput().

Normalize untrusted values at the formatting boundary: truncate() now
accepts unknown and safely stringifies null/undefined/objects (including
circular structures and throwing toJSON), formatStructuredCommand no
longer returns non-string commands verbatim, and fetch_web_content
request summaries tolerate malformed entries.

Fixes #13036

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

* fix(cli): keep valid empty-string args in structured command summaries

Greptile review: filtering normalized args by truthiness also dropped
genuine empty-string argv entries, so summaries could show a different
argument list than the one executed. Filter only nullish entries before
normalization instead.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-07 15:53:01 -07:00
Saoud Rizwan 1e24807d1a fix(vscode): fall back to session cwd or Desktop for @-mention file search in empty windows (#12982)
* fix(vscode): fall back to session cwd/Desktop for @-mention search in empty windows

* fix(vscode): use the shared chat workspace as the no-folder fallback root
2026-08-07 15:38:43 -07:00
Saoud Rizwan a5f90e0d53 fix(core): pick up checkpoints when git is initialized mid-session (#13026)
ensureGitRepository cached a negative probe for the lifetime of the hook
instance, so a session started in a non-git folder never got checkpoints
even after the user ran git init. Cache only the positive answer and
re-probe otherwise; the probe runs at most once per user turn.
2026-08-07 15:25:11 -07:00
Saoud Rizwan adabfc6bd5 fix(desktop): treat signed-out state as a typed result instead of a command error (#12976)
* fix(desktop): treat signed-out state as a typed result instead of a command error

* fix(desktop): sign out when the organization balance fetch reports the typed signed-out result

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

* chore: retrigger checks after runner outage

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-07 15:06:45 -07:00
Saoud Rizwan 397d6f344f fix(desktop): resolve startup script-load SyntaxError and attribute webview errors to their source URL (#12974)
* fix(desktop): remove Vercel Analytics injection that breaks packaged webview startup

* fix(desktop): attribute webview uncaught errors to their source URL

* chore: drop unrelated claude-dev version bump from lockfile

* chore: retrigger checks after runner outage
2026-08-07 15:02:36 -07:00
Bee 8ca3068b73 refactor(desktop): update team tools component (#13047)
* refactor(desktop): update team tools component

* fix(desktop): address team tool review feedback
2026-08-07 22:58:06 +02:00
John Choi 6e6befdb65 fix(ui): remove nested tool output scrolling (#13043)
* fix(ui): remove nested tool output scrolling

* fix(desktop): avoid nested tool output scrolling

* chore(desktop): remove obsolete scroll utility

* fix(desktop): preserve multiline tool details
2026-08-07 13:44:23 -07:00
Haley Park 71536e55aa refactor(ui): introduce Cline-owned semantic color system (#12941)
* refactor(ui): introduce Cline-owned semantic color system

* refactor(desktop): adopt shared semantic theme roles

* refactor(ui): set 15px root and recalibrate xs/sm type scale

Scale rem steps so xs/sm stay 12/13px visually, and slightly lift dark-mode neutral-4.

* refactor(ui): align SearchCombobox with package type and hover tokens

Use host-safe cline-ui utilities and keep option font inheritance from CSS.

* fix(ui): use standard stroke-2 utility on approval spinner

* refactor(desktop): modernize shared UI primitives for Tailwind v4

Replace legacy arbitrary/has selectors with current utility syntax.

* refactor(desktop): bump chat chrome typography to text-sm

Keep composer controls and pickers on the shared sm type step.

* refactor(desktop): use max-w-344 for page frame content width

* chore(desktop): disable Next.js dev indicators

* chore: ignore desktop-app Cursor settings

* docs(pr): add before/after screenshots for #12941

* chore: retrigger checks

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-07 10:15:18 -07:00
Saoud Rizwan 7348ba1847 chore(desktop): release v0.0.10 2026-08-06 23:45:56 -07:00
Bee 3e96fc6112 feat(cli): add mcp uninstall command (#12985)
* feat(cli): add mcp uninstall command

* unit test wiring

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-06 19:40:45 -07:00
Saoud Rizwan d84d09c543 desktop: fix silent turn failures, message duplication, and stuck composer; add first-run setup guidance (#12984)
* desktop: fix silent turn failures, message duplication, and stuck composer; add first-run setup guidance

Findings from two full computer-use UX audits of the desktop app:

- Surface failed turns in the transcript: queued turns (incl. the first
  prompt of a fresh session) only signal errors via chat_done, which the
  UI previously ignored - sending a message with no credentials failed
  in complete silence. Failed turns now show an error message enriched
  with the latest core error log and a pointer to Settings -> Models.
- Fix duplicated user messages: a live send's optimistic user message
  was materialized a second time by the runtime's queued-prompt-start
  event.
- Fix composer stuck on 'Agent is working...': drop prompts from the
  local queue snapshot when they start, emit a fresh queue snapshot from
  the sidecar on pending_prompt_submitted, and double-check the server
  queue on turn completion.
- Add a 'Connect a model' notice on the welcome screen when no provider
  has credentials, with actions to reopen onboarding at the connect step
  or jump to model settings; it reacts live to credential changes.
- Add 'Get an API key' links for popular providers in onboarding and
  Settings -> Models (the catalog docUrl is never populated), and link
  the Cline dashboard from the Cline API key form.
- Explain what Cline is on the onboarding welcome step.
- Make the stop button visible (was 8px with no padding) and support
  Esc to stop; add Cmd/Ctrl+N (new session) and Cmd/Ctrl+, (settings).
- Remove leftover [webview:delete] console.error debug logging that
  surfaced an error badge after deleting a session.

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

* desktop: remove remaining delete debug logging in session history hook

The sidebar right-click delete path had the same leftover [webview:delete]
console.error instrumentation, which made the Next dev-mode issues badge
appear after every deletion.

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

* desktop: fix Biome a11y error in WelcomeSetupNotice

biome's lint/a11y/useSemanticElements errors on role="status" divs;
use the semantic <output> element (implicit status role) instead. This
was failing the repo's 'bun run lint'.

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

* desktop: count structured-config and keyless providers as connected

The welcome setup notice previously only recognized apiKey/OAuth
credentials, so users running Bedrock/Vertex (structured configValues)
or a deliberately enabled keyless local endpoint (e.g. Ollama) were
nagged to connect a model they already use. isProviderConnected now
also counts an enabled provider whose required config fields are all
filled, or an enabled provider that has no API-key field at all.

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

* desktop: keep re-key eligible when chat_done lands in the same batch as its prompt start

When a turn fails fast, chat_queued_prompt_start and chat_done can be
dispatched in one React batch. Clearing the outstanding-optimistic-
bubble registry synchronously in the chat_done handler ran before the
re-key updater enqueued by the prompt-start event, so the optimistic
bubble was appended a second time instead of re-keyed. Clear the
registry inside a state updater so it executes in event order after
the re-key. Caught by the queued-turn-failure regression test.

* desktop: make the queued-prompt re-key updater idempotent under StrictMode

React StrictMode double-invokes state updaters in dev. The
chat_queued_prompt_start re-key updater consumed the optimistic
bubble's id from outstandingOptimisticUserIdsRef on its first run, so
the second run against the same prev found no eligible candidate and
appended the same user message a second time (and, without a promptId,
makeId() minted a different id per invocation). Hoist the message id
out of the updater and remember which optimistic bubble each queued
message id re-keyed so a re-run reaches the identical result. The memo
resets alongside the outstanding set (error state, reset, hydration).

Root-caused with runtime instrumentation: the duplicate only appeared
on turns that exercised the queue-drain re-key path, and hydration
later collapsed it to one message because the duplicate never existed
in persisted state.

* desktop: preserve failure messages across post-send canonical hydration

Persisted history never contains UI-only error bubbles, so the two
post-send read_session_messages replacements in sendPrompt wiped the
failure explanation appended from chat_done ~40ms after it rendered
(confirmed with runtime instrumentation). Re-append the active
session's error messages after the canonical history. Includes a
regression test reproducing the chat_done-error-then-RPC-resolution
race.

* desktop: don't let an optional API-key field veto a connected provider

Greptile P1 follow-up: Bedrock's catalog entry carries an optional
apiKey field ('Optional Bedrock bearer token') alongside IAM/profile
authentication, and keyless local endpoints can also surface one — so
treating the mere presence of an apiKey field as proof of disconnection
kept nagging configured users. An enabled provider (the user
deliberately persisted settings for it) now counts as connected unless
a required config field is unmet; auth may legitimately live outside
the catalog (IAM, env vars, local endpoints). Brand-new users have no
enabled providers, so the first-run notice still shows for them.

* desktop: tighten credential-error guidance and stop re-pinning stale failure bubbles

* desktop: invalidate the shared provider catalog after settings OAuth login

Greptile P1 follow-up: runOAuthProviderLogin only updated the settings
view's local provider state, so the shared catalog cache and its
invalidation subscribers (the composer selector and the welcome
screen's 'Connect a model' notice) kept reporting the provider as
disconnected until an unrelated invalidation or a pane remount. Notify
the shared cache on successful OAuth login, like the account view and
the API-key save path already do.

* desktop: clear the remembered core error on turn end, reset, and hydration

Greptile flagged that turn-start events are the only thing clearing
lastCoreErrorBySessionRef, and websocket events are not replayed: a
transport interruption that drops a turn's start event lets a later
detail-less failure resurrect an earlier turn's error. The remembered
error belongs to exactly one turn, so clear it whenever a turn ends
(chat_done, any reason) as well as on reset() and history hydration.
Regression test covers the dropped-start-event sequence.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-06 19:25:42 -07:00
Bee 6f7f817d63 feat(core): support pre-registered OAuth clients for remote MCP (#12983)
* feat(core): support pre-registered OAuth clients for remote MCP

* fix(core): invalidate tokens when OAuth client changes

* fix(core): preserve compatible MCP OAuth sessions

* fix tests

* feat(desktop): wire mcp oauth in desktop

* UI update

* fix(core): reject stale mcp oauth callbacks

* fix(mcp): handle invalid settings and preserve state

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-06 18:58:52 -07:00
Bee c18d9478ba feat(cli): use saved provider settings for schedules (#10667)
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-08-06 17:34:40 -07:00
Ara 574b8eb45e fix(llms): use configured fetch for Vertex ADC refreshes (#12981) (#12991) 2026-08-06 10:07:06 -07:00
Saoud Rizwan 81cce3d70e chore(vscode): prepare 4.1.6 release 2026-08-06 00:47:54 -07:00
Saoud Rizwan e1352fa709 chore(cli): release v3.0.51 2026-08-06 00:28:37 -07:00
Saoud Rizwan 394fb04518 chore(sdk): release v0.0.71 2026-08-06 00:14:21 -07:00
Saoud Rizwan f1aebbfd5a feat(llms): surface meta/muse-spark-1.2-contributor for the Cline provider (#12995)
* chore(llms): regenerate model catalog from models.dev

* feat(llms): surface meta/muse-spark-1.2-contributor for the Cline provider

* test(llms): guard Vercel-only Cline model allowlist
2026-08-06 00:04:16 -07:00
Saoud Rizwan 543dd0d818 fix(telemetry): attribute agent.run sdk.error events to the active model (#12972)
* fix(telemetry): attribute agent.run sdk.error events to the active model

* fix(telemetry): strip undefined values from sdk.error properties
2026-08-05 17:51:06 -07:00
Saoud Rizwan 1f2cbbeb9f chore(vscode): prepare 4.1.5 release 2026-08-05 14:04:04 -07:00
Saoud Rizwan b1a89156d6 feat(vscode): explain when a free model promotion ends (#12970)
* feat(vscode): explain when a free model promotion ends

Once a free promotion ends, the cline-free/ model is removed from the
catalog and the backend answers 'model not found' to requests against it.
The CLI has shown a dedicated 'Free model promotion ended' banner for this
since #12593; the extension instead rewrote the answer into generic
model-not-found guidance with no model-picker offramp.

Detect the case in the host where the active model id is known
(reshapeErrorForWebview, fed by a new MessageTranslatorState model-id
source), stamp the payload with a cline_free_promotion_ended code, and
render a dedicated card in the webview with a button into the model
picker. Classification is gated on the cline-free/ prefix so ordinary
model-not-found errors keep their generic path, and it runs before the
auth branch since the 404 status falls inside the generic auth range.

* fix(vscode): prefer the live task model over session-start metadata

A mid-task model-only switch updates the running session's model in place
(updateActiveSessionModel) and refreshes the task API shim, but never
touches the session's startConfig/manifest. Preferring the session-start
snapshot could therefore misclassify after such a switch: a genuine
retired-model 404 would miss the promotion-ended card, and the reverse
switch could show it for the wrong model. Provider switches restart the
session, so both sources agree there; the shim starts as "unknown"
(filtered out), so fresh sessions still resolve through start metadata.
2026-08-05 14:00:09 -07:00
Bee 1d7d9ce5e2 feat(llms): add portable reasoning resolution for AI SDK providers (#12946)
* feat(llms): add portable reasoning resolution for AI SDK providers

Introduce resolvePortableReasoning to map gateway reasoning requests
(effort levels, enabled/disabled flags) to the AI SDK's top-level
reasoning setting, applying it in buildAiSdkStreamConfig for supported
providers including Ollama.

- Defer exact token budgets to provider-specific options
- Omit reasoning when the caller expresses no explicit intent
- Replace manual provider-specific thinking overrides (e.g. Anthropic
  budget clamping, Moonshot/OpenAI-compatible toggles) with the
  portable reasoning path where applicable
- Add tests covering effort mapping, budget passthrough, and provider
  stream config integration

* fix(llms): prioritize explicit reasoning disable

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-05 12:17:49 -07:00
Mikołaj Kondratek 78b7c3d8ac fix(desktop): stop rendering the first chat message twice (#12779)
* fix(desktop): dedupe chat_queued_prompt_start emitted for the same prompt

PendingPromptService.drain() emits a pending_prompts snapshot (head
removed) and a pending_prompt_submitted event back-to-back for the same
prompt. The sidecar translated both into chat_queued_prompt_start, so
the webview rendered the user's message twice until the chat was
re-hydrated from history. Track the last announced prompt id per live
session and emit the start chunk once.

* fix(desktop): re-key optimistic user bubble when the runtime queues the prompt

The send path renders an optimistic user bubble for prompts dispatched
while the session is idle, keyed by a random id. When the runtime
routes that prompt through its pending queue (e.g. during session
startup), the queued-prompt-start event appended a second bubble under
queued_user_<promptId> — the same message rendered twice until the
chat was re-hydrated from history. Re-key the trailing optimistic
bubble to the event's id instead of appending.

* fix(desktop): re-key only outstanding optimistic bubbles on queued prompt start

Review follow-up: matching by content alone could swallow a new queued
prompt that repeats the text of a message left at the transcript tail
by an earlier cancelled/failed turn. Track in-flight optimistic bubble
ids explicitly (registered on optimistic append; cleared on re-key,
turn end, error, and history hydration) and only re-key those.
2026-08-05 14:49:05 +02:00
Saoud Rizwan d626cfb0b5 chore(vscode): prepare 4.1.4 release 2026-08-05 03:03:51 -07:00
Saoud Rizwan e14f354c59 chore(desktop): release v0.0.9 2026-08-05 02:29:46 -07:00
Saoud Rizwan 41ba332f0a chore(cli): release v3.0.50 2026-08-05 02:16:56 -07:00
Saoud Rizwan 6997fae815 chore(sdk): release v0.0.70 2026-08-05 01:59:51 -07:00
Saoud Rizwan 5594512eb0 fix(vscode,cli): recoverable agent errors must not kill a turn that completes with a plan (#12953)
* fix(core): don't count plan-mode guard-blocked commands as model mistakes

The plan-mode command guard (#12906) rejects file-editing run_commands
calls with a tool error. The orchestrator counted that error as a failed
tool call, so a turn whose only tool call was guard-blocked fed the
MistakeTracker, which emits a recoverable "error" AgentEvent
("1 tool call(s) failed: [run_commands] ...").

Hosts render that event as a failed turn. In the VS Code extension the
turn ended in the "error" phase (Retry / Start New Task footer), the
final plan text was never retagged to plan_completion_result, and
toggling to Act therefore rebuilt the session without the auto-continue
send - the toggle appeared to do nothing and the presented plan was
never acted on. In the CLI TUI the same event flipped the footer to
idle mid-turn.

A guard rejection is deliberate session policy, not a model mistake:
the run continues and the model is expected to fold the change into
its plan. Tag the guard error with a stable marker sentence, expose
isPlanModeBlockedCommandError, and skip the failed-tool bookkeeping for
matching results so no mistake is recorded and no error event is
emitted. Repeated blocked attempts are still bounded by loop detection
and maxIterations.

* docs(core): flag plan-mode guard error string matching for typed skip channel

FIXME on isPlanModeBlockedCommandError: recognizing guard rejections by
sniffing the error text is brittle. The intended replacement is a typed
skipSource/skipCode on the tool-finished runtime event so the
orchestrator (and the VS Code approval-denial suppression) can identify
skipped tools structurally instead of via string matching.

* Revert core mistake-counting change for plan-guard blocks

A model attempting a file-editing command in plan mode is disobeying
its instructions - that IS a model mistake, and the MistakeTracker
should keep counting it (it is the brake that stops weak models from
flailing at blocked commands indefinitely). The real bug is host-side:
a recoverable mid-turn mistake must not kill a turn that afterwards
completes with a presented plan. The follow-up commit fixes that in
the hosts instead.

* fix(vscode,cli): treat recoverable agent errors as in-run notices, not turn outcomes

The MistakeTracker emits a recoverable error event for every recorded
mistake while the run continues - e.g. a plan-mode guard-blocked
run_commands call as the turn's only tool call. Both hosts treated any
error event as terminal:

- The VS Code translator cleared the pending completion retag, set
  errorSeen (turn phase "error": Retry / Start New Task footer), marked
  the turn complete, and rendered the error recovery UI. A plan turn
  that recovered from the mistake and completed cleanly therefore never
  produced plan_completion_result, so togglePlanActMode's planPresented
  check failed and switching to act mode rebuilt the session without
  the auto-continue send - the toggle appeared to do nothing.

- The CLI TUI flipped isRunning/isStreaming to idle mid-turn, so the
  footer lied about the still-running turn.

Recoverable errors are informational: the turn's outcome is decided by
how it actually ends (done/error). VS Code now logs them and keeps them
out of the chat (the tool failure is already shown inline on its tool
row, and provider-failure telemetry already ignores recoverable events
for the same reason); the CLI keeps its running state and surfaces them
only in verbose mode, as it already did for display. Genuine run
failures carry recoverable: false and keep the existing error UI.
2026-08-05 01:02:23 -07:00
Saoud Rizwan 6712d43c69 Revert "fix(llms): flatten top-level tool schema unions before sending to pro…" (#12950)
This reverts commit 21edad82a6.
2026-08-04 21:11:33 -07:00
Bee bd27d9c41b feat(desktop): session source filtering (#12943)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-04 21:01:40 -07:00
Saoud Rizwan 21edad82a6 fix(llms): flatten top-level tool schema unions before sending to providers (#12948)
Anthropic (and several providers OpenRouter fans out to) rejects any tool
whose input_schema has oneOf, allOf, or anyOf at the top level, failing the
whole request with:

  tools.N.custom.input_schema: input_schema does not support oneOf, allOf,
  or anyOf at the top level

MCP servers commonly advertise tools whose input schema is a union of object
shapes (e.g. generated from a Zod union), so one such tool bricked every
turn of the session. Merge union branch properties into a single object
schema at the provider boundary; tools still validate their real input
shapes in execute().

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 20:56:39 -07:00
Tomás Barreiro 2f58bbe4ed Add Auto Approval to ACP (#12897)
* Add Auto Approval to ACP

* Update apps/cli/src/acp/auto-approve.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update apps/cli/src/acp/auto-approve.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-04 20:54:39 -07:00
Saoud Rizwan ddfb67515b fix(desktop): inline telemetry config into the packaged sidecar binary (#12925)
* fix(desktop): inline telemetry config into the packaged sidecar binary

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

* fix(desktop): reject non-http OTLP endpoints in the telemetry selfcheck

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 20:43:01 -07:00
Saoud Rizwan 472f9c88c5 Add plan-mode command blocklist to run_commands (#12906)
* Add plan-mode command blocklist to run_commands

Plan mode kept run_commands available (needed for read-only
investigation) but relied on prompting alone to prevent file edits,
and weaker models routinely ignore that. Add a hard guard in
@cline/core's createShellTool that inspects each command before
execution and rejects file-editing constructs with a plan-mode tool
error instead of running them.

The guard is a quote/heredoc-aware scan that blocks file-manipulation
commands (rm/mv/cp/tee/touch/...), in-place editors (sed -i, perl -i,
gawk -i inplace, sort -o), output redirection to files (allowing /dev
sinks and /tmp for the documented output-capture pattern), mutating
git subcommands, package-manager installs, find -delete/-exec, and
nested command strings (sh -c, eval, sudo, xargs, ...). Windows and
PowerShell equivalents are covered too.

Enabled via a new blockFileEditingCommands flag on DefaultToolsConfig,
set by the plan tool preset (CLI and core runtime) and plumbed through
the VS Code extension's custom run_commands tool from the session mode.
The tool description and PLAN_MODE_INSTRUCTIONS now state the hard
block so models are forewarned.

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

* Simplify plan-mode command guard to a plain blacklist

Replace the char-by-char shell tokenizer (heredoc queues, process
substitution, recursion into sh -c/eval, find -exec analysis) with a
simple scan: mask quoted text/heredoc bodies/escapes/comments so they
cannot false-positive, split on shell separators, and compare the
leading command word of each part against flat blacklists (commands,
mutating subcommands, in-place edit flags), plus one redirect check.
Quoted nested commands (bash -c 'rm x') are a documented false
negative. Also drop the guard from the package's public exports; it
is internal to createShellTool.

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

* Move plan-mode command guard into a built-in beforeTool hook

Review feedback (abeatrix): command blocking is session policy, not
shell-executor configuration. Replace the blockFileEditingCommands
flag threaded through preset -> tool config -> VS Code host with a
core extension registered by the runtime builder for plan-mode
sessions. The beforeTool hook intercepts every run_commands tool in
the runtime - the SDK builtin, host replacements like the VS Code
terminal tool, and delegated sub-agents - and rejects file-editing
calls with the plan-mode error before tool policy and user approval,
so users are no longer prompted to approve a command that would only
fail. All VS Code wiring for the guard is removed.

Also adds block telemetry (sdk.plan_mode_command_blocked with the
blocked construct, never raw command content), per review.

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

* Address review feedback on the plan-mode command blacklist

False positives (mkondratek):
- perl -Ilib / uppercase value-taking flags no longer match the
  in-place check; the flag cluster must end at a lowercase i
  (sed -Ei still blocked)
- awk inplace detection is tied to the -i/--include flag instead of
  matching the substring anywhere (filenames like inplace-notes.txt
  no longer trip it)
- read-only git forms allowed: stash list/show, worktree list,
  submodule status/summary, and any git subcommand with --help/-h
- arithmetic expansion (1) is masked before the redirect scan

Hardening and coverage (mkondratek, dominiccooney):
- temp-path redirect allowance rejects .. traversal (/tmp/../...)
- Windows gets a temp escape hatch: %TEMP%/%TMP%/$env:TEMP redirect
  targets are allowed and the block error mentions it
- curl -o/-O/--output/--remote-name and wget downloads blocked
  (--spider and -qO- stdout forms stay allowed)
- python -m pip resolves to the pip subcommand check
- unambiguous PowerShell aliases (mi, ri, cpi, rni, ac, clc) plus a
  case-variant test
- more package managers: winget, nuget, gem, composer, dotnet add,
  go install/get; bare classic yarn blocked again
- find -exec/-execdir/-ok chains and xargs -I {} placeholders are
  checked for mutating commands

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 20:35:54 -07:00
Saoud Rizwan f77d0930ba fix(llms): preserve models.dev reasoning options in generated catalog so adaptive-era Claude models never get manual thinking (#12908)
* chore(llms): regenerate model catalog from models.dev to pick up reasoning options

The baked fallback catalog was last regenerated before toModelInfo started
mapping models.dev reasoning_options into ModelInfo.reasoningOptions, so it
carried no reasoning metadata. Whenever the live models.dev fetch fails or a
model resolves from the baked catalog, adaptive-era Claude models (4.6+/5.x)
fell through the missing-reasoningOptions path to Anthropic manual thinking
and the API rejected the request with 'thinking.type.enabled is not
supported'.

This regen also picks up upstream models.dev drift; test expectations that
hardcoded stale catalog values (GLM 5.2 context window, OpenRouter GLM 4.7
reasoning controls, Vercel AI Gateway Qwen 3.6 Plus budget controls) are
updated to the current published values.

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

* fix(llms): infer adaptive thinking for adaptive-era Claude ids when catalog reasoning options are missing

Claude 4.6+ and 5.x models reject the manual thinking wire shape
(thinking.type 'enabled') on the Anthropic API. When a model resolves
without reasoningOptions metadata (offline baked catalog before the regen,
or user-typed unlisted ids such as claude-opus-4-6:1m), the reasoning
policy previously fell through to anthropic-manual and every
reasoning-enabled request failed with a hard API error.

Add isClaudeAdaptiveEraModelId as a narrowly scoped id fallback (name-first
Claude ids with version 4.6+ or 5.x, plus the Fable line) and use it in the
missing-reasoningOptions branch of resolveAnthropicReasoningRequestPolicy.
Genuinely old or unknown Claude-compatible ids keep the manual default,
which remains the safe shape for third-party Claude-compatible endpoints.

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

* fix(llms): prefer adaptive thinking over manual when a model advertises an effort control

A numeric reasoning.budgetTokens (e.g. a thinkingBudgetTokens setting
migrated from the legacy extension) used to force the anthropic-manual
policy whenever the model advertised a budget_tokens control. Claude 4.6+
models advertise both effort and budget_tokens on models.dev but reject
thinking.type 'enabled' on the Anthropic API, so those requests failed.
Effort now wins: adaptive is selected and the numeric budget is ignored.
Budget-only models (Sonnet 4.5 and older) keep honoring explicit budgets
via the manual shape.

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

* test(llms): guard baked catalog reasoning options for adaptive-era Claude models

Resolve adaptive-era Claude models through the generated (offline fallback)
catalog and assert their entries carry effort reasoning options that the
Anthropic reasoning policy resolves to adaptive thinking.

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

* test(core): update GLM 5.2 context window to current models.dev value

The catalog regen picked up upstream drift: models.dev now publishes a
1,000,000-token context window for zai/glm-5.2 (was 1,040,000).

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

* revert(llms): drop the Claude id-based adaptive-thinking fallback

Keep the fix surface minimal: the catalog regen covers every model
models.dev lists (the overwhelming share of the production failures), and
the effort-over-budget policy covers listed models that advertise both
controls. Unlisted id variants (e.g. claude-opus-4-6:1m) keep the
pre-existing manual fallback rather than introducing id-version parsing in
model-facts.ts; if they appear in models.dev the catalog picks them up
automatically.

This reverts commit 6d725b1ecd7c896a08c3c58dbb37afeaf34bb31e, keeping the
regenerated catalog and the effort-precedence change.

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

* fix(llms): default unknown Claude ids to adaptive thinking when catalog options are missing

Reintroduce the id-based fallback with the forward-compatible policy the
ecosystem converged on (vercel/ai#17804 for @ai-sdk/anthropic's capability
lookup; opencode's transform.ts after repeated allowlist misses for
opus-4.7, sonnet-5, and opus-5): when catalog reasoningOptions metadata is
unavailable, treat unrecognized Claude ids as newer than the known model
list and use adaptive thinking, since new Claude releases reject the manual
wire shape. Known legacy families (Instant, 2.x, 3.x, and name-first 4.0-4.5)
keep manual, as do non-Claude Anthropic-compatible ids and unknown Claude
ids carrying an explicit numeric budget (a custom-endpoint signal).

Unlike the earlier reverted allowlist (which defaulted unknown ids to
manual), this fails open for future models: claude-opus-4-6:1m-style
variants and next year's Claude work without a code change.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 20:34:13 -07:00
Saoud Rizwan d1462cf919 fix(llms): retry empty model turns on all providers, not just Ollama (#12927)
* fix(llms): retry empty model turns on all providers, not just Ollama

Production telemetry shows 'Model returned empty response' hard failures
on hosted backends (openrouter, cline, openai-compatible endpoints), not
just local Ollama — 46 tasks / 120 events in 24h on the SDK extension vs
~0 on legacy, which has its own empty-response fallback.

The retry-empty-response middleware already existed but was wired only
into the Ollama vendor. Move the wrap to the central AI SDK composition
point (createAiSdkProvider in ai-sdk.ts), where every vendor's model is
constructed, so all providers get it: retry only when a turn produced
genuinely nothing (no text, no reasoning, no tool call), tool-call-only
turns are never retried, non-empty turns stream through live, and error/
token-limit finishes pass through unchanged. Vendors can opt out or tune
attempts via ProviderFactoryResult.retryEmptyResponses. The agent
runtime's loud failure after persistently empty turns is unchanged.

* docs(sdk): drop changelog edit — release commits own the changelog

v0.0.69 is already published; its section must not be edited
retroactively. The next release commit will describe this change.

* ci: re-trigger checks (flaky Windows runner test timeouts)

* ci: re-trigger checks (flaky Windows runner test timeouts)

* fix(llms): classify stream parts exhaustively, buffer retry attempts, aggregate usage

Review follow-up (dominiccooney): the retry predicate and the response
parser were two independent, incomplete interpretations of the
LanguageModelV4StreamPart union, and rejected attempts leaked structural
parts and dropped billable usage.

- stream-part-classification.ts is now the single exhaustive boundary:
  every part is converted content, explicitly unsupported output,
  structural metadata, stream-start, finish, or error, with a never
  check so new AI SDK part types fail compilation. Retry eligibility
  derives from it: only turns with no output at all are retried;
  unsupported-but-real output (custom, reasoning-file, source,
  provider-executed tool-result) is never retried.
- Generated file parts are converted end to end: emitAiSdkEvents emits
  a new file AgentModelEvent and the agent runtime assembles it onto
  the assistant message (image part for image/*, file part otherwise),
  so a file-only turn is no longer an empty message. The legacy
  ApiStream bridge explicitly skips file events (no chunk type).
- Each retry attempt is buffered until it proves non-empty (first
  output or error part), so discarded attempts leak nothing — one
  retried request produces one clean stream with exactly one
  stream-start.
- finish.usage from discarded attempts is aggregated field-by-field
  (cache and reasoning detail included) into the emitted finish, so a
  three-request turn reports three requests' worth of tokens.
2026-08-04 20:33:06 -07:00
Saoud Rizwan 49d33aa793 fix(vscode): show cwd-relative tool paths in the chat view (#12900)
* fix(vscode): show cwd-relative tool paths in the chat view

The SDK message translator copied the model's absolute file paths straight
into the ClineSayTool messages, so chat cards like "Cline wants to read this
file" showed full absolute paths. Relativize them against the task's cwd for
display (classic getReadablePath behavior: relative inside the cwd, basename
for the cwd itself, absolute when outside), including apply_patch's
"*** Update File:" markers which DiffEditRow parses for its headers.

Also restores the readFile card's click-to-open target by setting content to
the absolute path, matching the classic extension.

* refactor: apply display-path relativization as a single ClineSayTool transform

Instead of threading cwd through every case of sdkToolToClineSayTool, leave
the tool mapping untouched and apply one toDisplaySayTool transform (with a
filesystem-path tool whitelist) at the points where tool cards are emitted.
Same behavior, much smaller footprint; MCP/unknown tools keep their exact
prior behavior.

* fix: keep absolute readFile open-target untouched on Windows; match '..' as whole segment

path.resolve(cwd, absPath) rewrites a drive-less absolute path onto the
current drive on Windows, breaking the readFile card's click-to-open target
(and the tests asserting it). Guard with path.isAbsolute instead.

Also match '..' only as a whole path segment in toDisplayPath so an in-cwd
entry literally named '..config' is not misclassified as outside the cwd
(greptile P1).

* fix: keep Desktop-fallback paths absolute; relativize '*** Move to:' destinations

When VS Code has no workspace open, getWorkspaceRoot() falls back to the
Desktop; classic getReadablePath deliberately keeps full absolute paths in
that case so the user can see where operations occur. Restore that guard in
toDisplayPath.

Also enroll PATCH_MARKERS.MOVE in relativizePatchPaths so a rename renders
both source and destination relative (covers the split-patch path too).
2026-08-04 20:03:07 -07:00
Saoud Rizwan 9b5dcc6405 Fix Bedrock prompt caching: emit Converse cachePoint markers instead of anthropic cache_control (#12928)
* Fix Bedrock prompt caching: emit Converse cachePoint markers instead of anthropic cache_control

The Bedrock provider manifest routed prompt caching through the
anthropic-cache-control format, so requests carried cache_control
provider options that @ai-sdk/amazon-bedrock silently drops - its
Converse message converter only reads providerOptions.bedrock.cachePoint.
Bedrock never received a cache checkpoint, cacheRead/cacheWrite were
always 0, and a stray top-level cache_control field leaked into the
Converse request body.

Adds a bedrock-cache-point prompt-cache format that attaches a
message-level cachePoint marker to the last user message, which the
converter appends as a cachePoint content block, caching the whole
prefix up to it.

Fixes #12913

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

* Format gateway.test.ts assertion

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 20:00:16 -07:00
Bee f2ac1ef10a fix(desktop): show skills in slash command menu (#12894)
* fix(desktop): show skills in slash command menu

* fix(core): normalize runtime slash command names

* fix(core): disambiguate colliding slash commands

* fix(core): preserve same-kind slash commands

* fix(core): stabilize colliding slash command aliases

* fix(core): avoid slow runtime command regex

* fix(core): remove quadratic hyphen trim

* fix(core): prefer skills over workflows on slash command collisions

Workflows are effectively deprecated in favor of skills, so when a
workflow's normalized name collides with a skill the skill now owns the
token and the workflow is dropped. This removes the collision
qualification machinery (-skill/-workflow/-hash aliases), which silently
renamed established CLI and VS Code command tokens, and removes the
duplicate-token throw that sat in the CLI send path, the hub snapshot
capability, and the desktop list_user_instruction_configs command.
Same-kind collisions resolve to the first entry of the deterministic
(name, id) sort, stable across discovery order.

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

* fix(vscode): resolve typed workflow filenames through record ids

Name normalization broke the legacy /my-workflow.md fallback for
workflows renamed via frontmatter: the configured record name (e.g.
"Ship It") no longer compares equal to the normalized command token
("ship-it"), so a typed filename stopped expanding. Match the discovered
record to its runtime command by the stable record id instead, keeping
the canonical-name comparison as a fallback for callers that pass
records without ids.

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

* fix(core): normalize snapshot names in hub slash command proxy

The hub-side proxy normalizes the typed token but compared it against
snapshot command names verbatim. Snapshots served by older clients carry
raw configured names (e.g. "Ship It"), which could previously exact-match
typed input and would now never match. Normalize both sides of the
comparison so mixed-version hub setups keep resolving.

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

* fix(desktop): expand slash commands in the sidecar send path

Selecting a skill or workflow from the desktop slash menu inserted the
token but the sidecar dispatched it verbatim, so the model received
literal text like '/publish-ui write docs' instead of the configured
instructions. handleSend now expands a leading runtime slash command via
the core user-instruction service before dispatch (mirroring the CLI's
buildUserInputMessage), keeping the raw token as the session's display
prompt. Built-in webview commands (/fork, /team) and unknown tokens pass
through unchanged, and discovery failures fall back to the raw prompt.

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

* fix(desktop): expand slash commands when editing queued prompts

Editing a pending prompt stored the raw slash token, which the runtime
later delivered to the model unexpanded — only the initial send path
went through expandRuntimeSlashCommand. handleUpdatePendingPrompt now
expands a leading skill/workflow token before persisting the update,
matching the enqueue behavior in handleSend.

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

* fix(core): preserve Unicode letters in slash command tokens

Normalization stripped all non-ASCII characters, so a skill named 发布
got an unrelated generated token while typing /发布 could never resolve —
a regression from pre-normalization behavior where the exact name
matched. Keep Unicode letters and numbers in normalized tokens and only
collapse whitespace and symbol runs into hyphens.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-04 19:59:05 -07:00
Saoud Rizwan 10da3bf5d6 fix: emit AI SDK 7 image shapes and correct the claude-code peer range (#12901)
* fix(shared): emit AI SDK 7 file parts for images

formatMessagesForAiSdk still built the retired shapes: user images as
{type:'image'} message parts and tool-result images as {type:'image-data'}
content parts. AI SDK 7 auto-migrates both at runtime, but logs a
DeprecationWarning through process.emitWarning on every image-bearing
request, and the shims are slated for removal in the next major.

Emit the canonical shapes instead: {type:'file', data, mediaType} for
user images and {type:'file', data:{type:'data', data}, mediaType} for
tool-result media. mediaType is required on file parts, so URL-backed
images without a known type use the bare 'image' top-level segment,
which AI SDK 7 resolves per provider.

* fix(llms): allow the AI SDK 7 major of ai-sdk-provider-claude-code peer

The AI SDK 7 upgrade moved the ai-sdk-provider-claude-code
devDependency to ^4 but left the peer range at ^3.4.3, so consumers
resolving the peer would install the AI SDK 6 (Provider V3) major.
Align the peer range with the version the package is built against.
2026-08-04 19:53:51 -07:00
Saoud Rizwan 0034efe48c fix(llms): send max_completion_tokens for reasoning models on OpenAI-compatible endpoints (#12902)
* fix(llms): send max_completion_tokens for reasoning models on OpenAI-compatible endpoints

* fix(llms): require leading boundary in gpt-5 model-id pattern

* docs(llms): add maintenance notes to reasoning-era model-id patterns
2026-08-04 19:42:44 -07:00
Saoud Rizwan 64993e78d5 fix(llms): substitute image content for models without image support (#12903)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 17:34:44 -07:00
Saoud Rizwan 06f31f2821 fix(llms): route Bedrock foundation models through geo inference profiles (#12926)
* fix(llms): route Bedrock foundation models through geo inference profiles

AWS Bedrock offers no on-demand throughput for newer foundation models;
they must be invoked through an inference profile. The SDK Bedrock vendor
passed model ids through unmodified, so every request with a bare modern
model id (e.g. anthropic.claude-sonnet-4-6) failed with "Invocation of
model ID ... with on-demand throughput isn't supported".

Resolve the wire-level model id in the Bedrock vendor: honor the existing
useCrossRegionInference / useGlobalInference settings (already plumbed
through provider config but previously ignored), and auto-prefix bare ids
of models known to have no on-demand throughput so they work without the
toggle. Ids that are already profile-prefixed, ARNs, and custom-model
configurations are never rewritten; unknown regions fall back to the raw
id. Country profiles (jp./au.) are preferred over apac. where the model
catalog shows AWS ships them.

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

* fix(llms): future-proof Bedrock profile-required model patterns

Match Anthropic tier-first naming generically (excluding the frozen
legacy claude-3-*/claude-v2/claude-instant naming schemes) instead of
enumerating tier names, so future profile-only Claude tiers work without
pattern-list updates. Also cover the profile-only Amazon Nova 2 series.

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

* fix(llms): gate Bedrock geo profiles on catalog availability

Address review feedback on inference-profile resolution:

- Never manufacture apac. (or other unconfirmed) profile ids: prefer a
  catalog-confirmed variant among the region's candidates (jp./au./apac.),
  and otherwise keep the raw id so AWS returns the actionable on-demand
  error instead of "provided model identifier is invalid". Profile-only
  models still fall back to us./us-gov./eu. prefixes, where AWS reliably
  ships geo profiles for such models.

- Drop the customModelBaseId short-circuit: legacy migration copies the
  base id without the custom-selected flag, so its presence must not
  disable profile routing for a normal catalog model. Custom/provisioned
  ids stay raw on the cross-region path because no catalog variant can be
  confirmed for them, and ARN-based custom models were already passed
  through.

Adds a per-region wire-id table test, an injected-catalog apac test, and
a stale-customModelBaseId regression test.

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

* fix(llms): require catalog confirmation for every Bedrock geo profile

Remove the us./us-gov./eu. pattern fallback: AWS documents
inference-profile availability per model and geography, so no geographic
prefix is assumed valid without a catalog-confirmed variant (the catalog
had bare amazon.nova-lite/micro/pro ids with no geo variants, which the
fallback would have rewritten to unconfirmed eu./us. ids). The pattern
list now only gates eligibility for automatic routing; the catalog
always picks the actual prefix, and the raw id is preserved when no
variant is confirmed.

Adds boundary tests asserting pattern-matched models without confirmed
variants stay raw (with and without cross-region inference), plus
injected-catalog positive tests for us-gov. and future tier-first ids.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 17:34:13 -07:00
Saoud Rizwan 71e6b44ef7 Consolidate plugin display-name resolution into @cline/shared (#12905)
* Fix installed plugins all displaying as "index" in the desktop app

Hoist getPluginDisplayName (nearest-ancestor package.json name with
basename fallback) into @cline/shared storage paths, re-export it via
@cline/core, and replace the duplicated copies in cline-hub, the CLI
TUI, and VS Code marketplace helpers. Fix the desktop sidecar and
'cline config plugins', which still named plugins by entry-file
basename, so package-backed installs showed up as "index".

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

* Use shared getPluginDisplayName in desktop sidecar after #12933

Merging main brought in PR #12933, which fixed the desktop plugin
naming with another local copy of the helper. Drop that copy in favor
of the shared @cline/shared implementation this branch introduces, and
remove the node:path imports it needed.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 17:25:36 -07:00
Saoud Rizwan 0bcd602150 fix: task with attachments not resetting on New Task click (#12924) (#12937)
The webview posts an optimistic say:'task' message carrying the user's
images/files, and only clears it once an identical authoritative message
arrives from the extension. emitInitialTaskMessage omitted attachments,
so the optimistic copy was never confirmed and withPendingUserMessage
kept re-injecting the old task into the transcript even after New Task
cleared it - leaving the chat permanently stuck on the previous task.

- Include images/files on the authoritative initial task message so the
  optimistic pending copy is confirmed and cleared as designed.
- Defensively drop any unconfirmed optimistic message in startNewTask so
  an explicit New Task click always yields a clean slate.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 17:02:27 -07:00
Saoud Rizwan d759f4c646 fix(cli): track external git branch changes in the TUI status bar (#12930)
* fix(cli): track external git branch changes in the TUI status bar

The branch shown below the prompt was read once at startup and only
refreshed after an agent turn, so checkouts made from another terminal
or an editor left the TUI showing a stale branch (#12911).

Watch the repo's git dir for HEAD changes (git replaces HEAD via
rename, so a directory watch is used) and refresh the status bar
immediately, with a slow 5s poll as a fallback for filesystems where
fs.watch is unreliable. State updates are skipped when nothing changed
to avoid needless re-renders.

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

* refactor(cli): replace subprocess polling with stat-based HEAD backstop

Drop the unconditional 5s git-subprocess poll from useRepoStatus. The
fs.watch directory watcher stays for instant updates where the runtime
delivers HEAD events, but Bun on Linux drops them, so add fs.watchFile
on the HEAD file as the backstop: one in-process stat() every 2s that
only triggers git subprocesses when HEAD actually changed. Verified in
the Bun-run TUI that external checkouts show up within ~2s.

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

* refactor(cli): simplify HEAD watching to a single fs.watchFile

Drop the fs.watch directory watcher (Bun on Linux never delivers its
HEAD events, making it dead weight on the runtime the CLI ships on) and
the debounce it required. watchGitHead now just stat-watches the single
.git/HEAD file via fs.watchFile, which survives git's rename-based HEAD
updates and works on network mounts.

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

* refactor(cli): use a plain 5s poll for repo status

Remove the HEAD watcher entirely per review preference for minimal
code: root.tsx now just polls readRepoStatus every 5 seconds, skipping
state updates (via isSameRepoStatus) when nothing changed so idle ticks
don't re-render the app.

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

* fix(cli): skip repo status poll ticks while a read is in flight

Bounds concurrent git subprocesses when a read exceeds the 5s interval
(slow git on huge repos) and prevents an older completion from
overwriting newer status. Addresses Greptile review feedback.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 16:50:30 -07:00
Saoud Rizwan b035bf9255 fix(desktop): give chat message actions breathing room under the last line (#12921)
The copy/fork (and user copy/edit/restore) action row was pulled up 8px
(-translate-y-2), which made the icons collide with the descenders of the
message's last line of text. Reduce the raise to 4px (-translate-y-1) so the
actions sit with a small, deliberate gap under the message content while
still hugging the message closely enough to read as attached to it.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 16:49:09 -07:00
Saoud Rizwan ca9cb7f554 Slow hero heading verb rotation from 2.6s to 5s (#12940)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 16:27:12 -07:00
Saoud Rizwan 1ae2f71a0f fix(telemetry): dedupe sdk.error across layers and rate-limit repeated failures (#12931)
* fix(telemetry): dedupe sdk.error across layers and rate-limit repeated failures

Every provider failure was emitted twice — once by the model layer
(provider.stream, handled: true) and again verbatim by the agent loop
(agent.run, handled: false) — and unattended retry loops emitted the
same failure every iteration, unbounded. 24h of CLI data: 300K events
from 1.7K users, top 10 machines at 70% of volume.

Two changes:

- The agent loop no longer re-reports model stream failures. run-failed
  events carry errorClass exactly when the run failed on a model stream
  error, and the model layer already reports those at its own error
  boundary — so the run loop reports only failures that originate in
  the loop itself (empty response, max iterations, ...).

- captureSdkError caps identical failures per process: 5 per hour per
  (event, component, operation, error_type, normalized message), with
  digit runs collapsed so retry counters coalesce. Suppressed emissions
  surface as suppressed_count on the next emission after the window
  rolls over. In-memory only; the cap never blocks reporting.

Event name, attributes, and all call sites are unchanged;
suppressed_count is the only additive field.

* fix(telemetry): make sdk.error dedup ownership explicit and key limiter on status/code

Review follow-ups (#12931):

- Reporting ownership is now an explicit signal instead of being inferred
  from errorClass. captureSdkError returns whether the failure was
  recorded, the model layer forwards that as errorReported on the finish
  event, and the run loop skips only failures marked reported. Custom
  AgentModel implementations that never call captureSdkError leave the
  bit unset, so their failures still produce exactly one sdk.error from
  the run loop (regression test added).

- The rate-limit key now includes the structured error_status and
  error_code that normalizeSdkError already extracts, so an HTTP 429
  hot loop cannot consume an HTTP 401's budget even though their
  messages differ only by digits (tests added for both fields).

- resetSdkErrorRateLimiterForTests is tagged @internal; it stays
  re-exported because package test suites can only reach it through the
  package entry point.
2026-08-04 16:25:44 -07:00
Saoud Rizwan 62c57a0ccd ci(ui): publish @cline/ui without a manual approval gate (#12938)
The publish job ran under the shared `Publish` GitHub environment, whose
required reviewers turned every @cline/ui release into a two-person
ceremony. Nothing in the job reads secrets from that environment — it
authenticates to npm purely over OIDC trusted publishing — so the
environment bought us an approval prompt and nothing else. sdk-publish
and cli-publish already publish unattended the same way.

The npm trusted publisher for @cline/ui was registered with
`environment: Publish`, which pins the OIDC token's environment claim, so
it has been re-registered without it (same repo, workflow file, and
permissions). That change is already live; landing this without it would
have broken publishing.

Access is still gated by workflow_dispatch (write access required), the
`refs/heads/main` ref check, and the typed `publish` confirmation.
2026-08-04 16:23:46 -07:00
Saoud Rizwan f3c8b6748b Remove model-initiated plan-to-act switching from the VS Code extension (#12929)
* Remove model-initiated plan-to-act switching from the VS Code extension

Match the legacy extension: the model can no longer call switch_to_act_mode
to move itself from plan mode to act mode. The user must flip the Plan/Act
toggle manually. The CLI keeps the tool and its prompt unchanged.

- Stop registering the switch_to_act_mode extra tool in plan-mode sessions
  and drop the pending-mode-change queue, beforeModel stop hook, and idle
  apply path that existed only for the tool-initiated switch.
- Add a planModeSwitchTool option to buildClineSystemPrompt (default true,
  CLI output unchanged) and a PLAN_MODE_INSTRUCTIONS_MANUAL_SWITCH variant
  that directs the model to ask the user to toggle to Act mode instead of
  calling a tool it does not have; the extension passes false.
- The user-driven toggle path (togglePlanActModeProto), including
  auto-continue when a completed plan is presented, is unchanged.

* Use a generic completing-tool name in translator retag test

Review feedback: submit_and_exit is a yolo-mode tool and does not exist
in plan/act sessions. The test exercises tool-agnostic translator
behavior, so use a neutral example name and clarify the comment.
2026-08-04 16:09:05 -07:00
Bee e6104cfd2c feat(desktop): capture application errors in telemetry (#12893)
* feat(desktop): capture application errors in telemetry

* fix(desktop): deduplicate errors across reporting layers

* fix desktop error telemetry fallback

* fix(desktop): skip idle transport-close reports and reuse http endpoint helper

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

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 15:42:28 -07:00
Bee 12431cd97b fix(cli): claim connector instance before socket connect (#12765)
* fix(cli): claim connector instance before socket connect

Prevent racing foreground or detached connector launches from
both opening socket-mode with the same bot token by exclusively
claiming the state file via tryClaimConnectorStateFile before
connecting, and exit with CONNECT_ALREADY_RUNNING_EXIT_CODE when
another live instance already holds the claim.

* serializes stale-generation replacement without a removable mutex

* lint

* fix

* base

* fix(cli): keep pre-claim Slack state files manageable

State files written by CLI versions that predate connector claiming have
no claimId, so requiring it in readConnectorState made a live legacy
connector invisible: stop deleted its state without stopping it, which
let the next connect open a second socket-mode connection with the same
bot token. Treat claimId as optional metadata; claiming itself never
relied on the validator.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 15:41:19 -07:00
Bee 2b34b48ec6 fix(desktop): plugin package names (#12933) 2026-08-04 15:38:28 -07:00
Mikołaj Kondratek dc7eb755cf chore(telemetry): drop capture defs @cline/core owns, keep agent identity on late tool events (#12914)
* chore(telemetry): remove duplicate capture defs owned by @cline/core

The cline-core bundle layer (apps/vscode, which also compiles into the
cline-core.js that JetBrains runs) still carries event constants and
public capture methods inherited from the legacy architecture. Where
@cline/core now emits the same event, the bundle-layer twin is a second
capture path on the same telemetry service — which is how the
task.provider_api_error double-emission happened (cline/cline#12820,
follow-up to the removals in cline/cline#12818).

Removes 20 such methods and the EVENTS constants only they referenced:

- 15 whose signal @cline/core emits today (task lifecycle, tokens, tool
  and skill usage, auth start/success/failure, opt-out, workspace init,
  and summarize_task, which core replaced with task.compaction_*).
- 3 obsolete on both architecture lines: captureModelSelected (its
  model_selected signal survives as an action on
  captureOnboardingProgress), captureRulesMenuOpened, captureHostEvent.
- 2 whose trigger moved into core/sdk, so this layer can no longer
  observe them and re-wiring here would be wrong:
  captureWorkspacePathResolved (core already owns workspace.path_resolved)
  and captureGeminiApiPerformance (providers live in core; generic
  provider-timing events supersede it).

Deliberately NOT removed: capture methods with no caller here but a live
caller on legacy-extension. Those emit signals originating in this bundle
(webview UI, VS Code storage, host terminal, checkpoints, focus chain,
legacy-task migration), so core cannot emit them and the missing piece is
a call site on this line, not a redundant definition. They are the
SDK-parity backlog and are flagged as such in the file.

Verified against the JetBrains plugin repo: it references none of these
methods or event names, and no proto surface changes.

Also drops the unused TokenUsage interface, the taskTurnCounts and
taskToolCallCounts maps (only deleted methods wrote to them), and EVENTS
constants that were already orphaned before this change.

Tests that only exercised a removed method are gone; tests that used one
merely as a vehicle for provider/metadata assertions now use a surviving
method, so that coverage is preserved.

* fix(telemetry): keep agent identity on events dispatched after session teardown

A small share of task.tool_used events (~285 of 229k over 48h on
extension_variant=next) arrive without any agentId/agentKind/isSubagent
attributes. Root cause: AgentEventBridge.dispatchAgentEvent resolves
identity solely from the live-session map (AgentEvent metadata never
carries agentId in practice), and session teardown deletes the map entry
before the agent's run fully drains — dispose/stopSession paths can skip
or fail agent.shutdown() without aborting first. Late events from the
still-draining run then hit the session-map miss branch, which passed no
identity at all, so buildTelemetryAgentIdentity returned undefined and
the event was emitted bare.

Fix: snapshot the identity stamped on each session's events while the
session is registered (bounded FIFO map) and reuse it on a session-map
miss. Purely additive — no event is added, removed, or renamed; the
live-session and sub-agent paths emit byte-identical properties.
2026-08-04 22:44:19 +02:00
Bee accd7e5809 feat(sdk): add session initiation mode and lazy session persistence (#12807)
* feat(sdk): add session initiation mode and lazy session persistence

- Introduce top-level `StartSessionInput.mode` (`user`, `automation`, `subagent`, `team`) alongside `source`, so persisted history records both the client surface and how the session began; missing mode defaults to `user`.
- Make root-session persistence lazy: starting a runtime allocates the session ID in memory without creating a database row, manifest, or messages artifact. The first accepted user turn persists that same ID, so closing a runtime before any user turn leaves no empty history entry, and persistence never allocates a replacement ID for unknown sessions.
- Require automation runtime adapters to explicitly persist `mode: "automation"` for every run.
- Document the provenance model in `sdk/ARCHITECTURE.md`, update the VS Code session factory comment, and add tests for the automation runtime handlers.

* fix(sdk): persist automation trigger source as session provenance

The runtime adapters stopped writing the cron request source into the
session row when source became the client surface, which silently
dropped the spec-defined trigger label. Record it as
sessionHistoryOrigin.trigger instead, surface it in the messages-file
origin, and sort the new history-origin import in HubRuntimeHost.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-04 13:29:36 -07:00
Bee 400ba47387 fix(llms): switch ollama provider package (#12892)
* fix(ollama): use native AI SDK provider

* fix(ollama): patch ollama-ai-provider-v2 wire contracts and lock them with real-provider tests

The pinned ollama-ai-provider-v2@4.0.1 breaks four native Ollama wire
contracts (review findings on #12892). Patch the package via Bun
patchedDependencies:

- omit think from the request when no reasoning setting resolves,
  instead of forcing think: false (lets the server default apply)
- surface mid-stream {"error": ...} objects as error stream parts with
  an error finish reason, instead of dropping them before a clean finish
- serialize attachment-only user turns as string content (""), not []
- include the documented tool_name field on tool result messages

Add ollama.wire.test.ts exercising doStream through the vendor module
against the real (patched) package with a stubbed fetch, asserting on
the actual /api/chat request bodies and parsed stream so regressions in
the dependency's request converter or stream parser are caught.

* fix ollama model list refresh

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-04 13:28:53 -07:00
Bee 3af23c1c4c fix(cli,core): stop duplicate connector launches (#12770)
* fix: prevent duplicate connector launches during doctor/connect

Mark connectors as starting before the hub daemon spawns so autostart
skips in-flight instances, and improve doctor process filtering with
container-aware namespace/cgroup checks plus detached log rotation.

* Update apps/cli/src/connectors/common.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* feat(hub): supervise connector processes

* feat(connectors): enable tools by default, and stop replaying the Slack greeting

Tools were on by default only for Telegram (via --no-tools); Slack, Discord,
Linear, Google Chat and WhatsApp all required an explicit --enable-tools. All
six now default to tools on and opt out with --no-tools.

--enable-tools still parses everywhere, including Telegram which never accepted
it, so deployed scripts, systemd units and persisted autostart arguments keep
working. Passing both resolves to the safer answer: --no-tools wins. This also
affects hub/webview starts, which never emitted a tools flag and so ran those
five connectors with tools off.

Slack no longer posts the "Connected to Cline." first-contact message. It was
gated on per-thread welcomeSentAt, so a connector restart or a cleared history
made the next user message look like first contact and replayed the greeting.
The host mechanism is unchanged and the other adapters still greet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(connectors): recover a thread whose session is wedged mid-run

A connector thread keeps a long-lived mapping to a hub session. When that
session's runtime still had a run in flight and no abort had been requested,
every message in the thread came back as "SessionRuntime.shutdown called while a
run is in progress" instead of an answer, and stayed that way until someone
cleared the binding by hand. Observed on the Cline Mom Slack bot after a stack
restart.

The connector host already recovers from a session the hub no longer knows
about: it forgets the mapping and replays the turn once against a fresh session.
This widens the trigger from "session not found" to "session cannot serve
another turn" via isUnusableSessionError, so a wedged runtime takes the same
path.

The shutdown error now carries a stable code (SessionRunInProgressError,
session_run_in_progress) so callers can recognise it structurally. The predicate
also matches on message, because an error reaching a connector has crossed the
hub's JSON boundary and arrives as a bare message - and because a host commonly
runs a hub and CLI of different versions. Ordinary run failures still propagate
untouched: replacing the session on those would hide real errors and drop the
conversation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(connectors): serialise turns that share a session

Answering "what happens if I message the bot in another thread while it is still
replying": channel threads were already independent, but DMs were not.

findBindingForThread deliberately reuses one binding — and therefore one runtime
session — for every message in a DM channel, so a DM stays one continuous
conversation. The turn queue, though, was keyed by thread id, and a DM thread id
carries the message timestamp. Two messages in flight in the same DM therefore
got two independent queues and ran concurrently against a single session, which
fails with "shutdown called while a run is in progress" or interleaves two
conversations in one session history.

The queue key now follows the same identity rule as the binding lookup, via
resolveThreadTurnQueueKey next to findBindingForThread so the two cannot drift.
DM messages queue behind each other on the shared session; channel threads keep
their own key and still run in parallel. Applied to all six adapters, which all
had the same mismatch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(core): abort an in-flight run before tearing its session down

Where the Slack bot's "plugin-sandbox process exited (code=null, signal=SIGTERM)"
came from, and its "shutdown called while a run is in progress" sibling: both are
one event, a session released while a run was still going.

stopSession aborts the agent first "so shutdown can proceed", but callers that
reach shutdownSession or releaseSessionRuntime another way did not - hub
dispose() on a restart being the one that hurt. Without an abort the runtime
refuses to shut down, that error is rethrown from the cleanup, and the plugin
sandbox is SIGTERMed while tool calls are still pending, so those calls reject
with "plugin-sandbox process exited". A connector turn awaiting the run reports
whichever surfaced first instead of answering.

Both paths now abort and let the run drain before shutting the agent, runtime and
sandbox down, guarded on session.aborting so callers that already aborted do not
abort twice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(connectors): stop announcing "Steering current task."

Every follow-up sent while the bot was replying added an acknowledgement line to
the thread, and the wording overstated what happens: the host treats delivery
"steer" the same as "queue", enqueuing the prompt for the session rather than
injecting it into the loop already running. The follow-up is now handed over
silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(core): retire a dead supervised entry before replacing it

A start arriving while an instance sat in backoff left the old entry's
restart timer live. The timer closes over the old entry object, so when
it fired it spawned a second process for the same (channel, instanceId)
- untracked by the supervisor's map, so invisible to list() and
unreachable by stop() - two connectors holding one bot token, which is
the exact failure supervision exists to prevent. Its exit handler then
kept reaping the live instance's state and rescheduling restarts.

The same window exists before the timer is even scheduled: the
exit-cleanup chain runs first, and a replacement made mid-chain would be
followed by a restart scheduled for the retired entry.

start() now retires a dead existing entry explicitly - cancel its timer,
mark it stopped, drop its exit listener. Both the timer callback and the
cleanup chain already stand down on "stopped", so one mark covers both
phases.

* fix(core): serialise supervisor start/stop and wait for stopped processes to die

Found by exercising a hub restart against a live webhook connector: the
new hub's boot reconnect restarts the adopted survivor - which suspends
inside stop() on the CLI cleanup - while the user's `cline connect`
arrives as connector.start. With no per-instance serialisation the two
starts interleaved across that suspension and both spawned. The map
tracked one process while the other lived on untracked, holding the
connector's webhook port; the tracked chain crash-looped on EADDRINUSE
through all five attempts and ended state=failed, while the ghost kept
running with no way to reach it through list() or stop().

Two changes:

- start/stop (and the backoff-restart spawn) now run under a per-
  instance-key promise queue, so one instance has exactly one lifecycle
  operation in flight. The exit-cleanup chain also stands down when its
  entry is no longer the one in the map.
- stop() waits for the process to actually die after SIGTERM (bounded,
  then SIGKILL) instead of returning while it still holds its listen
  port - the race that turned the double-spawn into a crash loop, and
  that could burn a backoff cycle on any webhook connector restart.

process.kill is now injectable (killProcess), which also stops the test
suite from signalling arbitrary real pids like 600 on the host.

Verified live: the same kill-hub-then-reconnect sequence now converges
to one tracked running process, with the concurrent user start
correctly answered "already running under the hub".

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-04 13:28:18 -07:00
dependabot[bot] ce04e80909 chore(deps): bump rand (#11231)
Bumps the cargo group with 1 update in the /apps/examples/desktop-app/src-tauri directory: [rand](https://github.com/rust-random/rand).


Updates `rand` from 0.9.2 to 0.9.4
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/0.9.4/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/rand_core-0.9.2...0.9.4)

---
updated-dependencies:
- dependency-name: rand
  dependency-version: 0.9.4
  dependency-type: indirect
  dependency-group: cargo
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 12:53:15 -07:00
Saoud Rizwan 058c8c90a2 feat(desktop): ship a single universal macOS DMG instead of per-arch downloads (#12923)
Tauri's universal-apple-darwin target lipos the Rust binary but expects
sidecars to already be fat binaries, so build-sidecar-bin.ts now compiles
both Bun slices and merges them when the target triple is universal.
The publish workflow builds one universal bundle instead of a two-leg
matrix, verifies every Mach-O in the bundle carries both slices, and the
updater manifest points both darwin-aarch64 and darwin-x86_64 at the same
universal artifact so existing per-arch installs migrate automatically.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 12:42:55 -07:00
Saoud Rizwan 0f7e4b66ef Add themes to the CLI (#12899)
* Add user-selectable color themes to the CLI TUI

Adds a theme system to the interactive TUI (cline -i):
- New tuiTheme global setting persisted in global-settings.json
- Built-in themes: Auto (terminal-adaptive, default), Cline Dark,
  Cline Light, Tokyo Night, Gruvbox Dark, Nord, Dracula, Catppuccin
  Mocha, One Dark, Solarized Dark, Solarized Light
- /theme command, command palette entry, and a Theme row in
  /settings General tab, all opening a live-preview theme picker
- Named themes paint their background, default foreground, accents,
  syntax highlighting, and derived diff colors across the TUI
- CLINE_THEME env var overrides the persisted theme at startup

Closes #12872

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

* Widen theme picker dialog and label column

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

* Format theme picker

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

* Give each theme a descriptive picker blurb

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

* Theme all main-surface components instead of static palette colors

The ask-question / tool-approval element, toasts, queued prompts,
autocomplete dropdown, chat error cards, searchable lists, and the
onboarding screens hardcoded the brand palette (act blue, selection
highlight, black-on-selection text) and fixed dark grays, so they
ignored the active theme.

- ResolvedTheme gains selection/textOnSelection; the selected-row text
  flips between black and white by WCAG contrast against the accent
- Inline ask-question / tool-approval, Toast, QueuedPrompts,
  AutocompleteDropdown, SearchableList, and chat error cards now use
  theme accents and the themed selection pair
- Onboarding screens derive subtle borders/details from the theme
  background instead of #333333/#555555, and use themed accents
- Dialog surfaces (settings, pickers, history) intentionally keep their
  static dark surface styling

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 12:26:00 -07:00
Bee f1c45b63c8 feat(desktop): add token usage warning colors (#12919)
* feat(desktop): add token usage warning colors

* context info
2026-08-04 11:17:20 -07:00
Deach d6d1c789b3 fix: correct Linux keybinding label in Plan/Act mode tooltip (#11067)
* fix: correct Linux keybinding label in Plan/Act mode tooltip

On Linux, event.metaKey maps to the Super (Win) key, not Alt.
detectMetaKeyChar was returning "Alt" for Linux, causing the Plan/Act
mode toggle tooltip to display "Alt+Shift+A" instead of "Super+Shift+A".

Fixes #11026

* fix: update platformUtils.spec.ts Linux test expectation to Super

---------

Co-authored-by: Dominic Cooney <dominic.cooney@cline.bot>
2026-08-04 10:44:18 -07:00
oab24413gmai 3e81006863 docs: capitalize GitHub in security note (#11088)
Co-authored-by: Mira Sato <275437409+oab24413gmai@users.noreply.github.com>
2026-08-04 10:11:29 -07:00
John Choi 8ea52a2eea fix(desktop): make agent header draggable (#12910)
* fix(desktop): make agent header draggable

* fix(desktop): keep read-only title draggable
2026-08-04 09:21:22 -07:00
John Choi 5ec2d47b21 refactor(ui): extract agent prompt queue (#12791)
* feat(ui): extract agent prompt queue

* fix(ui): keep prompt queue usable on failures

* fix(ui): surface prompt queue action failures

- report failed edit, steer, and remove callbacks inline as a row alert
- disable all queue actions while any action is in flight
- mark the busy row aria-busy and accept readonly item arrays

* fix(ui): preserve prompt queue failures

* chore(ui): drop unrelated formatting changes

* refactor(ui): style prompt queue with Tailwind

* style(ui): remove redundant prompt queue reset
2026-08-03 20:01:37 -07:00
Saoud Rizwan 46fcde0a96 ci(desktop): drop the Rust build cache from the code-signing job (#12898)
* ci(desktop): drop the Rust build cache from the code-signing job

The `build` job is the only one that can read the Apple Developer ID
certificate and the Tauri updater signing key, and it restored a
swatinem/rust-cache archive before running them. A restored cache archive
is attacker-controlled the moment the Actions cache is poisoned, which is
the pivot used against this repo's nightly workflow in Feb 2026 and the
reason actions/cache was stripped from the credential-bearing publish
jobs at the time. This workflow was added months later and reintroduced
the pattern. The updater key is the worst thing here to leak: it signs
every auto-update the installed desktop app accepts.

The cache was also not buying anything. Across the eight runs of this
workflow, seven logged "No cache found" on both matrix legs; only the run
32 minutes after another one hit, saving 1-3 minutes. A release cadence
measured in days does not outlive the entry under the repo's 10 GB LRU
eviction, so the steady state was a cold build regardless. Cold builds
took 5-7 minutes against a 90-minute timeout.

No behaviour change otherwise: the step had no id and no outputs, so
nothing referenced it.

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

* ci(desktop): trim the cache-removal comment to the constraint

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-03 19:16:23 -07:00
John Choi fbaa44be96 refactor(ui): extract agent ask question (#12790)
* feat(ui): extract agent ask question

* fix(ui): harden ask-question presentation

- dedupe repeated model-supplied options so keys stay stable
- mark pending items aria-busy to match the approval card
- expose the accent palette as overridable custom properties

* fix(ui): polish ask question feedback

* fix(ui): label follow-up question sections

* refactor(ui): style ask question with Tailwind
2026-08-03 18:55:12 -07:00
John Choi aec350bb9d refactor(ui): use Tailwind for shared components (#12719)
* refactor(ui): extract desktop approval card

* fix(ui): preserve approval card parity

* refactor(ui): keep approval labels fixed

* refactor(ui): use Tailwind for shared components

* test(ui): cover each Tailwind component source

* refactor(ui): migrate approval card styles

* fix(ui): preserve host and quick-action behavior

* fix(ui): preserve component hover behavior

* fix(ui): preserve selected option hover

* fix(ui): isolate embedded Tailwind contract

* fix(ui): reset approval button block padding

* chore(ui): bump preview package version
2026-08-03 18:28:57 -07:00
oab24413gmai 16129cf90a docs: capitalize GitHub in security note (#10723)
Co-authored-by: Mira Sato <275437409+oab24413gmai@users.noreply.github.com>
2026-08-03 17:34:27 -07:00
James Arlen 3520786f4a security: hygiene sweep — docs pin lifts, example next bump, workspace overrides (closes ~153 Vanta findings) (#12749)
* security: docs/examples/tooling hygiene sweep — lift fix-blocking pins, bump example next, workspace overrides

VMP 2026-07-30 quarterly run, PR 3 of the condensed worklist (closes ~153 Vanta findings).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(deps): refresh lockfiles for security updates

* fix(deps): keep Discord on patched Undici 6

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-08-03 17:21:03 -07:00
Bee 8f0ff70215 chore(llms): upgrade to AI SDK 7 (#12891)
* chore(llms): upgrade to AI SDK 7

* fix(llms): include Codex provider during builds

* fix(llms): address AI SDK 7 runtime regressions
2026-08-03 17:10:41 -07:00
Saoud Rizwan 0619e5a016 fix(cli): deliver Telegram slash commands to the connector command host (#12888)
The @chat-adapter/telegram library intercepts any message whose leading
entity is a bot_command and routes it to slash-command handlers instead
of the mention/subscribed-message handlers. The Telegram connector
registered no onSlashCommand handler (unlike Discord and Slack), so
commands like /clear were consumed by the library and silently dropped.

Register a slash-command handler that rebuilds the originating chat
thread and forwards the original message text (preserving @bot
addressing for group chats) into the same turn pipeline as regular
messages, so connector commands reach the chat command host.

Fixes #12871

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-03 15:57:41 -07:00
Dominic Cooney 2a0dd197bf chore(vscode): remove dead next-gen model classifier (#12887)
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-08-04 07:10:04 +09:00
Tomás Barreiro 82e3596f96 Add a script to do dev work on ACP (#12886) 2026-08-03 22:21:16 +02:00
Octopus 895ab53b78 fix(llms): inherit MiniMax default from models.dev (#11218)
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-08-03 21:23:44 +02:00
Tran Binh Minh b8f51b9e55 fix(vscode): surface a clear error when a provider has no API key (#12859)
* fix(vscode): surface a clear error when a provider has no API key

A key-based provider with no API key sends the request without an Authorization header, and the provider's raw 401 reached the chat panel unclassified because reshapeErrorForWebview falls through to the raw message and ClineError's auth regexes do not match it. Rewrite the missing-Authorization-header case into actionable guidance naming the provider, alongside the existing model-not-found matcher. Matching is limited to the no-header signature so a present-but-wrong key is never relabelled as missing, and no preflight is added because authMethod misclassifies local providers and 175 of 179 builtins resolve keys from the environment.

* fix: don't name a fallback provider in the missing-key message

reshapeErrorForWebview defaults providerId to "cline" for its
ClineError-JSON branches, but state.activeProviderId() can be undefined —
the missing-credential message would then blame the cline provider for a
key it doesn't take. Keep the "cline" fallback for the JSON branches and
pass the raw id to the credential matcher, which now only names a provider
it was actually given.

---------

Co-authored-by: Mikołaj Kondratek <19799111+mkondratek@users.noreply.github.com>
2026-08-03 11:27:08 -07:00
Michael Gasperini 25dc89eab4 feat(vscode): recognize Chutes provider (#12068) 2026-08-03 11:16:09 -07:00
Mikołaj Kondratek 5fdd840d5f refactor(llms): classify typed AI SDK errors before the structural walk (#12814)
* refactor(llms): classify typed AI SDK errors before the structural walk

Add a typed pre-pass to classifyProviderError that recognizes real AI SDK
error instances via their symbol-based isInstance() guards: RetryError
unwraps to its last attempt, APICallError is judged on message/responseBody/
data with its typed statusCode as the sole authoritative status,
TypeValidationError on the payload in value, and any other AISDKError
recurses into its cause. The detection rules (overflow patterns, provider
codes, rate-limit vetoes, invalid-request status gate) are extracted into a
shared verdict function used unchanged by both the typed pass and the
existing structural walk, which remains the fallback for gateway-forwarded
plain-JSON payloads that only name an AI SDK error (ENG-2394).

* fix(llms): classify a RetryError by its final attempt even when untyped

When a RetryError's last attempt was not a typed AI SDK error, the typed
pre-pass fell back to structurally walking the whole wrapper, letting
signals from earlier (retried-away) attempts veto or fake the final
attempt's verdict — e.g. a retryable 429 on attempt one vetoing a plain
overflow rejection on the final attempt. Walk the final attempt alone
instead; a RetryError with no recorded attempts still falls back to the
plain structural walk.

* fix(llms): gate typed APICallError verdicts on the authoritative statusCode

verdictFromSignals checks the explicit context_length_exceeded code before
the rate-limit veto and the invalid-request status gate, so a typed
APICallError with statusCode 429 or 500 whose body echoed that code was
still classified as an overflow, contradicting the branch's contract that
the typed statusCode is the sole authoritative status. Gate the whole
payload verdict on the typed statusCode first (absent a statusCode the
payload still decides), and cover the explicit-code case at 429/500/400
with real instances.
2026-08-03 06:40:40 -07:00
Mikołaj Kondratek cdcaa74422 feat(sdk): detect and recover from context-window overflow errors (#12804)
* feat(sdk): detect and recover from context-window overflow errors

Port of the legacy arch's context-window-exceeded handling to the SDK
arch (the SDK arch previously surfaced these as raw unclassified stream
errors with no recovery; see ENG-2394 root-cause investigation).

- llms: new classifyProviderError() walks the raw provider error
  structure (AI SDK wrappers, gateway value.error_message, responseBody,
  cause chains) and classifies it before extractErrorMessage flattens
  it. Reuses the legacy detectors' message patterns with rate-limit
  vetoes and an invalid-request status gate.
- shared: ProviderErrorClass union; errorClass on the model finish
  event, run-failed event, runtime snapshot, and prepare-turn contexts.
- core: prepare-turn overflowRecovery flag forces a compaction that
  bypasses the token-estimate trigger (the estimate just proved wrong)
  and runs the deterministic basic strategy directly, so recovery never
  depends on another successful LLM request. New overflow_recovery
  compaction mode in status notices and compaction telemetry.
- agents: on a classified overflow the runtime force-compacts and
  retries once per run, emitting a status notice. Terminal states fail
  with actionable messages instead of raw provider dumps: nothing to
  compact (first-prompt overflow), no prepare-turn pipeline, or a retry
  that still overflows. The doomed request is not re-sent when forced
  compaction cannot shrink the transcript.
- telemetry: task.provider_api_error gains errorClass and
  task.provider_stream_failed gains error_class, populated from the
  same classification, so context-overflow failures become countable.

* fix(core): keep overflow-recovery compaction deterministic with custom compactors

A session-supplied compaction.compact previously took precedence over
the overflow_recovery basic-strategy branch, so an LLM-backed custom
compactor could hit the same context overflow mid-recovery. The custom
compactor still gets first shot (it sees mode overflow_recovery and
owns its transcript invariants), but if it throws or declines, basic
compaction now runs so recovery never depends on another successful
LLM request. Cancellation still propagates.

* fix(core): fall back to basic compaction when a custom compactor does not shrink during overflow recovery

A custom compactor that returns unchanged or larger messages would
previously satisfy the recovery branch, and the runtime would then
reject the retry as non-shrinking and fail terminally even though
basic compaction could still prune the transcript. Recovery now
treats a non-shrinking custom result like a decline and runs basic
compaction.

* fix(core): hold custom overflow-recovery compaction to the recovery token target

A custom compactor result that was only marginally smaller than the
input passed the shrink check, skipped the basic fallback, and spent
the run's single recovery retry on a request that still could not fit.
The custom result is now accepted only when it is strictly smaller AND
within the recovery token target basic compaction aims for; otherwise
basic compaction runs.

* fix(core): reject empty custom compaction results during overflow recovery

An empty transcript from a custom compactor passed both the shrink and
token-target checks (trivially smaller, zero tokens) and suppressed the
basic fallback, so the retry would have been sent without the request
it was supposed to re-send. The acceptance bar now covers the full
input space in one predicate: non-empty AND strictly smaller AND within
the recovery token target.

* feat(core): expose the turn abort signal to custom compactors

CoreCompactionContext now carries the prepare-turn abort signal, so a
custom compact implementation that calls a model or external service
can observe cancellation instead of blocking the turn (including the
overflow-recovery path) on a stalled request. Builtin strategies
already received the signal via providerConfig; this closes the gap
for custom compactors across auto, manual, and recovery modes.

* fix(sdk): classify provider errors from registered ApiHandler models

Registered handlers (VS Code LM and any other host-supplied provider)
reach the runtime through createAgentModelFromApiHandler, which flattens
failures to a message string — so context-window rejections on that path
were never classified and never entered overflow recovery.

- The adapter now classifies at its own error boundary, where the raw
  error is still structured (status codes, response bodies), for both
  thrown errors and failed done chunks. Aborts stay unclassified.
- The runtime falls back to classifying the finish message when a model
  supplies no class, so custom AgentModel implementations are covered
  too.
- Hold the custom-compactor acceptance check to token estimates on both
  sides instead of mixing serialized length with a token target, and
  document why the runtime's shrink backstop keeps a serialized-size
  proxy (the shared estimator is linear in characters, so the verdict is
  identical) with a TODO to surface real estimates from prepareTurn.
- Drop the now-unused errorClass parameter from captureProviderApiError:
  #12820 removed core's capture site, so host adapters own that event.

* test(core): reuse the handler harness for the overflow classification case

The hand-rolled throwing generator had no yield, which biome's
correctness/useYield rejects as an error (the repo's lint gate runs on
sdk/ and apps/, and biome does not honor the eslint require-yield
directive the existing harness carries). fakeHandler now accepts the
error to throw, so the new case reuses it instead.
2026-08-03 09:03:22 +02:00
Mikołaj Kondratek 5acc98474a fix(mcp): refresh lists on list_changed notifications instead of toasting (#12619)
* fix(mcp): refresh lists on list_changed notifications instead of toasting

Servers emit notifications/tools/list_changed in bursts (a toolset change
or shutdown can produce a dozen at once), and the fallback notification
handler surfaced every one of them as a host toast, flooding the user
with identical messages (ENG-2298, found testing the JetBrains IDE MCP
server integration).

Handle tools/resources/prompts list_changed notifications by refreshing
the corresponding cached lists, debounced 300ms per server and list
kind, then pushing the update through notifyWebviewOfServerChanges() so
the webview and the SDK session tool-list check pick it up. Downgrade
remaining unhandled notification types to logger output.

* fix(mcp): guard list_changed refreshes against races and failed fetches

Address review: serialize per-key refreshes by chaining onto any
in-flight one, so overlapping fetches can't complete out of order and
publish a stale list. Make the fetch helpers return undefined on
failure (instead of an empty list) so the refresh path can keep the
previous cached list and skip the webview notification, rather than
erasing valid entries on a transient error; connect-time call sites
keep their old empty-list fallback.

* fix(mcp): drop in-flight list refresh when the connection was replaced

Address review: refreshChangedList captured the connection object before
awaiting the list fetches, so a reconnect mid-fetch wrote the result to
the removed connection while the replacement kept its own state. Re-check
connection identity after the fetches and drop the result when it
changed — the replacement fetched fresh lists at connect time, after the
change that produced the notification, so the in-flight result is older.

* fix(mcp): retry failed list refreshes and publish state after reconnect

Address review. A list_changed notification consumes the server's change
signal, so a transiently failed refresh left the cached list stale until
the next notification; retry with exponential backoff (1s/2s/4s, max 3)
per server and list kind, with a fresh notification superseding any
pending retry. Also publish server state after a successful streamable
HTTP reconnect: connectToServer() loads fresh lists but never sent them,
leaving the webview on 'connecting' with pre-reconnect capabilities.

* fix(mcp): don't restart a live connection when post-reconnect publish fails

Address review: the post-reconnect notifyWebviewOfServerChanges() sat
inside the connect retry loop's try block, so a publication failure
(e.g. a settings file read error) was treated as a transport failure
and re-ran connectToServer() against the already-live connection,
leaking its client/transport. Publication now happens outside the
connect try/catch and only logs on failure.

* fix(mcp): drop superseded in-flight list refreshes instead of publishing

Address review: a newer list_changed notification queued its refresh
behind one already in flight without invalidating it, so the older run
could briefly publish an obsolete list (and churn the SDK session)
before the newer refresh corrected it. Each schedule now starts a new
generation per server+kind; a run whose generation is no longer current
skips fetching (when caught early), drops its result before publishing,
and doesn't schedule retries — the superseding refresh covers it.

* fix(mcp): harden list refresh and reconnect publication paths

Address review (post-reconnect publish failure leaving consumers stuck
on 'connecting' with stale lists) plus an adversarial pass over the
whole change to close the remaining gaps in one batch:

- Retry publications bounded (publishServerChanges) after a successful
  reconnect AND in both terminal disconnected paths, which are equally
  terminal; never throw from handleError, whose promise transport.onerror
  discards. Guard the stdio/SSE onerror publishes the same way.
- Treat an undeclared capability or a method-not-found answer as an
  authoritatively empty list instead of a retryable failure, so servers
  without e.g. resources/templates/list don't burn the full retry ladder
  on every list_changed notification.
- Retry when the fetch succeeded but the webview publish failed: the
  cache is updated but consumers haven't seen it.
- Cap debounce deferral at 2s so a sustained sub-300ms notification
  stream can't starve the refresh indefinitely.
- Cancel pending refresh timers in deleteConnection; return 'skipped'
  (not 'failed') when a fetch failure coincides with connection
  teardown or supersession, so no retry fires against a replacement
  connection that already fetched fresh lists.
- Clear the pre-existing toolListChangeDebounceTimer in dispose().

* fix(mcp): supersede in-flight refreshes during connection teardown

Address review: deleteConnection removes the connection from
this.connections only after awaiting transport/client close, so a list
refresh completing inside that window passed its identity check and
published state for a connection being torn down. Bump the per-key
generation at the start of deleteConnection so any in-flight refresh is
superseded and drops its result; bumping (never resetting) keeps
generations monotonic across reconnects.

* fix(mcp): close reconnect-retry and teardown-publication races

Address review (cline-cloud):

1. The streamable HTTP reconnect loop revalidated only after the first
   backoff. Later retries could resurrect a server removed or disabled
   from settings during a delay, or displace a replacement connection
   another path had installed — connectToServer() drops a same-name
   connection without closing it, leaking its transport. The loop now
   revalidates before every attempt: it aborts when a live replacement
   exists (our own original connection and the 'disconnected' husk left
   by our own failed attempt don't count) or when fresh-read settings no
   longer define the server as enabled (isStillWanted callback; a
   settings read failure keeps the chain alive).

2. deleteConnection removed the connection from this.connections only
   after awaiting transport/client close, so a publication passing its
   suspension points inside that window could still serialize and
   publish the dying connection's state. The connection is now removed
   from published state before the close handshake is awaited.

The exhausted-retries test's partial-connection mock now carries status
'disconnected', matching what connectToServer's error path actually
leaves behind — that status is what distinguishes our own husk from a
live replacement.

* fix(mcp): don't displace an OAuth-required replacement during reconnect retries

Address review: the retry loop's replacement guard treated every
'disconnected' connection as our own failed-connect husk. An
OAuth-required connection is also 'disconnected' but retains its
client, transport, and authProvider for authentication — a retry that
displaced it would orphan that session and clobber the pending auth
state. Distinguish by client presence: the husk's creation sites set
client: null, so a 'disconnected' connection holding a client is a
replacement and aborts the retry chain.

* fix(mcp): distinguish OAuth replacements by flag, not client presence

Address review: an ordinary post-registration connect failure leaves a
'disconnected' connection with its (already-closed) client still
attached, so the client-presence check classified it as an OAuth-style
replacement — aborting the reconnect chain after a single failure,
including our own retries. Discriminate on server.oauthRequired
instead: only the OAuth-required connection retains live
client/transport/authProvider state worth protecting; ordinary failed
connections closed their client before being marked disconnected, so
retrying past them displaces nothing live. The exhausted-retries test
mock now carries the real husk shape (closed client attached) to pin
this regression.

* test(mcp): cover retry succeeding after a failed attempt's registered connection

Requested in review: the guard must recognize the 'disconnected'
connection a failed non-OAuth connectToServer() leaves behind (closed
client still attached) as our own attempt, and the following retry must
proceed and succeed.

* fix(mcp): don't retry reconnects with a config settings no longer define

Address review: a retry reconnects with the config captured at
connection creation, so if the user changed the server's config during
a backoff delay (and the watcher's reconnect with the new config
failed, leaving a disconnected husk our guard rightly retries past),
the retry would resurrect the obsolete URL/headers/command — and a
successful stale connection would contradict settings until the next
file touch. isStillWanted now also compares the captured config against
current settings via configsRequireRestart (connection-relevant fields
only), aborting the chain when they differ: the settings watcher owns
reconnection after a config change.
2026-08-03 07:51:04 +02:00
Bee 53a5266239 feat(desktop): show token usage in input toolbar (#12803)
* feat(desktop): show token usage in input toolbar

Load per-model context window sizes from the provider catalog and
pass the active model's limit down to ChatInputBar. Render a token
ring that visualizes current token usage against the model's context
window, and hydrate token usage plus cumulative cost from messages
and chat_usage events so the indicator stays accurate across turns
and session reloads. Add tests covering the ring rendering and usage
hydration.

* bigger ring

* move submit button to input box

* fix

* add cost tracker

* fix

* fix queued turn cost tracking
2026-08-02 17:19:33 +02:00
Saoud Rizwan 1654517614 ci(desktop): gate desktop publish secrets behind PublishDesktop environment (#12854)
* ci(desktop): gate desktop publish secrets behind PublishDesktop environment

The Apple signing/notarization and Tauri updater secrets were repository
secrets, readable by any workflow in the repo and by anyone with push
access via a branch carrying a modified workflow. Move them behind the
PublishDesktop environment, which requires reviewer approval and
restricts deployments to main.

The build job now declares the environment, so those secrets are readable
only there and only after an approval. Add a preflight check because a
missing secret fails dangerously rather than loudly: Tauri silently skips
code signing when APPLE_CERTIFICATE is empty and skips notarization when
APPLE_API_KEY is empty, so a misconfigured environment would still
publish an unsigned, un-notarized bundle. Only a missing updater key was
already caught, by the .sig check in Collect artifacts.

validate stays ungated so a bad tag fails in seconds rather than after an
approval, matching the ungated-build/gated-publish split in
ext-vscode-ab-package. The shared Slack and telemetry secrets stay where
they are; scoping them to this environment would silently empty them in
the CLI, SDK, and extension publish workflows.

* ci(desktop): verify signing secrets are not repository-scoped

The preflight added in the previous commit checks that the signing
secrets are non-empty, which proves presence but not scope, and then
reported that they had resolved from PublishDesktop. An environment-gated
job resolves repository and organization secrets too — environment values
merely take precedence — so a credential left at repository level would
pass that check while the message claimed the migration had worked. This
workflow already demonstrates it: the gated build job reads the shared
Slack and telemetry secrets, none of which are on the environment.

Add the complementary check to validate, which declares no environment: a
signing secret that resolves there can only be repository- or
organization-scoped, so it fails the run and names the offenders. Neither
check establishes provenance alone; together they do. validate is
ungated, so a misplaced secret now fails before the approval rather than
after it.

Also drop the provenance claim from the build message and correct the
skill doc, which stated that a repository-level secret would be invisible
to the gated job.

Reported by greptile on #12854.
2026-08-02 01:55:24 -07:00
Saoud Rizwan bbfcbcd31d chore(vscode): bump to 4.1.3 for stable release 2026-08-01 22:11:03 -07:00
Saoud Rizwan 102ef1ab84 chore(desktop): release v0.0.8 2026-08-01 21:53:13 -07:00
Saoud Rizwan 55d1476169 chore(cli): release v3.0.49 2026-08-01 21:37:00 -07:00
Saoud Rizwan 6173bad65e chore(sdk): release v0.0.69 2026-08-01 21:11:32 -07:00
Saoud Rizwan cf710d76bf feat(llms): retry empty Ollama responses at the model boundary (#12845)
Local backends (Ollama especially) intermittently return a turn that
finishes normally but carries no text, reasoning, or tool call. In the
SDK runtime an empty assistant turn is a hard failure ("Model returned
empty response"), so one flaky generation kills the whole task.

Adds a LanguageModelV3 middleware that retries the stream only when a
turn produced genuinely nothing, wired as the outermost middleware on
the Ollama vendor. A tool-call-only turn counts as content and is never
retried; non-empty turns stream through live with no added latency; and
turns that error or hit the token limit are passed through unchanged.

This is the streaming-safe slice of ai-sdk-ollama's reliability story:
its own reliability layer lives in doGenerate and owns the tool loop
(executes tools and force-synthesizes text), which is incompatible with
Cline running its own loop over doStream.
2026-08-01 19:34:54 -07:00
Saoud Rizwan 9b31692fa0 fix(migration): fall back to the default Cline model for unknown legacy model ids (#12846)
* fix(migration): fall back to the default Cline model for unknown legacy model ids

Some migrated users ended up making Cline provider requests with a model
id the new extension doesn't have because the legacy migration carried
their stored model id over verbatim and never applied a default.

Two small fixes in the provider settings migration:

- Drop a legacy Cline model id the catalog doesn't know so the entry
  falls back to the default model instead of carrying the unknown id
  into inference requests.
- getDefaultModelForProvider only accepted defaults present in the
  generated model block; Cline's generated block holds a few free models
  while its declared default (anthropic/claude-sonnet-5) lives in the
  collection catalog, so the fallback previously landed on an arbitrary
  free model instead of the default.

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

* fix(migration): validate legacy Cline models against the full runtime catalog

The known-model check used the curated Cline collection plus the tiny
generated cline block, but the runtime Cline catalog is OpenRouter-backed
and also resolves Vercel AI Gateway alias ids. Legacy users on
runtime-served ids outside the curated collection (e.g. the z-ai/glm-5
family) would have been wrongly defaulted. Suffixed variant ids like
...:1m still fall back to the default Cline model.

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

* fix(migration): validate Cline models against the canonicalized runtime catalog

Greptile review: the raw generated-catalog checks accepted alias ids
(e.g. OpenRouter's z-ai/...) that buildClineModels canonicalizes away
(to zai/...), persisting models absent from the exposed runtime catalog.

Validate against the collection model list (which the runtime catalog
mirrors exactly) and fold alias spellings onto their canonical ids via
the shared VERCEL_OPENROUTER_MODEL_ID_ALIAS_RULES, so legacy z-ai users
keep their model under the canonical id instead of being defaulted.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-01 19:34:03 -07:00
Saoud Rizwan d3e32500ae fix(ollama): raise the response-start timeout default to 5 minutes so model cold loads don't error (#12839)
* fix(llms): retry Ollama response-start timeouts through the AI SDK retry loop

The pre-SDK handler wrapped Ollama chat calls in withRetry({ retryAllErrors:
true }), which silently rode out model cold loads: Ollama holds /api/chat open
while loading and only sends response headers once the model is ready, so the
first attempt of a large model routinely times out at 30s and a later retry
lands on the loaded model. The SDK path lost that behavior twice over: the
response-start timeout rejected with a plain Error (the AI SDK only retries
APICallError with isRetryable), and ai-sdk-ollama wraps every doStream failure
in its own OllamaError, hiding even a correctly-typed error from the retry
predicate. Net effect: one attempt, a surfaced timeout error, and no automatic
recovery - a regression vs the legacy extension for local models that load
slower than the timeout (cline/cline#12829).

Fix: withOllamaResponseTimeout now rejects with APICallError(isRetryable:
true) when its own timer fired (upstream aborts still propagate untouched),
and a restoreOllamaApiCallErrorMiddleware unwraps the buried APICallError from
OllamaError cause chains so streamText's built-in retry (2 retries with
backoff, ~96s of cold-load coverage) engages.

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

* style: biome format

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

* rework: raise Ollama response-start default to 5 minutes instead of retrying

Replaces the APICallError/retry-middleware approach: the 30s guillotine was
the actual root problem (Ollama sends response headers only after the model
cold-loads; killing a healthy request forces error/retry churn), so give the
response-start budget the same order of generosity other AI SDK-based agents
use (opencode: no default header timeout for custom providers, 5 minutes for
its only default) and delete the retry machinery. Unreachable servers still
fail instantly at the connection level, users can still cancel from the UI,
and an explicit requestTimeoutMs is still honored.

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

* revert Ollama timeout description copy, keep the new default values

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

* fix: forward Ollama request timeout and context window to standalone handlers

Greptile review catch on #12839: buildSdkProviderConfig never carried
requestTimeoutMs, so handlers built via buildApiHandler (commit message
generation) ignored an explicit user timeout — pre-existing, but material now
that the fallback default is 5 minutes. Reuse the session factory's
resolveOllamaProviderConfig so the standalone path honors the configured
timeout and the user's context window (num_ctx) instead of Ollama's 4096
default, keeping the two paths on one source of truth.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-01 19:19:48 -07:00
Saoud Rizwan b0cb179a06 fix(settings): persist base URL and API key edits made before provider config loads (#12840)
OpenAI Compatible and LiteLLM gated their base URL onChange (and API key
writes via canWrite) on the async provider config having loaded. Text typed
in that window hit a no-op onChange after the debounce cleared the
pending-edit flag, so the late initialValue resync wiped it and nothing was
saved. write() never needed loaded config, and useProviderConfig's request
sequencing already drops the stale initial read, so the guards are removed.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-01 18:26:09 -07:00
Saoud Rizwan f57c7944ec fix(vscode): keep plan/act input border in sync with actual textarea focus (#12841)
Sending a message (Enter or send button) cleared the isTextAreaFocused
flag without blurring the textarea. Since the DOM element stayed focused,
onFocus never re-fired (programmatic .focus() on an already-focused
element is a no-op), so the mode-colored outline stayed hidden until a
real blur/refocus cycle - which is why toggling Plan/Act mode brought it
back. Stop clearing the flag on send; blur is already handled by the
onBlur handler.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-01 18:02:59 -07:00
Saoud Rizwan e450cbf5dd fix(vscode): settle pending tool approval when a message edit replaces the session (#12836)
Editing a previous message while a tool approval prompt was pending left the
old session's approval promise parked forever: the superseded run stayed
suspended awaiting an answer that could never come, and the stale resolver
kept intercepting later ask responses. Clear pending interactions before
starting the replacement session, exactly like cancelTask / clearTask /
task-switch / mode-change already do.

Ref #12827

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-01 18:02:01 -07:00
Saoud Rizwan 830ef5d288 Fix AskSage custom API URL being ignored at inference time (#12843)
The runtime baseUrlMap in resolveBaseUrl lacked the asksage ->
asksageApiUrl mapping (present in store.ts, effective-config.ts, and the
legacy migration), so a custom AskSage API URL saved in legacy state was
never read and requests fell through to the builtin default
https://api.asksage.ai/server.

Also write the URL through the SDK provider-config store in
AskSageProvider.tsx (mirroring AnthropicProvider) so providers.json
stays in sync for CLI/desktop hosts; the store mirrors baseUrl back to
the legacy asksageApiUrl state key, keeping the /get-models fetch and
legacy readers working.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-01 18:00:58 -07:00
Saoud Rizwan 723ee3b610 fix(settings): persist Qwen/Moonshot API line to providers.json (#12837)
Convert the Qwen and Moonshot regional API line dropdowns from
legacy-state-only writes to useProviderConfig().write({ apiLine }),
matching the Z AI pattern. The host store mirrors the write back to the
legacy qwenApiLine/moonshotApiLine state keys, so a single write keeps
providers.json (read by the CLI and desktop app) and the legacy
StateManager (read by the VS Code session factory) in sync.

Adds store tests pinning the dual-write mirroring and webview component
tests for the dropdowns' write and display behavior.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-01 18:00:16 -07:00
cline-cloud[bot] 75e1cc7a6f fix(settings): restore custom URL toggle after clear failure (#12838)
* fix(settings): restore custom URL toggle after clear failure

* fix(settings): cancel pending URL edit before clear

---------

Co-authored-by: Cline <noreply@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-08-01 17:55:54 -07:00
Sufiyan Khan 96f8fbf671 fix(terminal): complete commands on shell execution end (#12658) 2026-08-01 17:26:42 -07:00
Tran Binh Minh 211cc035bd fix(vscode): include untracked files in commit message generation (#12069)
* fix(vscode): include untracked files in commit message generation

getGitDiff only ran git diff --staged and git diff HEAD, neither of which reports untracked files, so an add-only working tree failed with 'No changes in workspace for commit message'. Gather untracked files and diff each against /dev/null via execFile (argv, no shell) so add-only trees work and special-char filenames are safe.

Closes #12060

* fix(vscode): include untracked files alongside tracked changes

Address review: append untracked-file diffs in the non-staged path instead of gating on an empty diff, so a mix of edited tracked files and new untracked files includes both. Re-throw git exit codes other than 1 (files differ) so real errors aren't swallowed. Use a named, non-runnable label for the output header. Adds a mixed tracked+untracked test.

Refs #12060

---------

Co-authored-by: Minhkunn <minh.12072k6@gmail.com>
2026-08-01 17:14:54 -07:00
Dominic Cooney 1e9e3c7a3e fix(cli): restore the formatDisplayUserInput import in root.tsx (#12844)
#12831 removed the import while rewriting checkpoint restore, and #12830
landed on top of it adding a usage that assumed the import was still
there. apps/cli typecheck has failed on main since, which fails the
sdk-test Quality Checks job on every PR touching sdk/**.

Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-08-01 16:53:50 -07:00
Saoud Rizwan e543c692c7 fix(settings): persist custom base URL checkbox state and stop keystroke loss in URL fields (#12834)
* fix(settings): persist and display custom base URL checkbox, stop keystroke loss in URL fields

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

* fix(settings): drop stale provider-config responses and re-read after failed writes

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

* fix(settings): skip write-failure recovery read when a newer write is in flight

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-01 13:15:07 -07:00
Saoud Rizwan e34e794624 fix(cli): show plain text when prefilling a restored message (#12830)
After a checkpoint restore (/undo or Esc Esc), the rewound user message is
dropped into the input box to edit and re-send. It was prefilled from the
raw stored text, which the runtime wraps in a <user_input mode="...">
envelope, so the input showed '<user_input mode="act">...</user_input>'
instead of what the user typed. Prefill the display form via
formatDisplayUserInput (already used for the picker preview and imported in
this file), which strips the envelope and preserves slash-command display
form.
2026-08-01 10:01:16 -07:00
Saoud Rizwan 8d078f59bd fix(checkpoints): create reliably, full workspace rewind on restore, repair CLI /undo (#12831)
* fix(core): create checkpoints reliably across hosts, restarts, and compaction

#12691 moved checkpoint run-boundary detection into a beforeRun hook that
recorded snapshot.messages.length, assuming the run's user prompt is
appended afterwards. SessionRuntime (VS Code + CLI) instead seeds the
prompt into initialMessages and calls run(""), so the beforeRun delta is
always empty and no checkpoints were ever created in either surface.

Gate checkpoint creation on two signals instead of the fragile in-memory
delta alone:
- introducedUserRun: the beforeRun delta contains a new user turn. Covers
  hosts that pass the prompt as run input and refreshes the entry on
  edit-and-regenerate.
- alreadyCheckpointed: the run count already exists in the DURABLE session
  checkpoint history. Covers the seeded-prompt path and, unlike an
  in-memory counter, still holds after a process restart.
Skip only when neither applies (a continuation/resumption re-running an
already-checkpointed run), so a reopened session can't overwrite a good
pre-run snapshot with the mutated workspace. Run numbering uses the
span-aware countUserRunMessages so it survives compaction folding turns
into one summary message.

Adds regression tests for the seeded-prompt creation, the reopen-without-
new-turn overwrite case, and the first-turn-after-compaction case.

* fix(cli): number /undo checkpoints span-aware so restore can map them

The interactive /undo picker counted every role="user" message when
assigning run numbers to checkpoints. Tool-result messages also carry
role "user", so any turn that used tools got an inflated run number; the
picker then handed that number to the core, whose span-aware
findUserRunMessage could not map it and aborted with 'Could not find user
message for run N'. Restore was effectively unusable whenever the agent
called a tool.

Count runs with the core's getUserRunSpan (tool results contribute 0, a
compaction summary spans the turns it folded) so the picker's run numbers
match what the core records and resolves. Extracted the item-building into
a pure buildCheckpointPickerItems helper with unit coverage for the
tool-result and compaction cases.

* fix(core): capture untracked files in checkpoints as a third parent

Checkpoint creation used plain `git stash create`, which cannot include
untracked files (no -u support). Restore therefore had no way to bring back
a file Cline created during a task, so a full rewind was impossible.

Synthesize a stash-shaped snapshot commit that also records untracked,
non-ignored files as a third parent - exactly like
`git stash create --include-untracked` - without touching the working tree,
the real index, or the stash list: list `ls-files --others
--exclude-standard`, stage into a temp GIT_INDEX_FILE, write-tree +
commit-tree to get the untracked parent, then rebuild the stash commit with
that extra parent. When the tracked worktree is clean but untracked files
exist, synthesize the stash from HEAD so they are still captured instead of
falling back to a bare HEAD-commit checkpoint. Fully clean worktrees still
use the HEAD-commit fallback.

* fix(core): full workspace rewind on restore for snapshot checkpoints

Restore now rewinds untracked files generation-aware:
- If the checkpoint carries an untracked third parent (a snapshot from
  createWorktreeStashCommit), do a full rewind: reset tracked to the base,
  `git clean -fd` to drop files created after the checkpoint (and clear the
  worktree so `stash apply` cannot hit an "already exists" conflict), then
  `git stash apply`, which restores each captured untracked file to its
  checkpoint-time content from the third parent. `git clean -fd` (no -x)
  leaves .gitignored paths - build output, node_modules, .env - alone. This
  is safe because everything removed is either recreated from ^3 or postdates
  the checkpoint, and the pre-restore recovery snapshot (stash push
  --include-untracked) can roll the whole operation back.
- If the checkpoint has no third parent (legacy 2-parent stashes and
  HEAD-commit fallbacks from before capture existed), keep the conservative
  behavior: never touch untracked files, since nothing can reconstruct them.

This makes 'Reset Code' / '/undo' a true rewind: a file Cline created in an
early turn and ruined later comes back to the early-turn version.
2026-08-01 10:00:33 -07:00
cline-cloud[bot] 94f897f559 fix(telemetry): preserve provider error details (#12824)
Co-authored-by: Cline Cloud Agent <cline-cloud-agent@users.noreply.github.com>
2026-08-01 10:06:02 +02:00
Saoud Rizwan 4b529f5f81 fix(telemetry): stop counting tool use mistake notices as provider API errors (and stop double-counting them) (#12820)
* fix(telemetry): single classified emitter for provider API errors, gated on terminal failures

* chore: remove explanatory comment block from agent-events.ts

* feat(telemetry): stamp terminal=true on SDK provider failure events

* chore: remove dead notice api_error capture (no producer emits that reason)

* refactor(telemetry): rename provider-failure 'terminal' flag to 'fatal' (terminal is the shell in Cline)

* refactor(telemetry): drop the fatal flag - only user-surfaced failures are reported on both bundles
2026-07-31 23:53:58 -07:00
Tomás Barreiro 978155814e Fix Text rendering when restarting ACP sessions (#12823) 2026-07-31 23:48:41 -07:00
Saoud Rizwan 055210a2bf fix(cli): don't let the ClinePass promo dialog trap users whose terminal drops Esc (#12819)
* fix(cli): don't let the ClinePass promo dialog trap users whose terminal drops Esc

The promo dialog could only be dismissed with Escape, and Esc is the
least reliable key across terminals: it arrives as a bare \x1b that
needs timeout disambiguation, and Bun's Windows console input layer is
known to swallow it (Windows PowerShell users reported being unable to
dismiss the dialog at all). Worse, the 'shown' marker was only written
when the dialog closed, so a user who force-quit saw the promo again on
every launch.

- Any key other than Enter now dismisses the dialog (Enter still opens
  the subscription page)
- The shown marker is persisted when the dialog is displayed, not when
  it is dismissed, so a force-quit never loops the promo
- Add a tuistory e2e test covering marker timing and any-key dismissal

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

* fix(cli): let any key cancel the OAuth waiting screen

Like the ClinePass promo, the OAuth wait screen was dismissible only
with Esc (plus K for the API-key fallback when offered) while blocking
on a browser flow that may never complete — a trap on terminals that
drop Esc. Any key other than K now cancels the pending auth attempt;
K still switches to manual API key entry when available.

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

* fix(cli): don't let a modifier keypress dismiss link-bearing dialogs

The ClinePass promo and OAuth wait screens both render a URL the user
opens by holding Cmd/Ctrl and clicking. With 'any key closes', that
modifier keystroke could tear the dialog out from under the click. Add
a shared isAnyKeyDismiss() guard so only unmodified keys dismiss; keys
held with ctrl/meta/super/hyper (and bare modifier presses) are ignored.
Enter still opens the promo and K still opens manual API key entry.

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

* revert(cli): persist promo shown-marker on dismiss again

Now that any key dismisses the promo, users can reliably close it, so
there's no need to write the shown-marker eagerly on display. Restore
persisting it in the dialog's finally() and update the e2e assertion.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-31 23:44:29 -07:00
Saoud Rizwan 8e68b14673 chore(sdk): release v0.0.68 2026-07-31 22:15:38 -07:00
Saoud Rizwan 4061dda034 fix(cli): close remaining open-package gaps in browser URL opening (#12822)
Follow-up to #12782, which replaced the open package with openUrlInBrowser
but missed two call sites and dropped some platform handling the package
provided:

- Migrate the two remaining open users (skills marketplace open in
  tui/root.tsx and ACP OAuth in acp/auth.ts) to openUrlInBrowser; the
  listenerless-child crash fixed by #12782 was still reachable there.
- Treat containers running on a WSL2 kernel (Docker Desktop for Windows,
  devcontainers) as plain Linux: /proc/version says microsoft but there is
  no Windows interop, so use xdg-open instead of powershell.exe (matches
  the is-inside-container check open@10 performed).
- Try opener candidates in order: on WSL, powershell.exe on PATH, then the
  absolute /mnt/c/... path (covers appendWindowsPath=false), then xdg-open
  (sandboxed WSL with WSLg); on win32, the %SystemRoot% absolute PowerShell
  path first (what open@10 used), then PATH lookup.
- Convert Linux file paths to \\wsl$ UNC paths via wslpath before handing
  them to Start-Process, so 'cline doctor log' works on WSL.
- Remove the now-unused open dependency from apps/cli.
2026-07-31 22:07:36 -07:00
Dominic Cooney b93a8cd442 fix(vscode): run Store PowerShell profiles correctly (#12802)
* fix(vscode): run Store PowerShell profiles correctly

* test(vscode): cover Store PowerShell background execution

* fix(core): handle shell stdin write failures

* test(windows): verify legacy PowerShell execution
2026-07-31 22:03:10 -07:00
Saoud Rizwan 72561771a7 docs: move ACP editor integration to a dedicated Usage page (#12821) 2026-07-31 21:26:14 -07:00
Mikołaj Kondratek 123477dcd9 chore: remove dead host-side capture methods for core-owned telemetry events (#12818)
captureDiffEditFailure and captureWorkspaceInitError have no callers: SDK core
is the sole emitter of task.diff_edit_failed and workspace.init_error. Keeping
callable host-side capture APIs for core-owned events is how the
task.provider_api_error double-emission happened — a future host caller would
silently double-count these events with no type error or failing test. Also
drops the two event-name constants, which were only referenced by the removed
methods.
2026-07-31 21:03:37 -07:00
Saoud Rizwan 2ac20c647c docs: add Editor Integration (ACP) section to CLI overview (#12808)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-31 20:52:37 -07:00
Saoud Rizwan 316efc4521 Resolve recommended-model display names once in the SDK feed (#12806)
* feat(core): resolve display-ready names in fetchClineRecommendedModels

* refactor(cli,vscode): render recommended-model names from the enriched feed

* fix(core): resolve catalog names through vercel/openrouter id aliases

* fix(core): share one timeout budget across the feed and catalog lookups

Greptile flagged that resolveDisplayNames started a fresh timeoutMs window
after the recommendation request finished, so a slow endpoint plus a cold
or hung catalog could keep the picker loading for ~2x the timeout. The
catalog race now gets only the budget remaining from a single deadline;
an already-cached catalog still applies on an exhausted budget because
its promise resolves ahead of the zero-delay timer.
2026-07-31 20:49:59 -07:00
Saoud Rizwan b0ee2cc80a ci(vscode): publish combined stable VSIX to Open VSX and harden ab-package gates (#12805)
* ci(vscode): publish combined stable VSIX to Open VSX and harden ab-package gates

* ci(vscode): address greptile review — env-routed version input, bookkeeping survives Open VSX failure
2026-07-31 20:09:09 -07:00
Cline Test a063317218 fix(connectors): strip the Slack bot mention from incoming messages (#12780)
* fix(cli): strip the Slack bot mention from incoming connector messages

Slack delivers an at-mention of the app as `<@U0B8E8H3U1F> hi`, and the chat
SDK deliberately leaves the bot's own mention unresolved so mention detection
keeps working - flattening it to `@U0B8E8H3U1F hi`. The connector forwarded
that verbatim, so the agent saw the raw bot id at the front of every
mention-triggered turn.

Strip the leading self-mention in onNewMention/onSubscribedMessage before the
approval-reply check and handleTurn, resolving the bot id from the adapter
(request-scoped in multi-workspace mode) with a fallback to the event envelope
authorizations. Mentions of other users and inline mentions are preserved, and
a bare mention is left as-is so the turn is not dropped as empty input.

* fix(cli): only strip a complete Slack bot mention, not an id prefix

The `<@ID>` and `<@ID|name>` alternatives in stripSlackBotMention are
terminated by `>`, but the SDK-flattened bare `@ID` alternative had no
trailing boundary, so it also matched the start of a longer id. With bot id
`U123`, a message addressed to a different user - `@U1234 help` - was
rewritten to `4 help`, corrupting both the approval-reply check and the text
handed to the agent.

Require the flattened alternative to be followed by a non-id character with a
`(?![A-Za-z0-9])` lookahead, so it only matches a complete Slack id. A plain
`\b` cannot express this, because Slack ids end in word characters and `\b`
still matches between `U123` and `4`.

Existing behaviour is unchanged: angle-bracket and flattened self-mentions are
still stripped, repeated leading mentions still collapse, trailing `[\s,:]`
separators are still consumed, other users' and inline mentions are preserved,
and a bare mention is still left untouched so the turn is not dropped as empty.

Adds regression tests for the prefix collision, which fail against the previous
regex and pass with this one.

---------

Co-authored-by: cline-test-bot <cline-test-bot@users.noreply.github.com>
2026-07-31 19:31:04 -07:00
Tomás Barreiro 6d10f363b3 Add CLinePass as a provider on ACP (#12793)
* Add CLinePass as a provider on ACP

* Resolve default model id
2026-08-01 03:28:37 +02:00
Mikołaj Kondratek ab0bc93182 fix: surface upstream provider error from gateway-forwarded stream failures (#12800)
* fix: surface upstream provider error from gateway-forwarded stream failures

Vercel AI Gateway streams upstream rejections (e.g. Alibaba Qwen context-
length errors) wrapped in its own parse failure: the top-level message is
just 'Stream error occurred' and the cause is an internal ZodError, while
the real rejection is JSON-encoded in value.error_message. Unwrap it so
users see 'This model's maximum context length is 40960 tokens...' instead
of a raw Zod issue dump.

Also fall back to JSON.stringify for opaque object errors so the UI never
renders '[object Object]'.

* refactor(llms): use shared safe-JSON helpers and a named type guard in extractErrorMessage
2026-07-31 18:08:32 -07:00
Saoud Rizwan 22ded04bc4 Resolve display names for Cline free models in the CLI and extension model pickers (#12801)
* fix(llms): resolve OpenRouter display names for all Cline free models

* fix(vscode): resolve featured model card display names from the provider catalog

* fix(vscode): fall back to endpoint-provided names on featured model cards
2026-07-31 17:25:16 -07:00
Saoud Rizwan edaab58716 Add tuistory-based TUI e2e harness for the CLI (#12796)
* Add tuistory-based TUI e2e harness for the CLI

Evaluates https://github.com/remorses/tuistory as a Playwright-style
driver for the interactive TUI. Adds:

- tuistory devDependency in apps/cli
- test:e2e:tuistory script + vitest.tuistory.e2e.config.ts
- src/cli.tuistory.e2e.test.ts: ports the script(1)-based interactive
  smoke tests to reactive waitForText/screen-state assertions against a
  real PTY + Ghostty terminal emulator (5 tests, ~11s, no fixed sleeps)
- DEVELOPMENT.md docs for the vitest suite and the tuistory session CLI
  agents can use to manually drive the TUI headlessly

* Add tuistory agent skill (.cline/skills, symlinked to .claude/.agents)

Teaches coding agents to drive the Cline TUI headlessly via tuistory
sessions (launch with isolated env, reactive wait, snapshot/screenshot,
observe-act-observe loop) and to write launchTerminal()-based e2e tests,
closing the loop for cloud agents testing apps/cli.
2026-07-31 17:14:22 -07:00
Tomás Barreiro 0ec999b31a Remove CLI Promo Code (#12797)
* Remove CLI Promo Code

* fix import

* fix tests

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-31 16:52:58 -07:00
Octopus 3845be53b3 fix(llms): preserve video input capability (#12787)
Co-authored-by: octo-patch <266937838+octo-patch@users.noreply.github.com>
2026-07-31 16:39:50 -07:00
Saoud Rizwan 8014a05088 Revert "fix(cli): don't crash when no browser opener binary exists (#12782)" (#12799)
This reverts commit c7850423f1.
2026-07-31 15:58:03 -07:00
Saoud Rizwan c7850423f1 fix(cli): don't crash when no browser opener binary exists (#12782)
* fix(cli): don't crash on browser-open failure when no opener binary exists

open() with { wait: false } resolves to the detached child process before
the opener binary is known to exist. On hosts without one (e.g. xdg-open
on headless Linux), the failure arrives as an async 'error' event on the
listenerless child, escalating to an uncaughtException that kills the CLI
— bypassing every try/catch and .catch() at the call sites. Hitting
"Sign in with Cline" from the welcome screen reliably crashed the TUI in
containers.

Route all browser opens through a shared openUrlInBrowser() helper that
attaches the error listener and reports failure via its returned promise,
so flows fall back to their existing "visit the URL below" messaging.

* fix(cli): attach opener error listeners in the same tick as spawn

Greptile's review caught that the helper attached its listeners only after
awaiting open()'s promise. Empirically that window is safe under Node 22
(the listener wins) but real under Bun — the runtime the compiled CLI
ships on — where the missing-binary ENOENT 'error' event fires before the
microtask queue drains, reproducing the exact crash this helper exists to
prevent.

macOS and non-WSL Linux now spawn their opener (open / xdg-open) directly
with listeners attached in the same synchronous tick, which both runtimes
guarantee can never miss the event. Windows and WSL keep delegating to the
open package for its shell quoting and interop routing; their openers
(cmd/powershell) always exist, so the post-await path cannot hit ENOENT.
The regression test now emits the error on nextTick — before microtasks —
which fails against the previous implementation.

* fix(cli): drop the open package — same-tick opener spawn on every platform

The win32/WSL delegate path still attached listeners after awaiting
open()'s promise, leaving a narrow uncaught-error window under Bun for
emittable spawn failures (e.g. AV-blocked EPERM). Spawn the opener
directly everywhere instead: open on macOS, xdg-open on Linux, and
powershell -EncodedCommand on Windows/WSL — the base64-encoded
Start-Process command sidesteps cmd/PowerShell quoting of URLs entirely,
so nothing is ever shell-interpolated.
2026-07-31 14:29:14 -07:00
Saoud Rizwan ed5f3031b0 fix(cli): silence dialog-container 'not a child of __root__' warning on exit (#12795)
On CLI exit, renderer.destroy() runs root.destroyRecursively() before
React flushes the DialogProvider's passive unmount cleanup, so the
dialog container is already detached when the cleanup calls
renderer.root.remove(container), triggering OpenTUI's 'Renderable with
id dialog-container is not a child of __root__, skipping remove'
warning. Drop the explicit remove from the patched @opentui-ui/dialog
provider cleanup (react + solid): Renderable.destroy() already detaches
from its parent when attached and no-ops when already destroyed.
2026-07-31 14:23:17 -07:00
Tomás Barreiro 3f38bd516f Add Organization selector to ACP (#12774)
* Add Organization selector to ACP

* linter
2026-07-31 12:01:46 -07:00
Tomás Barreiro be8c16b0ae Fix ACP session resolution (#12756)
* Fix ACP session resolution

* Use the selected provider/model

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-31 10:09:46 -07:00
Sufiyan Khan 0074b7222f fix(vscode): remove attachments from edited messages (#12578) 2026-07-31 10:05:30 -07:00
Mikołaj Kondratek ce81bc6855 fix(core): omit workspace hint for filesystem root paths (#12778)
basename("/") is an empty string, which WorkspaceInfoSchema rejects
(hint is z.string().min(1).optional()), so upsertWorkspaceInfo threw a
ZodError for any session rooted at the filesystem root — e.g. the
desktop app launched from the Dock with cwd "/" — and commands never
ran. Omit the hint instead of storing an empty string.
2026-07-31 17:50:46 +02:00
Bee e0803124b2 fix(cli): restart hub via installed wrapper after update (#12755)
Launch the hub through CLINE_WRAPPER_PATH after Unix self-updates so npm 12 does not reuse a deleted cached executable. Preserve the in-process fallback for Windows and development builds, and add coverage for success and failure paths.

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-31 01:01:22 -07:00
Saoud Rizwan 644e841737 chore(vscode): bump to 4.1.2 for release 2026-07-30 22:02:40 -07:00
Saoud Rizwan 5c6753e6d7 Show Legacy/Next extension variant in the settings About page (#12777) 2026-07-30 21:57:04 -07:00
Saoud Rizwan fdcc5367dc chore(vscode): bump to 4.1.1 for release 2026-07-30 21:08:32 -07:00
Dominic Cooney 901fdbc5cb Remove vestigial MCP server-key machinery from McpHub (#12773)
The uid/mcpServerKeys registry existed to encode server names into
native tool-call function names and decode them back at dispatch.
That encode/decode path was removed with the extension host
(c4c126bee): tool names are now built by the SDK's deterministic
defaultMcpToolNameTransform and execution closes over the server
name directly, so getMcpServerByKey has no callers and the keys are
write-only state. Delete the registry, the uid field, and the
deleteServerKey callback plumbing.

Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-07-30 21:05:58 -07:00
Saoud Rizwan 69e3149f5f ci(vscode): add tag, GitHub Release, and Slack bookkeeping to combined publish workflow 2026-07-30 20:59:08 -07:00
Saoud Rizwan 3a3d0c1bc3 chore(vscode): bump to 4.1.0 and backport legacy 4.0.x changelog to main 2026-07-30 20:59:08 -07:00
Tomás Barreiro 0746ea72bf Improve ACP agent errors (#12766)
* handle finish reasons

* describe agent error

* use SDK functions

* Add isLikelyAuthError to the check

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-30 19:12:38 -07:00
Sebastien Tardif f0d5ede555 fix: replace flaky setTimeout waits with drainForTesting in BannerService tests (#10530)
BannerService tests still used 10ms sleeps for background fetch completion.
On slow CI runners that races mocha timeouts. drainForTesting() already
exists and awaits the in-flight fetch promise deterministically.

Rebased onto monorepo main (apps/vscode path).

Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
2026-07-30 18:24:00 -07:00
Saoud Rizwan ed821a6456 chore(cli): release v3.0.48 2026-07-30 18:17:43 -07:00
Saoud Rizwan f36b59c9cb chore(sdk): release v0.0.67 2026-07-30 18:03:17 -07:00
Saoud Rizwan 311959e757 ci(vscode): add test gate to combined A/B publish + publish-extension skill (#12764)
* ci(vscode): gate the combined A/B package workflow on both bundles' test suites

* docs(skills): add publish-extension skill for VS Code extension releases

* ci(vscode): pin tested revision for next bundle and refuse publishing untested next-refs

* ci(vscode): pin legacy bundle to the revision its test gate ran against
2026-07-30 17:54:29 -07:00
Saoud Rizwan f21e6faf1c fix(vscode): show migrated model in settings instead of hardcoded default (#12768)
* fix(vscode): show migrated model in settings instead of hardcoded default

After the SDK provider migration a user who never explicitly picked a model
(i.e. took the legacy default) ends up with the model recorded in
providers.json but not in the mode-specific globalState fields the settings
picker reads. The OpenRouter picker and its info card then fell back to the
hardcoded openRouterDefaultModelId (claude-sonnet-4.5) and its pricing, while
the extension actually ran the migrated model (claude-sonnet-5).

- resolveModelInfo: when no model id is requested, honor the provider store's
  committed selection (which reads providers.json when the state field is
  empty) before substituting a catalog default.
- OpenRouterModelPicker: source the displayed model id/info from the
  authoritative resolver as the fallback when the mode fields are empty,
  instead of the hardcoded constant. Committed-field users are unaffected.

* fix(vscode): guard picker model info against resolver default substitution

Review hardening: the resolver substitutes its provider default for ids it
cannot resolve, so only trust its info when it answered for the id actually
displayed. Prefer the live catalog entry for the displayed id (synchronous
once fetched, which also removes the transient placeholder while the resolver
is in flight), and never render another model's metadata under the displayed
model's name. Also document why the act-then-plan readSelection order in the
empty-id branch cannot misattribute a mode-specific selection.
2026-07-30 17:51:21 -07:00
Saoud Rizwan 1cf19304f4 docs: restore 'open Cline in right sidebar' guide as section of IDE usage page (#12771) 2026-07-30 17:43:58 -07:00
Saoud Rizwan 09aec528d0 Fix task export button: resolve the SDK session folder reliably (#12772)
* Restore task export to markdown and show download button in all builds

* Render untyped tool outputs and object tool inputs as JSON in task export

* Open the task's SDK session folder from the export button and show it in all builds

* Keep the task header session-folder button dev-only
2026-07-30 17:34:29 -07:00
Saoud Rizwan 4ec2b68c7c Fix queued prompt row alignment and auto-scroll when queueing a message (#12767)
* Fix queued prompt row alignment and auto-scroll on queue

Center the dot, badges, and cancel button on the first text line of each queued prompt row (the X previously sat ~3px below the text), and re-pin the chat view to the bottom when a prompt is queued so the queue banner doesn't cover the end of the conversation.

* Don't treat task switches as queue growth for auto-scroll

Guard the queued-prompt auto-scroll effect on the displayed task's ts: switching to a task that already has queued prompts grows the count without a send from this webview, and should not hijack the newly opened conversation's scroll position.
2026-07-30 17:33:19 -07:00
Saoud Rizwan 56fd6bb1ce Fix hidden plan/act mode-switch prompts reappearing when resuming a task from history (#12769)
* fix(vscode): hide synthetic mode-switch and resumption prompts when rehydrating chat from history

* chore: add changeset
2026-07-30 17:04:32 -07:00
Tomás Barreiro 49c7a89882 Inject device_id into tracking events (#12708)
* Inject  into tracking events

* fix sandbox resolution
2026-07-31 01:13:17 +02:00
Bee b47851791c feat(desktop): support message editing & checkpoints (#12691)
* feat(desktop): support message editing & checkpoints

Fork sessions before a selected user run, trim checkpoint history, and restore prior messages so prompts can be edited safely. Update the chat UI and tool activity panels to support the editing flow and preserve horizontal scrolling for long content.

* fix(desktop): restore checkpoints when editing messages

* fix(core): infer kindless checkpoint types

* fix(core): preserve checkpoint run numbering

* fix(desktop): make message edit restores transactional

* fix(desktop): make checkpoint restores workspace-atomic
2026-07-30 15:50:33 -07:00
Saoud Rizwan c0a966c46a Restyle compact-task confirmation as a bordered card with even spacing (#12759)
The confirmation that appears when clicking the compact button in the
task header was a bare unstyled row with a stray bottom margin (my-2)
that stacked on the header card's own bottom padding, leaving a dead
gap under the buttons. It is now a distinct bordered card (editor
background against the header's toolbar surface) with a title, a short
description of what compacting does, and right-aligned Cancel/Compact
buttons, with symmetric spacing above and below.

Also drops the ContextWindow wrapper's bottom margin (my-1.5 -> mt-1.5)
so the row's bottom spacing matches the header padding, and adds a
ContextWindow Storybook story that mirrors the expanded TaskHeader
surface so the confirmation can be previewed in isolation.
2026-07-30 15:42:30 -07:00
Saoud Rizwan cf3f3e08eb fix: don't block provider switch UI on ClinePass account switch (#12758)
Selecting ClinePass in settings awaited a network round-trip (PUT
/active-account + possible token refresh) before postStateToWebview,
so the settings panel stayed on the previous provider until the
request finished. Make the personal-account switch fire-and-forget:
it was already best-effort, and auth state changes propagate to the
webview separately once it completes.

Also convert the helper's test to bun:test so it actually runs (the
mocha version was excluded by both the bun unit runner and the
vscode-test glob) and fix its stale null-vs-undefined assertion from
the SDK migration.
2026-07-30 15:42:06 -07:00
Ara 7712e44468 fix(vscode): show catalog-driven reasoning effort selector for xAI, Z AI, and Moonshot (#12754)
The xAI, Z AI, and Moonshot settings components were never wired to the
catalog's reasoning capability: xAI only offered a legacy low/high
checkbox hardcoded to grok-3-mini model ids, and Z AI / Moonshot had no
reasoning control at all, even though models.dev marks grok-4.5, glm-5,
kimi-k2-thinking, etc. as reasoning models. Every catalog-driven
provider (GenericProviderSettings, OpenRouter/Vercel/Requesty pickers)
already gates ReasoningEffortSelector on supportsReasoning.

Render the shared ReasoningEffortSelector in these three components when
the selected model's catalog info advertises reasoning, persisting the
choice to the provider config the same way GenericProviderSettings does.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-30 14:39:48 -07:00
Saoud Rizwan f77f584a32 fix(vscode): stop queued-prompt turns from getting stuck on Thinking (#12751)
* fix(vscode): stop queued-prompt turns from getting stuck on Thinking

When the SDK drains a queued prompt at the end of a turn, the new turn's
pending_prompt_submitted bookkeeping (isRunning=true, phase=streaming) always
runs before the previous turn's send promise unwinds in fireAndForgetSend.
That .then then unconditionally called setRunning(false), so the queued turn
ran with isRunning=false and its own turn-complete was mistaken for a
cancelled-turn straggler - the phase never left "streaming" and the chat
showed an endless Thinking indicator.

Track a monotonic turn epoch on SdkSessionLifecycle: immediate sends and
drained queue prompts bump it, and both the send-settled callbacks and the
event coordinator's turn-end handling skip their bookkeeping when a newer
turn has started since (covers the symmetric interleaving where the done
handler resumes after the drain and would clobber the queued turn's
streaming phase).

* Simplify: preserve only an actual cancel phase in the turn-complete straggler guard

Replaces the turn-epoch machinery with the minimal fix: the straggler
guard's intent is to preserve the cancel-set "resumable" phase, so key it
on the phase itself instead of the isRunning proxy. When the SDK drains a
queued prompt at turn end, the previous turn's send promise settles after
the queued turn already started and flips isRunning back to false
mid-turn; with the old guard the queued turn's real completion was then
mistaken for a cancel straggler and the phase stayed stuck on
"streaming" (endless Thinking). Checking for "resumable" lets that
completion resolve the terminal phase normally while cancel behavior is
unchanged.
2026-07-30 14:35:33 -07:00
Saoud Rizwan 2771760305 fix(vscode): stop thinking loader flickering around mid-turn tool calls (#12750)
The anti-flash grace period (added to stop the loader flashing at turn
end) also fired mid-turn, causing a visible hide/show/hide flicker right
before a tool row appeared:

- When a reasoning tail finalized while the turn kept streaming, the
  reasoning shimmer collapsed, the loader stayed hidden for the 500ms
  grace, popped in, then hid again when the tool row landed. Reasoning
  never ends a turn, so skip the grace for reasoning tails and hand the
  shimmer straight to the loader.
- When the loader was already visible below a streaming tool group, the
  group tail finalizing blinked it off for the grace period. The grace
  now only delays hidden -> visible transitions, never hides an
  already-visible loader.
2026-07-30 14:32:15 -07:00
Saoud Rizwan 16d0d04573 Show user message immediately when sending to a task opened from history (#12753)
* Show user message immediately when sending to a history-resumed task

Sending a message to a task opened from history routed through the
resume_task/resume_completed_task askResponse branch, which forced the
Thinking loader but never set the optimistic user_feedback bubble. The
extension only echoes the user's message after the full SDK session
resume completes, so the chat showed a Thinking indicator with no user
message until the (slow) resume finished.

Pass showPendingMessage on the resume branch like the other
non-streaming follow-up paths, so the user's message appears in the
chat immediately. The optimistic bubble reconciles with the extension's
say:user_feedback echo once the resume completes (identical raw text).

* Add changeset
2026-07-30 14:28:18 -07:00
Saoud Rizwan 3590b425eb fix(ci): de-flake Windows bun unit tests (hook PowerShell bridge + runner retry) (#12752) 2026-07-30 14:21:05 -07:00
Bee 12404f0a4b fix(core): add a plugin telemetry bridge (#12741)
* fix(core): add a plugin telemetry bridge

* fix(core): address plugin telemetry bridge review feedback

- Sanitization fallback now covers the whole executeTool IPC payload:
  `input` can be rewritten by beforeTool hooks or programmatic callers,
  so a non-serializable input degrades gracefully like the context does.
- The sandbox only offers ctx.telemetry when the host actually has a
  telemetry service (new PluginSandboxOptions.telemetryAvailable, derived
  from options.telemetry in the config loader), so feature-detecting
  ctx.telemetry means "someone is listening" in both execution modes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(core): plugin telemetry review round 2 — timer leak and setup-time fallback

- SubprocessSandbox.call: a synchronous child.send() throw (cyclic payload)
  left the pending timeout timer armed; it later fired and shut the sandbox
  down, killing unrelated in-flight calls. Cancel the pending entry and
  reject with the original error so serialization failures stay classifiable.
- plugin_telemetry events emitted during plugin setup() arrive before the
  session is registered, so the session-config lookup missed and setup-time
  telemetry was silently dropped. Route through a fallback telemetry service
  (extensionContext/local config/host default), mirroring handlePluginLog's
  fallback logger.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(core): classify BigInt IPC serialization errors for the sandbox fallback

Bun ("cannot serialize BigInt") and Node ("Do not know how to serialize a
BigInt") raise messages that did not match the cyclic/circular predicate, so
a bigint smuggled into tool input or context by a hook or programmatic caller
rethrew instead of retrying with the JSON-safe clone — which already drops
bigint leaves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 13:44:25 -07:00
Saoud Rizwan 6cb43f0309 fix(core): make compaction sidecar persistence reliable (#12747)
Auto-compaction state was silently rejected on every save ("Skipped
stale session compaction state"), forcing a full re-compaction — an
extra summarizer LLM call — on every turn past the trigger, and a
resume-time identity churn could leave a dead sidecar permanently
blocking replacements.

Three changes:

1. Stop hashing volatile transport identity. The source-prefix hash no
   longer includes message id/ts, which the codec regenerates on every
   wire/storage round-trip (a store's just-appended user turn has none
   yet; consolidated parallel tool results are re-split with minted ids
   on resume). The fingerprint now covers role, content, and durable
   metadata. Hash seed bumped to v2; v1 sidecars fail projection once
   and are replaced by the next compaction.

2. Validate persists against the exact source messages the state was
   computed over. createCompactionStateAwarePrepareTurn passes
   context.messages to saveState, and the local runtime host threads
   them into persistActiveSessionCompactionState instead of falling
   back to the conversation store's mid-turn shape.

3. Scope the count-based stale-write guard to states that still
   project. An unprojectable current state no longer blocks a
   newer-timestamped replacement, so invalidated sidecars self-heal
   instead of deadlocking the session.

All three regression tests fail on main and pass with this change.
2026-07-30 13:05:47 -07:00
Saoud Rizwan 3058563f37 fix(vscode): mark onboarding complete only after OAuth succeeds (#12744)
* fix(vscode): mark onboarding complete only after OAuth succeeds

The onboarding webview marked welcomeViewCompleted immediately after the
sign-in URL opened (accountLoginClicked resolves at URL-open time), so
Free/Frontier/ClinePass signups landed in chat signed out when the user
abandoned or failed browser auth, and the flag persisted across reloads.

Restore the classic extension behavior: the host (SdkAuthService) now
sets welcomeViewCompleted after the OAuth token exchange succeeds, in
createAuthRequest, handleAuthCallback, and the E2E mock login. The
webview persists the model selection up front, stays on the 'Almost
there!' step until auth completes, and fires the 'completed' funnel
event via a pending-intent module once clineUser arrives (mirroring the
pendingClinePassSubscribe pattern). This also fixes the legacy
WelcomeView fallback, whose 'Get Started for Free' never completed
onboarding after login.

* refactor(vscode): slim the onboarding-completion fix to its essentials

Drop the pending-telemetry module and App hook (the 'completed' funnel
event keeps its existing main-branch semantics, firing when the flow is
initiated, so no telemetry change in this PR), restore finishOnboarding
to its original shape with just a markCompleted parameter, and reduce
the host helper to a single setGlobalState call.
2026-07-30 12:40:44 -07:00
Saoud Rizwan 39de7479b8 Fix delayed and stale Plan/Act mode switches (#12732)
* fix(vscode): make plan act switches responsive

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

* fix(vscode): recognize completed plans with trailing usage

* fix(vscode): continue reopened completed plans

* fix(vscode): always publish state after mode rebuild

* fix(vscode): roll mode back when session replacement is refused

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-30 12:34:29 -07:00
Saoud Rizwan 86c2600a8a Show the thinking indicator immediately when starting a turn (#12746)
* Post streaming turn state to webview before session startup

The webview only learns the turn phase through full state posts, and the
first post after initTask happened only after startNewSession settled —
so the chat mounted with a stale idle TurnState and the thinking
indicator popped in noticeably late. Ship a state post right after the
initial task message is emitted, in parallel with session startup.

* Show thinking indicator optimistically on new-task submit

Capture the TurnState seq at the moment the newTask RPC is sent and
force the in-list Thinking loader row until a fresher TurnState arrives
(any phase), so the indicator renders together with the task message
instead of waiting for the streaming TurnState to round-trip. Rolled
back if the RPC fails; legacy (no turnState) hands off to the existing
tail heuristic once the task message lands.

* Paint the initial Thinking loader without waiting for Virtuoso

Frame-by-frame measurement showed the loader decision was true on the
chat view's first paint, but the synthetic in-list row still appeared
~150-200ms later: a cold-mounting virtualized list needs several frames
to measure and paint its first item. When the list has no visible rows
yet (new task just submitted), render the waiting row as a plain
element over the (empty) list instead; once any real row exists the
warm list takes over with the in-list row as before.

* Show thinking indicator immediately for follow-up messages too

Follow-ups had the same delay as new tasks: SdkController.askResponse
moves the phase to streaming but never posted state, so the webview
kept the stale terminal phase (hiding the loader) until the new turn's
first session event posted state. Post right after the phase change,
and generalize the webview's optimistic marker from new-task-only to
any turn-starting send (askResponse outside a streaming phase), with a
guard that never shows the loader while a content row is actively
streaming. Renames pendingNewTaskSeq to pendingTurnStartSeq.

* fix(vscode): render thinking loader synchronously
2026-07-30 12:28:40 -07:00
Saoud Rizwan f4230e475b fix(vscode): /compact UX — clear input, wrap divider, always update context header (#12735)
* fix(vscode): clear chat input immediately when /compact is submitted

* fix(vscode): let the compaction divider label wrap at narrow widths

* fix(vscode): update context-window header even when compaction grows the context

* chore: add changeset for /compact UX fixes

* docs(vscode): align getLastApiReqTotalTokens return doc with unclamped rescale
2026-07-30 12:13:33 -07:00
Saoud Rizwan 078abcd055 fix(ci): build shared package before the ui-publish desktop chat test
The desktop chat integration test renders components from @cline/ui, but
it also pulls @cline/shared/browser through the desktop app's own
message-content module. That subpath resolves to dist output no step in
this job produced, so the suite failed to collect.

Build @cline/shared before the test, and install the full workspace: the
two-package filter did not provide enough of the tree for that build.
2026-07-30 12:02:42 -07:00
Saoud Rizwan afc1229ab0 fix(ui): declare bun and node type dependencies
The ui-publish workflow installs only the @cline/ui and @cline/code
workspaces, so the root devDependencies that previously supplied the
'bun' and 'node' type roots were absent and tsc failed with TS2688.
Declare them on the package that requires them in its tsconfig types.

Also refreshes the stale @cline/code version recorded in bun.lock.
2026-07-30 12:02:42 -07:00
Saoud Rizwan 892837d352 Enable Auto Compact by default in VS Code (#12739)
* feat(vscode): enable Auto Compact by default

The SDK-based extension has no fallback context management: with auto
compact off, hitting the model's context window fails the request with a
provider error and retrying keeps failing (the legacy extension truncated
the oldest half of the conversation in this situation). The CLI already
defaults compaction on (agentic); align the extension with it.

* chore: add changeset for Auto Compact default-on
2026-07-30 12:02:02 -07:00
Ara 078d9f63f0 fix(models): tolerate null contextWindow/maxTokens in SDK catalog shapes (#12743)
* fix(models): tolerate null contextWindow/maxTokens in SDK catalog shapes

Live LiteLLM proxies report unknown model limits as explicit nulls in
/model/info (e.g. max_tokens: null). adaptSdkModelInfo only tolerated
undefined, so a single such model failed the entire catalog refresh and
left the model picker empty. Treat null like a missing value (matching
the existing pricing handling) and fall back to the safe defaults.

* Update apps/vscode/src/sdk/model-catalog/shape-adapter.ts

Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>

* Update apps/vscode/src/sdk/model-catalog/shape-adapter.ts

Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>

* fix(models): restore missing limit fallbacks

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>
2026-07-30 20:19:56 +02:00
Sufiyan Khan fba27b3c81 fix(vscode): preserve draft when toggling from act to plan (#12241)
* fix(vscode): preserve draft when toggling from act to plan

* fix(vscode): simplify mode toggle draft handling
2026-07-30 11:17:57 -07:00
Saoud Rizwan 6549bdbacc Show edited file after the diff preview closes (legacy parity) (#12731)
* Show edited file after diff preview closes, matching legacy behavior

* Add changeset

* Reveal destination after apply patch moves

* Skip reveal for superseded previews and aborted edits
2026-07-30 11:17:43 -07:00
Bee 55ccbb7d51 fix(cli): remove rendering multiple views in one Bun process (#10936)
* fix(cli): open history in the existing TUI

* refactor(cli): clarify history TUI startup target

* feat(cli): add history actions to TUI

* fix(cli): avoid empty session when resuming history

* fix(cli): fail history delete without session id

* fix(cli): dispatch resume hook from history picker
2026-07-30 11:16:51 -07:00
Tran Binh Minh f039b1419f fix(vscode): show per-file diff for multi-file apply_patch (#12086)
* fix(vscode): show per-file diff for multi-file apply_patch

apply_patch edits to multiple files rendered the entire multi-file patch in every per-file diff row. Split the patch into one tool message per file at content_end (mirroring the read_files split) so each row shows only that file's changes.

Closes #9904

* fix(vscode): address review on multi-file apply_patch split

Import the canonical PATCH_MARKERS from @cline/core instead of the local AP_MARKERS duplicate and export it through the core barrel. The cross-world import barrier the old comment claimed does not exist - apps/vscode already imports runtime values from @cline/core.

Route the apply_patch branch in sdkToolToClineSayTool through getApplyPatchString so the streaming and finalized rows derive their content from one source.

Handle the bare-string apply_patch input. ApplyPatchInputUnionSchema accepts { input: string } | string; a bare two-file patch made getApplyPatchString return undefined, so both content_start and content_end produced one empty-path row instead of the per-file split. Return the raw string when the field lookup finds nothing, with a start/end reconciliation test.

Refs #9904

---------

Co-authored-by: Minhkunn <minh.12072k6@gmail.com>
2026-07-30 11:13:45 -07:00
Saoud Rizwan 97c558ffe8 chore(ui): release v0.2.0-next.1 2026-07-30 11:02:02 -07:00
Saoud Rizwan 8f6f1652e1 Improve slash command description contrast on selection (#12742)
* fix slash command hover text contrast

* remove slash menu regression test
2026-07-30 10:40:54 -07:00
cline-cloud[bot] 26ee3abf82 fix(core): stabilize Windows SDK tests (#12722)
* fix(core): stabilize Windows SDK tests

* fix(core): handle late subprocess stdin errors

* test(core): verify shell process cleanup

* test(core): budget Windows PowerShell hook

---------

Co-authored-by: Cline Bot <bot@cline.bot>
Co-authored-by: Dominic Cooney <dominic.cooney@cline.bot>
2026-07-30 19:01:15 +02:00
cline-cloud[bot] d2b674bb9d fix(vscode): restore macOS E2E launch (#12726)
Co-authored-by: Dominic Cooney <dominic.cooney@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-30 03:04:21 -07:00
Saoud Rizwan 2ce4facd9d fix(vscode): show thinking immediately after submit (#12733)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-30 02:59:38 -07:00
Saoud Rizwan ac4724166d Disable feature tips by default in the VS Code extension (#12730)
* Disable feature tips by default in VS Code extension

* Add changeset for feature tips default change
2026-07-30 00:58:42 -07:00
Cline Test 2131bbba0c fix(connectors): recover Slack thread mapping when session is gone (#12727)
* fix(connectors): recover Slack thread mapping when session is gone

A connector thread binding can outlive its runtime session (hub restart,
session abort, retention cleanup). When that happened the thread stayed
pinned to a dead session id and every subsequent turn failed with
`session_not_found`, so the bot replied "Slack bridge error: session not
found" forever with no way to recover short of editing threads.json.

Drop the stale binding and replay the turn once against a brand new
session. Both the normal turn path and the steering path are covered.

Adds forgetThreadSession() to session-runtime and 3 regression tests.

* fix(connectors): serialize stale session recovery

---------

Co-authored-by: cline-test-bot <cline-test-bot@users.noreply.github.com>
Co-authored-by: abeatrix <beatrix@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-29 23:57:53 -07:00
Saoud Rizwan 792738eeed fix(vscode): auto-proceed long-running terminal commands (#12712)
* fix(vscode): auto-proceed long-running terminal commands

* chore(vscode): raise terminal auto-proceed timeout to 60s

* chore(vscode): raise terminal auto-proceed timeout to 300s
2026-07-29 23:30:52 -07:00
Saoud Rizwan 0ac7d0bdb5 remove Enable R1 messages format option from OpenAI Compatible provider (#12729) 2026-07-29 23:28:02 -07:00
Saoud Rizwan 4fa42771a7 Consolidate per-provider model refresh handlers onto the SDK catalog (#12716)
* Consolidate per-provider model refresh handlers into the SDK

The VS Code extension resolved model catalogs from two sources: the SDK
catalog (models.dev-backed) used by resolveModelInfo/task header, and
host-side refresh handlers (refreshOpenRouterModels & co.) used by the
settings pickers. This dual-source split produced inconsistencies like
ENG-2345.

SDK (@cline/core):
- New rich live model sources (live-model-sources.ts) ported from the
  extension handlers: OpenRouter (pricing incl. cache read/write,
  descriptions, image support, thinking config, tiers/global-endpoint
  metadata, curated overrides, stealth models), Vercel AI Gateway, and
  Hugging Face. Keyed by generated catalog key so cline shares
  OpenRouter's live data.
- mergeKnownModels layers rich live entries field-wise on top of the
  curated catalog (live fields win, curated fields fill gaps) instead of
  the modelsSourceUrl replace semantics.
- New Groq and Requesty private fetchers (API-key gated); Baseten
  private fetcher now parses live pricing and reasoning support and is
  enriched from the curated catalog.

Extension (apps/vscode):
- refreshOpenRouterModels/Groq/Baseten/VercelAiGateway/HuggingFace/
  Hicap/Requesty are now thin delegates over the SDK provider catalog;
  all bespoke fetch/parse/disk-cache code is deleted.
- shape-adapter maps the SDK's thinkingConfig, temperature,
  global-endpoint capability, and metadata tiers onto the extension
  ModelInfo.
- Removed the now-unused StateManager models cache, per-provider disk
  cache files, and the dead readOpenRouterModels stub.

Fixes ENG-2381.

* Simplify: rely on the SDK's models.dev catalog, no rich live sources

Drop the ported per-provider live fetchers and curated overrides
(live-model-sources.ts) and all SDK merge changes. The extension now does
exactly what the CLI does: refresh handlers resolve through
resolveProviderConfig, which serves the models.dev-backed catalog
(bundled + runtime live refresh) plus the SDK's pre-existing
authenticated fetchers (Baseten/Hicap/LiteLLM/Poolside). No hardcoded
model info or per-model pricing workarounds remain anywhere.

Also reverts the shape-adapter additions since no SDK catalog source
populates thinkingConfig/temperature/metadata tiers today.

* Replace thinking-budget sliders with catalog-driven reasoning effort selection

Match the CLI's UX: every reasoning-capable model (SDK catalog
'reasoning' capability -> supportsReasoning) gets the Reasoning Effort
selector (none/low/medium/high/xhigh); the legacy 'Enable thinking' +
budget-tokens slider is removed everywhere, along with the hardcoded
per-provider thinking-model id lists (Anthropic, Claude Code, Bedrock,
Qwen) and claude/grok model-id heuristics in the OpenRouter, Vercel,
and Requesty pickers.

Effort changes now dual-write the provider-config reasoning settings
({enabled, effort}) that the session factory actually consumes - the
budget slider wrote legacy plan/act thinkingBudgetTokens state that
sessions already ignored. The utility request path
(buildSdkProviderConfig) drops its budget preference and forwards
effort only; the SDK translates effort into each provider's wire
format (including budget-token mapping where required).

* Gate picker reasoning-effort UI on live catalog entries

The OpenRouter/Vercel/Requesty pickers read the committed legacy
model-info snapshot, which provider-config writes can clear when a
resolution lands on a fallback source - selecting an effort made the
selector disappear. Gate on the live catalog map (with snapshot
fallback) instead; Requesty gates on the catalog only, since its
safe-default fallback over-reports reasoning support.

* Address review: honor legacy thinking budgets, dedupe refresh handlers

- Persisted thinking budgets are honored again (greptile P1 / review
  request): normalizeProviderReasoningSettings maps a stored
  reasoning.budgetTokens (written by older versions or the SDK's
  legacy-state migration) onto the effort scale and treats it as
  thinking-on, and buildSdkProviderConfig derives an effort from the
  legacy plan/act budget fields when no explicit effort exists. An
  explicit 'none' still wins. Shared mapping lives in
  reasoningEffortFromThinkingBudget with low/medium/high buckets.
- Extract resolveProviderModelsRecord into providerCatalogShared and
  collapse the seven refresh handlers onto it.
- Document the explicit OCA decision: its reasoning control is the
  API-driven effort dropdown; the removed budget slider wrote state no
  OCA request path consumed.

* Harden OpenRouter picker reasoning gate against placeholder metadata

Gate on the raw committed model-info snapshot instead of the hook's
default-info fallback, so a selected id that is absent from the catalog
can never inherit reasoning support from placeholder metadata (the
fallback carries no supportsReasoning today, but reading the raw field
removes the latent dependency).
2026-07-29 23:27:05 -07:00
Saoud Rizwan c91cd4be59 fix(vscode): clear pending approvals on task switch (#12705)
* fix(vscode): clear pending approvals on task switch

* feat(vscode): show compact slash command

* docs(vscode): explain task approval cleanup

* fix(vscode): settle pending questions on cleanup

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-29 22:15:12 -07:00
Saoud Rizwan 851ee033bc Fix checkpoint restores across session resumes (#12713)
* fix checkpoints across session resumes

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

* style checkpoints mapping helper

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-29 22:08:37 -07:00
Saoud Rizwan 192e6ffab2 fix(vscode): interrupt running terminal command when a task is cancelled (#12696)
Cancelling a task previously only detached Cline's listeners from an
in-flight foreground command (process.continue()); the spawned process
kept running in the user's terminal after cancellation.

Send Ctrl+C (ETX) to the terminal before detaching so the shell delivers
SIGINT to the foreground process group, actually stopping the command.
The terminal is left open for reuse, and cancellation still succeeds even
if the interrupt write throws (e.g. terminal already disposed).
2026-07-29 21:45:43 -07:00
Saoud Rizwan 6b668fefdc fix(core): carry legacy OpenAI-compatible model-info overrides into seeded models.json (#12697)
The legacy-provider migration seeded the openai-compatible models.json
entry with hardcoded defaults (contextWindow 128k, no pricing/temperature/
maxTokens/R1 flag). Because later override migrations skip models that
already exist in models.json, the user's legacy planMode/actModeOpenAiModelInfo
overrides were silently discarded on first upgrade: context window,
max output tokens, input/output prices, temperature, supportsImages=false,
and isR1FormatRequired all reset to defaults.

Seed the entry from the mode-appropriate legacy model-info snapshot
instead, treating legacy sentinels (maxTokens -1, temperature 0,
prices 0) as unset.
2026-07-29 21:31:55 -07:00
Saoud Rizwan 8224ad1634 feat(llms): default OpenRouter provider to anthropic/claude-sonnet-5 (#12707)
Matches the legacy extension's OpenRouter default (openRouterDefaultModelId),
so users migrating from the legacy build without an explicitly selected model
keep the same default model instead of being silently moved to
anthropic/claude-sonnet-4.6.
2026-07-29 17:50:57 -07:00
Saoud Rizwan 96e8f51436 fix(core): don't abort config scan when .clinerules is a legacy single file (#12702)
A legacy single-file .clinerules at the workspace root made the config
watcher's scans of .clinerules/skills and .clinerules/workflows throw
ENOTDIR, which aborted the entire user-instruction refresh: workspace
rules, global rules, and the Skills view all silently failed to load.

Treat ENOTDIR like ENOENT in isIgnorableDirectoryError so a file in a
directory position simply yields no candidates. The .clinerules file
itself is still picked up by the file branch of discoverRulesLikeFiles.
2026-07-29 17:48:23 -07:00
Saoud Rizwan 5e5c4475bc fix(vscode): surface VS Code LM as a host provider (#12711)
* fix(vscode): register VS Code LM provider in catalog

* fix(vscode): use empty selector as vscode-lm catalog default model
2026-07-29 17:43:10 -07:00
Saoud Rizwan 66228fb30b Hide Plugins tab in extension Customize view (#12720) 2026-07-29 17:40:11 -07:00
Saoud Rizwan 7b8798c996 Fix built-in slash commands on the SDK runtime: /newtask aliases /compact, port /deep-planning expansion, hide /newrule and /reportbug (#12721)
* fix(vscode): hide /newrule and /deep-planning until their prompt expansions are ported to the SDK runtime

* feat(vscode): port the /newtask context handoff to the SDK runtime

Expand /newtask into explicit new_task-tool instructions in
SdkController.resolveSlashCommands (ported from legacy
newTaskToolResponse), register a custom new_task AgentTool that captures
the model-generated context summary and completes the run, and emit the
ask:"new_task" message on turn completion so the existing webview
"Start New Task with Context" button (which preloads a fresh task with
the ask text) becomes reachable again. Set the turn phase to
awaiting_followup when emitting the ask, since the completesRun
termination path skips the translator's usual end-of-turn status
handling.

* fix(vscode): hide /reportbug until its prompt expansion is ported to the SDK runtime

Also drop the feature tip promoting /reportbug so the UI doesn't
advertise a command that no longer autocompletes.

* Revert "feat(vscode): port the /newtask context handoff to the SDK runtime"

This reverts commit d9ad153aec.

* feat(vscode): make /newtask an alias of /compact

Condensing achieves /newtask's goal (continue working with a fresh,
summarized context window) without the legacy new_task tool, so the
webview intercepts /newtask alongside /compact and /smol and runs the
condense RPC. Menu description updated to match.

* feat(vscode): port the /deep-planning prompt expansion to the SDK runtime

Expand /deep-planning into the legacy generic-variant instructions
(silent investigation, targeted questions, implementation_plan.md) in
SdkController.resolveSlashCommands, ahead of workflow/skill expansion.
Legacy's STEP 4 created an implementation task via the new_task tool,
which doesn't exist on the SDK runtime; the ported prompt instead has
the agent present the plan and wait for explicit user confirmation.
Re-adds /deep-planning to the slash menu.

* refactor(vscode): simplify the /deep-planning expansion

Drop the custom regex/expander and shell-specific research-command
blocks: the builtin is now a plain AvailableRuntimeCommand appended to
the discovered workflow/skill commands, so the existing
expandSlashCommands machinery handles matching and replacement. The
prompt keeps the four-step protocol and implementation_plan.md
structure with a generic investigation paragraph instead of embedded
OS-specific commands.
2026-07-29 17:36:33 -07:00
John Choi dfd22bf79e refactor(ui): extract desktop approval card (#12693)
* refactor(ui): extract desktop approval card

* fix(ui): preserve approval card parity

* refactor(ui): keep approval labels fixed
2026-07-29 17:22:22 -07:00
John Choi 307707fd00 refactor(ui): extract desktop search combobox (#12663)
* refactor(ui): extract desktop search combobox

* fix(ui): preserve search selector visual parity

* fix(ui): preserve search combobox parity

* fix(ui): preserve combobox adoption parity

* test(desktop): reflect combobox accessible names

* fix(ui): disable open combobox options
2026-07-29 16:25:50 -07:00
Dominic Cooney 2a47c6ca08 fix(mcp): honor per-server timeout (seconds) across all clients (#12546)
* fix(mcp): honor per-server timeout (seconds) across all clients

The per-server timeout field in cline_mcp_settings.json was only read
by the VSCode extension's tools/call path. Everywhere else used
hardcoded constants: the SDK client timed out all requests at 5s and
initialize at 1.5s, and the extension's metadata requests (tools/list,
resources/*, prompts/*) timed out at 5s. Slow servers failed despite a
configured timeout (#7635, #12344).

Resolve the timeout once per client and apply it to every request:

- @cline/shared exports the default (60s) and bounds (1s-3600s) plus a
  resolver that clamps out-of-range values, so a milliseconds/seconds
  mix-up can no longer become hours.
- The SDK config loader parses timeout into
  McpServerRegistration.timeoutSeconds; StdioMcpClient and
  SdkUrlMcpClient apply it to initialize, tools/list, and tools/call.
  Unconfigured servers keep the fast 1.5s initialize probe so startup
  is no slower than before; a configured timeout raises that budget
  for slow-starting servers.
- The extension routes every request (including metadata) through one
  resolver and drops the hardcoded 5s DEFAULT_REQUEST_TIMEOUT_MS.
- createMcpTools derives the agent tool timeoutMs from the same value,
  keeping the wrapper and request timeouts in agreement.
- Timeout errors now name the bound and the field to increase; the
  VSCode server row and the CLI server list show the effective timeout
  and how to change it.

* fix(mcp): harden timeout lifecycle handling

* fix(mcp): address timeout review feedback

* fix(mcp): bound initialization and reconnect

* fix(mcp): keep timeout snapshots consistent

* fix(mcp): use standard stdio framing

* fix(mcp): bound legacy stdio fallback

* fix(mcp): honor timeout in framed fallback

* test(vscode): use SDK Vitest runner

* fix(mcp): fetch server capabilities in parallel

The four post-connect metadata requests (tools/list, resources/list,
resources/templates/list, prompts/list) ran sequentially, so a server
that hangs after initialize blocked connectToServer for four timeout
bounds. The MCP client correlates concurrent requests by JSON-RPC id
and the stdio transport writes each message atomically, so the fetches
now run in parallel and the worst case is one bound.

Also delete McpHub.readResource and McpHub.getPrompt and their response
types: nothing calls them since the SDK migration removed the
access_mcp_resource tool and prompt expansion.

* fix(mcp): keep failed servers and both framing errors visible

When both stdio framing attempts fail differently during initialize,
name each attempt's error instead of discarding the Content-Length
fallback's diagnostics. When they fail identically (both timed out),
rethrow the newline error unchanged so the timeout hint is the whole
message.

When connectToServer fails before the connection is registered (e.g.
the transport fails to start), register a disconnected entry carrying
the error so the server stays visible in the list instead of silently
disappearing, and notify the webview so the row leaves the connecting
state.

* fix(mcp): reject tool calls on connections without a client

A failed (re)connect registers a disconnected entry with a null client
so the server stays visible in the list. A tool wrapper captured by an
active session can still target that server; callTool now rejects it
with a controlled error naming the server and its last connection
error, instead of dereferencing the null client and throwing a
TypeError.
2026-07-29 16:25:23 -07:00
Edoardo Busano 704e953b69 fix(shared): keep valid OTEL headers when one entry is malformed (#12260)
parseKeyPairsIntoRecord wrapped the whole forEach in one try/catch, so a single entry that broke decodeURIComponent (e.g. a stray % in OTEL_EXPORTER_OTLP_HEADERS) aborted the loop and silently dropped every remaining header. Move the try/catch inside the loop to skip only the malformed entry. Adds regression tests.
2026-07-30 00:47:44 +02:00
John Choi c5661f8835 refactor(ui): extract desktop quick actions (#12664)
* refactor(ui): extract desktop quick actions

* fix(ui): preserve quick action visual parity

* style(ui): format quick actions import

* style(ui): format packaged component files
2026-07-29 14:52:05 -07:00
Ara 60c5a24eef Route reasoning controls from models.dev across providers (#12542)
* refactor(reasoning): route model controls from models.dev

Preserve typed reasoning capabilities from the model catalog, normalize requests once against advertised effort, budget, and toggle controls, and keep provider adapters focused on wire encoding. CLI presentation changes are intentionally deferred to a follow-up.

* fix(llms): encode catalog reasoning controls per provider

* fix(llms): clamp reasoning defaults and budgets

* refactor(shared): narrow reasoning exports

* refactor(llms): colocate reasoning controls

* fix(llms): handle mandatory Claude reasoning modes

* fix(llms): omit impossible Anthropic thinking

* fix(llms): reject impossible Anthropic thinking
2026-07-29 23:43:59 +02:00
John Choi 3f9ed573db refactor(ui): extract desktop aurora (#12665)
* refactor(ui): extract desktop aurora

* docs(ui): preserve aurora constraints

* docs(ui): document aurora container contract

* style(ui): format package file list
2026-07-29 14:30:56 -07:00
Bee ac432c87f0 fix(desktop): prevent long-running chat turns from timing out (#12671)
* fix(desktop): disable timeout for chat send commands

Add per-invocation timeout options to the desktop client and disable the deadline for long-running chat send requests. Extract the shared command response type and verify send commands use the timeout override.

* fix(desktop): clean up failed websocket sends
2026-07-29 23:30:28 +02:00
Etisha Garg 95b841a2dd docs: add screenshots for finding free models (#12690) 2026-07-29 12:50:43 -07:00
Saoud Rizwan 7d63376d98 Revert "Revert "docs: add Cline free models page (#12183)" (#12185)" (#12186)
This reverts commit ed3107f9ec.

Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
2026-07-29 07:41:43 -07:00
Sufiyan Khan 912c467818 fix(vscode): cancel Cline task on signout (#12657) 2026-07-29 00:41:33 -07:00
Saoud Rizwan c39c6d4479 feat(vscode): enable Cline Pass unconditionally, removing the ext-cline-pass feature flag (#12677) 2026-07-29 00:23:32 -07:00
Saoud Rizwan 071e5451b1 fix(webview): use theme-colored dropdowns for native select elements (#12676) 2026-07-28 23:55:28 -07:00
Saoud Rizwan 159961b3a3 Fix useDebouncedInput firing onChange on mount, which could wipe stored API keys (#12675)
useDebouncedInput scheduled its debounced onChange on mount and on every
external initialValue resync, not just user edits. Settings fields mount
with a placeholder value while their backing provider config is still
loading asynchronously, so the mount-fire echoed that placeholder back
to the backend ~100ms later.

For DebouncedTextField-backed secret fields (e.g. the OpenRouter API key,
which renders a masked value derived from the async readProviderConfig
response), losing that race meant writing apiKey: "" — silently deleting
the stored key from both providers.json and the legacy secrets store,
and leaving the field rendering empty despite a previously persisted key.
Non-secret fields similarly re-saved stale placeholder values on every
mount.

Gate the debounced save on an actual user edit: only values set through
the returned setter fire onChange; mount and external resyncs never do.
2026-07-28 22:39:13 -07:00
Saoud Rizwan 4a3f1ce310 fix(openai-compatible): carry custom model metadata across model-id changes (ENG-2341) (#12628)
* fix(openai-compatible): keep user model metadata when only the model id changes

Changing the OpenAI Compatible model id committed the new id without
overrides, so an id unknown to the catalog resolved to safe defaults
(inputPrice/outputPrice 0, supportsPromptCache false) and paid requests
billed as $0.0000. The legacy extension kept this user-authored metadata
in a single id-independent blob, so custom prices survived id edits.

Recommit the currently displayed overrides under the new id when the
model id changes, and let edits made while that commit is round-tripping
target the pending id instead of the stale read-back id.

Fixes ENG-2341

* fix(openai-compatible): scope pending selection state per mode

Review follow-up: the pending-override accumulator and pending-commit
counter were shared across Plan and Act. Changing the Act model id,
switching to Plan while that commit was round-tripping, then editing an
override committed the Plan edit under the pending Act model id (and the
shared pending count blocked Plan's reseed at the mode boundary).

Record the mode alongside the pending selection and only trust it for
edits in the same mode, keep per-mode pending counts so a mode switch
reseeds from that mode's committed state, and cover the deferred-commit
mode-switch scenario with a component test.

* fix(openai-compatible): give each mode its own pending-selection accumulator

Review follow-up: tagging the single shared accumulator with a mode still
lost state on a mode round trip. With an Act commit pending, visiting
Plan reseeded the shared slot to Plan; returning to Act could not reseed
(Act's read-back was still in flight), so the next Act edit merged onto
an empty set and silently dropped the pending prices/context/capabilities.

Keep one accumulator slot per mode so a round trip through the other
mode never disturbs a mode's pending state, and cover the scenario with
a deferred-commit round-trip test.
2026-07-28 22:30:19 -07:00
Saoud Rizwan 10a658a767 fix: report OpenRouter Anthropic models' full 1m context window consistently (#12629)
The OpenRouter model picker (refreshOpenRouterModels) still applied the
legacy 200k context-window restriction to Anthropic Claude models, while
the task header and auto-compaction resolve model info through the SDK
catalog, which reports the full 1m extended context window. The same
model showed Context: 200K in the picker and 1.0m in the task header.

Per the current product direction the 200k restriction (and its :1m
opt-in variants) is dropped entirely — everyone gets the 1m context
window. Remove the artificial clamps from refreshOpenRouterModels
(keeping the prompt-cache pricing overrides) and update the
openRouterDefaultModelInfo fallback to match, so the picker, the task
header, and compaction thresholds all agree on 1m.

Closes ENG-2345.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 22:04:55 -07:00
Dominic Cooney 9d63bfcb31 fix(vscode): restore foreground terminal default (#12672)
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-28 22:01:31 -07:00
Saoud Rizwan daa32ee138 fix(core): migrate legacy API keys for all secret-backed providers (ENG-2337) (#12626)
* fix(core): migrate legacy API keys for all secret-backed providers

collectCandidateProviderIds only nominated 11 provider ids while
buildLegacyProviderSettings can copy keys for 34, so stored keys for the
other 25 providers (deepseek, mistral, xai, groq, ...) were silently
dropped during migration unless the provider was the active plan/act
provider. Add the missing candidate checks so any stored key makes its
provider a migration candidate.

Also pick the legacy mode per candidate: a split plan/act config applied
the single globalState.mode to every provider, so the non-current mode's
configured model was replaced by the catalog default.

Migration re-runs on manager construction and never overwrites existing
entries, so users who already ran the buggy migration get dropped keys
backfilled from the still-present legacy secrets.json on next launch.

Fixes ENG-2337

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

* fix(core): normalize legacy provider-id aliases during migration

Address review: the mode selection and model fallback compared raw
legacy provider ids, so a declared alias (togetherai -> together,
sap-ai-core -> sapaicore) in globalState would miss its canonical
secret-derived candidate, read the wrong mode, and could write duplicate
alias/canonical entries. Route candidate collection, mode comparison,
and the generic model fallback through the existing normalizeProviderId
boundary. resolveMigratedProviderId now delegates to normalizeProviderId
(identical for the openai -> openai-compatible case it already handled).

Legacy ApiProvider never actually stored alias forms, so this is
hardening for hand-edited state rather than a live regression.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 21:59:39 -07:00
Saoud Rizwan a51fa646bb fix(vscode): reconcile the two provider state stores (ENG-2332) (#12640)
* fix(vscode): reconcile the two provider state stores (ENG-2332)

- createStorageContext now honors CLINE_DATA_DIR with the same priority as
  the SDK's resolveClineDataDir and the legacy reader's resolveDataDir
  (explicit option > CLINE_DATA_DIR > CLINE_DIR/data > ~/.cline/data), so
  globalState.json/secrets.json live in the same data dir as providers.json
  and legacy task state instead of silently splitting across directories.
- Add setLastUsedProvider and call it on active provider switches
  (SdkProviderChangeCoordinator) and when a session resolves its provider
  from StateManager (buildSessionConfig), so providers.json's
  lastUsedProvider no longer goes stale across provider switches.
- Trim env vars in legacy-state-reader's resolveDataDir to match the SDK's
  resolution exactly.

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

* refactor: trim ENG-2332 fix to the minimal change set

Revert the cosmetic legacy-state-reader trim, restore the original CLINE_DIR
line in createStorageContext, and tighten comments. No behavior change to
the two core fixes.

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

* refactor: drop lastUsedProvider sync, keep only the data-dir alignment fix

Scope ENG-2332 to the root-cause fix: createStorageContext honoring
CLINE_DATA_DIR like the SDK resolvers. The providers.json lastUsedProvider
staleness is deferred to a follow-up.

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

* fix: trim CLINE_DATA_DIR in resolveDataDir to match createStorageContext

Addresses Greptile P1: a whitespace-padded CLINE_DATA_DIR was trimmed by
createStorageContext but used verbatim by the legacy reader, which could
resolve the two stores to different directories again.

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

* chore: retrigger CI (windows e2e flake in chat.test.ts)

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

* refactor: share one data-dir resolver between storage context and legacy reader

Per review feedback: extract resolveDataDirFromEnv in storage-context.ts and
have legacy-state-reader's resolveDataDir delegate to it, so the two stores
structurally cannot drift apart again.

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

* fix: trim CLINE_DIR in the shared data-dir resolver to match the SDK

The SDK's resolveClineDir trims CLINE_DIR; a whitespace-padded value would
otherwise still resolve VS Code state and providers.json to different
directories.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 21:36:02 -07:00
John Choi 5213070f67 refactor(ui): extract desktop hero heading (#12609)
* refactor(ui): extract desktop hero heading

* test(ui): preserve hero heading constraints

* docs(ui): trim hero heading comments
2026-07-28 19:48:32 -07:00
Saoud Rizwan 7d7cddb9d9 chore(desktop): release v0.0.7 2026-07-28 18:33:51 -07:00
Saoud Rizwan 55e169f1f8 feat(vscode): working-directory badge in task header for out-of-workspace tasks (#12637)
* feat(vscode): show working-directory badge when a task runs outside the open workspace

Tasks resumed from the CLI or another workspace keep their original cwd,
so Cline reads, edits, and runs commands in a directory that is not the
one visible in the window - previously with no indication anywhere.

- Add TaskWorkingDirectoryBadge: a persistent warning chip in the task
  header (folder icon + cwd basename, full path + explanation in the
  tooltip) shown only when the task cwd is neither an open workspace
  root nor inside one. Hidden when roots or cwd are unknown to avoid
  false positives.
- Fix SdkController.getStateToPostToWebview to pass its workspace
  manager into the shared state builder; the SDK path previously always
  sent workspaceRoots: [] to the webview.
- Unit tests for the outside-workspace predicate (case, separators,
  multi-root, prefix collisions) and badge render states.

* fix(vscode): platform-aware path comparison in working-directory badge

Address PR #12637 review findings:
- Case folding is now platform-aware (win32/darwin insensitive, linux
  and unknown strict), so case-only path differences on Linux are no
  longer hidden; mirrors arePathsEqual in src/utils/path.ts.
- Backslashes are treated as separators only on win32; on POSIX a
  backslash is an ordinary filename character.
- Containment prefix no longer doubles the separator when a workspace
  root already ends with one, fixing false warnings for '/' and drive
  roots.
- Tests cover case-only pairs under win32/darwin/linux/unknown,
  POSIX-backslash filenames, '/' and 'C:\' workspace roots.

* fix(vscode): make darwin path comparison strict in working-directory badge

Follow-up to PR #12637 review: darwin volumes can be case-sensitive, and
the host's canonical arePathsEqual (src/utils/path.ts) already treats
only win32 as case-insensitive. Align the badge predicate with that
convention: case folding and backslash separators apply on win32 only;
darwin, linux, and unknown compare strictly. For a warning badge a rare
spurious warning beats silently hiding a real mismatch.
2026-07-28 18:23:41 -07:00
Saoud Rizwan c227e1ae36 fix(vscode): restore legacy workflow invocation and management UI (#12562)
* fix(vscode): restore legacy workflow invocation and management UI

- Expand /workflow slash commands typed with the legacy .md filename
  spelling (what the autocomplete menu inserts) and mid-message, and
  honor the user's workflow enable/disable toggles, instead of only
  expanding a leading extension-less /name via the SDK resolver.
- Restore the Workflows tab in the rules modal (view, toggle, create,
  edit, delete; enterprise section) that was dropped in the SDK-backed
  extension while all its gRPC handlers remained wired.

* chore: add changeset for workflow fixes

* fix(vscode): refresh workflow toggles on webview launch

The slash command menu is driven by workflowToggles state, but nothing
refreshed it at startup in the SDK-backed extension (only opening the
rules modal or creating a rule file did), so workflows never appeared in
the chat autocomplete until the user opened the modal. Legacy refreshed
toggles on task init.

* feat(vscode): move Workflows tab last and add deprecation warning

Workflows tab now appears after Rules/Hooks/Skills, and its view leads
with a warning banner: workflows are being deprecated in favor of
skills, with a docs link.

* chore: update changeset for workflow deprecation notice

* fix(vscode): address review findings on workflow expansion

- Honor remoteWorkflowToggles (and locked alwaysEnabled remote
  workflows) when building the disabled set, so disabled enterprise
  workflows no longer expand.
- Treat a workflow as disabled only when no scope has it enabled, so a
  disabled workspace file no longer shadows a same-named enabled global
  one (legacy expanded the enabled scope).
- Strip all workflow extensions the SDK discovers (.md/.markdown/.txt)
  when matching typed commands, not just .md.
- Re-read toggle state after the async directory scan in
  refreshWorkflowToggles so a toggle flipped mid-scan is not overwritten
  by the stale snapshot.

* fix(vscode): map workflow toggles to records so frontmatter names are governed

Compute the disabled set from the discovered workflow records
(listRecords) instead of toggle-path basenames alone: a file's toggle is
matched by its basename and disables the record's actual command name,
so a frontmatter 'name' that differs from the filename is still governed
by the Workflows toggle. Remote-config-materialized records are governed
by the name-keyed remote toggles (locked alwaysEnabled remain on).

* fix(vscode): harden workflow toggle-name mapping for expansion

- A command name shared by several records now counts as enabled when
  any record is enabled, so a disabled local workflow can no longer
  suppress an enabled or locked (alwaysEnabled) enterprise workflow.
- Remote toggles/locks are matched via a sanitizeSegment-compatible key,
  so config names that get rewritten during materialization (e.g. 'Org
  Standards' -> org-standards.md) still govern expansion.
- Typed filenames (e.g. /my-workflow.md from autocomplete) now resolve
  to workflows whose frontmatter renames the command, via the record's
  file basename.

* fix(vscode): govern each workflow command by its own record's toggle

Key the disabled set by exact command name and decide each record
independently instead of OR-aggregating by canonical name: distinct
commands whose names only differ by case or extension (e.g. a local
'Release' and a remote 'release') no longer influence each other, so an
enabled local workflow cannot keep a disabled enterprise workflow
expandable, and a disabled one cannot suppress a locked enterprise
workflow.

* fix(vscode): exact remote-name sanitization and keep mid-scan toggle additions

- Port @cline/shared's sanitizeSegment verbatim (incl. the 80-char cap)
  for remote workflow name comparison, so long enterprise workflow names
  cannot bypass a disabled toggle after filename truncation.
- The post-scan toggle merge now also keeps entries added while the scan
  was running (e.g. a workflow created via the modal), instead of
  pruning them with the deleted files.

* fix(vscode): handle mid-scan deletions and sanitized remote-name collisions

- The post-scan toggle merge now also drops entries that were removed
  from state while the scan ran, so a workflow deleted mid-refresh is
  not restored by the stale scan result.
- Remote toggle names that sanitize to the same materialized name merge
  as enabled-if-any-enabled instead of last-write-wins.

* fix(vscode): serialize workflow toggle refreshes

Queue refreshWorkflowToggles runs on a promise chain so overlapping
refreshes (webview launch, modal open, file create/delete) cannot
interleave scans and writes. Combined with the post-scan merge for
direct toggle flips, this closes the remaining stale-refresh races.

* fix(vscode): key remote workflow toggles off the materialized filename

The materializer names remote workflow files from the config name, so
derive the remote toggle key from the file basename instead of the
parsed command name; a frontmatter alias can no longer bypass a
disabled remote toggle.
2026-07-28 18:20:54 -07:00
Saoud Rizwan d0a0c802af fix(vscode): interrupted tasks disappear from History (ENG-2336) (#12613)
* fix(vscode): make interrupted tasks findable in History and restore Resume button

Interrupted/cancelled sessions were presented as gone (ENG-2336):

- History fuzzy search used location-based Fuse scoring (ignoreLocation:
  false, threshold 0.6), so any match more than ~60 characters into the
  task title scored above the threshold and the task silently vanished
  from search results even though it was in the list. Search now matches
  anywhere in the title.

- Opening a task from History never updated the authoritative TurnState,
  so the footer kept the previous context's phase (usually idle) and the
  Resume Task button never appeared for interrupted/failed sessions.
  showTaskWithId now derives the phase from the reopened conversation:
  resumable for interrupted tasks, completed for completed ones.

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

* fix(vscode): decide Resume vs Start New Task from persisted session status

SDK conversations do not record a completion tool call in the transcript
(a completed turn and one interrupted mid-stream both end with plain
assistant text), and history rendering appends a synthetic trailing
ask:"completion_result" either way, so the message tail always looked
"completed". Reopening a task from History now reads the persisted
session status: "completed" gets the Start New Task affordance, while
cancelled/failed (interrupted) sessions get Resume Task. When reopening
the currently-active task, the stop is awaited first so the status read
reflects how the last turn actually ended.

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

* fix(vscode): fence concurrent history opens and default unknown status to Resume

Address review feedback:

- showTaskWithId now takes a generation fence: a request that loses the
  race to a newer showTaskWithId or clearTask abandons installation after
  its awaited reads, so a slow older request can never clobber the user's
  latest selection (task proxy, messages, or turn phase). clearTask bumps
  the generation too so New Task wins over an in-flight history open.

- The resume affordance no longer falls back to the message tail when the
  persisted session status is unavailable: the tail always ends with the
  synthetic ask:"completion_result" that history rendering appends, which
  misclassified interrupted tasks as completed on a failed status read.
  Only an explicit "completed" status gets Start New Task; anything else
  (including unknown) gets Resume Task, the safe direction.

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

* fix(vscode): allocate history-open generation before the lookup and fence before session stop

Address review feedback: the latest-selection-wins fence started too late.
SdkController.showTaskWithId awaited findHistoryItem() before entering the
coordinator, so a stalled preflight for an older selection could re-enter
with a NEWER generation than a later selection and replace it — and since
the first fence check sat after endActiveSession, a superseded request
could also stop a session the newer selection had just installed.

The history lookup now lives inside the coordinator (skipHistoryLookup is
gone), the generation is allocated synchronously before all asynchronous
work, and a fence check runs before endActiveSession so a superseded open
never stops the newer selection's session. The coordinator returns the
HistoryItem so SdkController keeps its TaskResponse contract. Regression
test covers the exact reported sequence: stalled lookup for task A, task B
selected and loaded, A resolves last — B stays installed and A stops
nothing.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 18:16:19 -07:00
Saoud Rizwan f9227fead4 Show completion feedback box for inferred turn-final responses in SDK path (#12638)
* Show completion feedback box for inferred turn-final responses in SDK path

The SDK agent usually ends a turn with a plain text response instead of an
attempt_completion / plan_mode_respond tool call, so the legacy green 'Task
Completed' box (act) and 'Plan Created' box (plan) never rendered in the new
extension — making a finished turn look stuck or frozen.

Now, when a turn ends cleanly (done reason 'completed', no completion tool
used) and its last content is a text response, that text row is retagged in
place to say:'completion_result' (act, green box) or the new
say:'plan_completion_result' (plan, yellow-accented 'Plan Created' box).

- Track the turn-final text candidate in MessageTranslatorState; cleared on
  tool activity, errors, aborts, and new user turns
- Replay the same inference during history rehydration, recovering each
  turn's plan/act mode from the persisted <user_input mode="..."> wrapper
- Add plan_completion_result ClineSay type (+ proto enum) rendered via
  PlanCompletionOutputRow, restyled with the plan-yellow accent to match
  the plan/act toggle and the CLI's plan color
- Turn phase semantics unchanged: footer buttons still come from TurnState

* Remove attempt_completion tool and strip completion box headers

- Drop the attempt_completion extra tool (and its shell-command executor)
  from VS Code SDK sessions; the SDK's built-in submit_and_exit is already
  disabled for act/plan presets, so the agent now always ends its turn with
  a plain text response and the turn-end inference styles it.
- Translator keeps recognizing attempt_completion/submit_and_exit for
  replaying persisted transcripts from older sessions.
- Remove the 'Task Completed' header, check icon, and copy button from the
  green completion box, and the 'Plan Created' header, notepad icon, and
  copy button from the yellow plan box. The final text of a turn may be a
  question rather than an actual completion or plan, so the boxes are now
  quiet color cues that make no claim.

* Skip completion retag for terminal text of failed/cancelled sessions

The trailing text of a session whose last run failed or was cancelled is a
dangling partial response, not a completion. Gate the history converter's
final synthesized turn end on the session record's status so reopening a
broken task keeps its terminal text as a plain row instead of an inferred
completion box. Mid-transcript turns are unaffected: the user continued
after them and history carries no per-turn outcome.

* Require clean at-rest session status before retagging terminal text

Tighten the negative failed/cancelled check into an allowlist: the history
converter now only retags the transcript's terminal text when the session
record is 'completed' (formally stopped clean run) or 'idle' (the normal
at-rest state between interactive turns). 'running'/'pending' at rest means
the process died mid-turn, so its dangling partial response stays plain.

* Restrict history completion retag to the transcript's final turn

Persisted SDK transcripts carry no per-turn outcome, so a mid-conversation
turn the user cancelled mid-response (then followed up on) is
indistinguishable from one that ended cleanly. Retagging those presented
interrupted responses as deliberate turn ends. History rehydration now only
retags the final turn's terminal text, gated on the session record's
at-rest status; earlier turns always render as plain text. Live sessions
are unaffected — their per-turn boxes come from real done events.

* Trust only status 'completed' for the history completion retag

'idle' is written by markTurnIdle for every interactive finish reason,
including aborted turns, so an at-rest idle record cannot prove the last
turn ended cleanly. Terminal statuses are reliably written when sessions
are released (task switch, clear, dispose), so requiring 'completed' keeps
the box on normal reopened tasks while never styling an interrupted
response as a deliberate turn end.

* Treat missing session records as unknown outcome in history retag

A transcript with no session record has no recorded outcome, so its
terminal text stays a plain row instead of getting completion styling.
2026-07-28 17:38:00 -07:00
Saoud Rizwan bd83980359 fix(vscode): also check file-backed stores before re-onboarding upgraders (ENG-2346) (#12639)
migrateWelcomeViewCompleted derived the flag solely from VS Code's
per-profile stores, which are empty for users upgrading from the live
4.x extension (file-backed config under ~/.cline/data). The flag landed
as false and fully configured users were pushed back through onboarding.

Purely additive: the existing VS Code checks are untouched; the same
signals (completed flag, provider secrets, keyless provider configs) are
now also read from the file-backed globalState.json/secrets.json and
OR-ed into the result.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 17:16:28 -07:00
Saoud Rizwan 4d3b161b9a fix(core): coerce line-number tool args (#12641)
Models sometimes emit numeric tool arguments as JSON strings. `insert_line`
and the `read_files` line bounds were plain `z.number()`, so an
`insert_line: "3"` rejected the whole tool call before it ran:

  1 tool call(s) failed: [editor] {"error":"✖ Invalid input: expected number,
  received string\n  → at insert_line"}

The model is handed that error and burns a round trip re-deriving the argument.

`z.coerce` leaves the JSON Schema advertised to the model untouched (still
`integer`), and `.int()` / `.positive()` still reject "abc", "3.5" and 3.5.
2026-07-28 16:49:01 -07:00
Saoud Rizwan 255be9ad29 fix: stop Ollama model picker polling /api/tags once per second forever (ENG-2344) (#12621)
* fix(webview): stop unbounded polling of local model endpoints (ENG-2344)

The Ollama provider form polled /api/tags every 2s from two places at once
(OllamaProvider and a dead duplicate poll in ApiOptions whose result was
never read), producing ~1 req/s for as long as the settings pane was open.
Since the base URL is user-configurable, this could hammer a remote or
metered endpoint. VSCodeLmProvider and LMStudioProvider had the same
interval pattern.

- Remove all useInterval model polling; fetch on mount and when the
  base URL changes instead
- Refresh the Ollama model list when the picker field gains focus so a
  server started after the pane opened is still discovered
- Delete the dead _ollamaModels poll in ApiOptions

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

* fix(webview): add on-demand model refresh for LM Studio and VS Code LM

Greptile review follow-up: removing the polling intervals left these two
pickers pinned to their mount-time snapshot. Mirror the Ollama picker's
interaction-driven refresh:

- LM Studio: refetch models when the model dropdown or the manual model
  id field gains focus
- VS Code LM: refetch when the dropdown gains focus, and add an explicit
  'Refresh the model list' link to the empty state

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 16:44:46 -07:00
Saoud Rizwan 77b181a472 fix(vscode): label editor insert_line edits as edits, not new-file creations (#12635)
An `editor` tool call with `insert_line` (e.g. a prepend) targets an
existing file — the SDK editor executor requires the file to already exist
for inserts — but sdkToolToClineSayTool only treated `old_text`/`replace_in_file`
as edits, so inserts were classified as newFileCreated and the approval card
read "Cline wants to create a new file:" for an existing file.

Treat insert_line as an edit so the card reads "Cline wants to edit this file:".
2026-07-28 16:30:38 -07:00
Saoud Rizwan 45476650b7 fix(vscode): stop Preferred Language from silently resetting on settings mount (#12632)
The webview-ui-toolkit VSCodeDropdown fires a spurious change event with
the wrong option (index 2, Portuguese - Brasil) while its slotted options
initialize after a window reload, and the handler persisted that value
unconditionally. Any saved language not at the top of the list could be
silently rewritten to Portuguese just by opening the General settings tab.

Replace the toolkit dropdown with the ui/select component already used by
the other settings dropdowns (Auto Compact Strategy, MCP Display Mode),
which only emits onValueChange for real user selections, and render the
options from the shared languageOptions list instead of a hardcoded copy.
2026-07-28 16:30:28 -07:00
Saoud Rizwan f847b06cfd Fix thinking indicator flashing when a turn completes (#12631)
At turn end the final message is finalized (partial: false) via the fast
partial-message stream a moment before the done event flips turnState out
of "streaming" via a full state post. During that gap the in-list
"Thinking..." loader row appeared and immediately disappeared, flashing
on every turn completion.

- Extract the loader show/hide logic from MessagesArea into a testable
  useThinkingLoaderRow hook.
- Debounce the loader when its trigger is the tail message finishing
  streaming: mid-turn a real wait outlives the grace period, while the
  turn-end phase change cancels it before it ever shows.
- Add the legacy path's say("completion_result") anti-flicker guard to
  the turnState path so attempt_completion turns never flash regardless
  of timing.
2026-07-28 16:30:18 -07:00
Saoud Rizwan 598b3af7eb fix(cli): report correct default directories for --config and --data-dir in --help (#12627)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 16:30:00 -07:00
Saoud Rizwan fc936b7305 fix(vscode): restore Retry/Start New Task buttons after API failure (ENG-2339) (#12625)
* fix(vscode): restore Retry/Start New Task buttons after API failure

A provider stream error emits ask:'api_req_failed', but the session-event
coordinator resolved the turn-end phase to 'awaiting_followup', clobbering
the error state — so the footer never showed the error-recovery buttons and
the error surface offered no way to recover (ENG-2339).

Record the error outcome in MessageTranslatorState when the error event is
translated, and resolve turn end to the 'error' phase so the existing
api_req_failed button config (Retry / Start New Task) is reachable again,
matching legacy behavior.

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

* fix(vscode): also record error outcome for done(reason:'error') terminations

A turn can terminate with done(reason:'error') without a separate 'error'
event; record the error outcome there too so turn end still resolves to the
'error' phase and the Retry / Start New Task buttons appear.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 16:29:48 -07:00
Saoud Rizwan 9247dc30bb chore(vscode): remove dead task history command (#12624) 2026-07-28 16:29:35 -07:00
Saoud Rizwan 1b499161cc Fix ignored China/international endpoint toggles for Qwen, Moonshot, Z AI (ENG-2340) (#12623)
* Fix ignored China/international API line toggles for Qwen, Moonshot, Z AI (ENG-2340)

The regional apiLine setting was persisted through both storage layers but
never consulted when resolving the request endpoint, silently sending
regional users to the wrong host.

- @cline/llms: record china/international base URLs on the builtin specs
  for qwen, qwen-code, moonshot, zai, zai-coding-plan, and minimax; expose
  resolveProviderApiLineBaseUrl; resolve options.apiLine against the
  registered apiLineBaseUrls in GatewayRegistry.createProvider (explicit
  base URLs still win).
- @cline/core: toProviderConfig now resolves the base URL from apiLine
  between the explicit setting and the static provider default.
- VS Code: buildSessionConfig resolves the API line from legacy state
  (qwenApiLine/moonshotApiLine/zaiApiLine/minimaxApiLine) with a
  providers.json fallback and forwards it on the provider config so the
  gateway can route regionally.

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

* Share the base provider's legacy API line with qwen-code and zai-coding-plan

The coding variants have regional endpoints in the SDK but no legacy
state field of their own, so a China-line user selecting them from the
VS Code UI would silently fall back to the international default. The
variant's own providers.json apiLine still wins over the shared legacy
field.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 16:29:28 -07:00
Saoud Rizwan 0e5cb433b0 fix(core): expose live in-memory session messages for mode-switch rebuilds (#12622)
Session rebuilds seed the replacement session from readMessages, but the
persisted transcript only catches up at assistant-message/turn boundaries
and abort() does not flush. Toggling plan/act mode while a task's first
turn is mid-flight (e.g. a command approval pending) therefore rebuilt the
session with no history at all and the new mode's model lost the task.

Add RuntimeHost.readLiveSessionMessages (optional) which prefers the
resident session's agent.getMessages() and falls back to the persisted
transcript, expose it as ClineCore.readLiveMessages, and use it in the
VS Code history loader that feeds session rebuilds. readSessionMessages
keeps its persisted-transcript semantics for existing callers (compaction
validation, session snapshots, history).

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 16:29:18 -07:00
Saoud Rizwan 53a46c309b Revert "Add a built-in cline-settings skill and broaden the legacy resume war…" (#12669)
This reverts commit 36c78267c6.
2026-07-28 16:15:25 -07:00
Dominic Cooney 36c78267c6 Add a built-in cline-settings skill and broaden the legacy resume warning (#12660)
* Add a built-in cline-settings skill and broaden the legacy resume
warning

Models diagnosing configuration problems have no authoritative source
for where Cline stores settings: the SDK migration removed the old MCP
documentation tool, and resumed legacy conversations can carry stale
paths and instructions from older runtimes (CLINE-2570).

Add a core-owned virtual skill, cline-settings, whose instructions are
generated at invocation from the shared storage path resolvers. It is
listed and invoked through the existing skills registry on both the
local and Hub session paths, is reserved against shadowing by
file-backed skills (case-insensitive), honors session skill allowlists
(an explicit empty allowlist disables all skills including built-ins),
and never appears in editable listRecords.

Broaden LEGACY_RESUME_MODEL_WARNING to cover stale configuration
paths, file formats, and product instructions, not just tool names.
Anchor the persisted history boundary on a stable marker; recognize
and upgrade the historical warning in place so previously resumed
tasks get the new wording without duplicate warnings, and preserve
resumed user text that shares a message with the warning.

* Fix Windows MCP stdio spawn for paths with spaces; add settings-skill
rule

The runtime-builder MCP test failed on Windows because the stdio
client spawns with shell: true there, and cmd.exe split the unquoted
executable path at the space in "C:\Program Files\nodejs\node.exe".
Quote the command and arguments for cmd.exe so any server whose
command or arguments contain spaces can start. Also raise the connect
timeout to match the request timeout: connect covers process spawn
plus the first initialize round-trip, and 1.5s is tight for cold
starts on loaded machines.

Add a brief .clinerule noting that settings/storage-path changes may
require updating the cline-settings built-in skill.

* Quote empty MCP arguments for cmd.exe

An empty-string argument passed through unquoted disappears when
cmd.exe re-parses the concatenated command line, silently shifting the
server's argument list. Quote empty values so they survive as "".

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-28 16:10:27 -07:00
Tomás Barreiro 76c30b1c60 Update ClineFreeModelLimitError wording (#12666) 2026-07-29 01:03:44 +02:00
Saoud Rizwan b496c5a1f3 chore(cli): release v3.0.47 2026-07-28 15:36:26 -07:00
Bee 2fc06ce446 feat(desktop): add pagination to session history view (#12618)
* fix(desktop): order sessions by last activity and unify status dot colors

* feat(desktop): add pagination to session history view

Display sessions in ten-item pages and fetch older history only after reaching the final local page. Add coverage for pagination, session opening, and compact token formatting.

* page numbers

* Support adding a session to favorite list

* apply feedback

* fix
2026-07-28 15:30:43 -07:00
Bee 64c50380f3 feat(chat): refine tool and reasoning message disclosures (#12616)
* feat(chat): refine tool and reasoning message disclosures

Add tool-specific icons with a fallback, display elapsed reasoning time, and restyle reasoning disclosures. Position hidden message actions outside the layout and update attachment sizing to valid Tailwind utilities.

* fix(desktop): improve chat message actions and scrolling

Refine action positioning, sizing, timestamps, and visibility for chat messages. Remove nested overflow constraints so scrolling remains controlled by the conversation viewport, and tighten tool disclosure spacing.

* tools icon mapping

* fix(desktop): align chat timestamps and tool icons
2026-07-28 15:30:33 -07:00
Saoud Rizwan e36197d911 chore(sdk): release v0.0.66 2026-07-28 15:22:13 -07:00
Bee 52d187578e feat(desktop): show subagent/teammates execution history (#12615)
* feat(desktop): expose session agent execution history

Add a list_session_agents sidecar command to retrieve agent and team run details from child sessions and tool messages. Include comprehensive tests for agent discovery, message parsing, status handling, and result normalization.

* apply feedback

* add test

* feedback fix

* fix

* fix p1
2026-07-28 15:09:27 -07:00
Tomás Barreiro a6239c420c Update generated files (#12649) 2026-07-28 13:56:05 -07:00
Bee c7c5e6518b fix(desktop): order sessions by last activity and unify status dot colors (#12617) 2026-07-28 12:06:59 -07:00
Bee 98c0717302 feat(desktop): system tray session status (#12659)
* feat(desktop): add system tray session status support

Enable Tauri tray icon and PNG image features for desktop tray integration. Expose the running session count in process context so the tray can reflect active work, with test coverage for running and idle sessions.

* fix(desktop): buffer tray actions and show app status
2026-07-28 20:59:36 +02:00
Dominic Cooney b4aed24ff8 fix(vscode): compact tasks opened from history (#12002)
* fix(vscode): compact tasks opened from history

The compact button only worked while a session was actively running.
Opening a task from history and clicking compact errored with "There is
no active task to compact."

Compaction is defined over a session transcript, so rather than grow a
second implementation for displayed tasks, resume a displayed history
task on an isolated session host and compact it through the existing
path. The coordinator owns and disposes that host, so task navigation
cannot make cleanup stop a replacement active session.

Follow-up resume and both compaction paths (idle active session and
displayed task) acquire the same session-rebuild boundary around
transcript read, session start, and persistence. Task and session
object identity are rechecked across awaits; cleanup targets only the
exact host and session started by the operation. A follow-up abandoned
by task navigation settles the streaming turn phase it pre-set, so the
newly displayed task never shows a stuck Thinking/Cancel footer.

The resume-start preparation shared by follow-up and compaction is
extracted into prepareTaskResumeStartInput, including legacy task
conversion, so the two callers cannot drift apart.

The compaction divider UX and context-meter shrink remain owned by the
already-merged webview compaction change.

* fix(vscode): deliver follow-ups across a same-task proxy reload

Follow-up targeting checks compared the displayed TaskProxy by object
identity, but showTaskWithId allocates a fresh proxy for the same task
id, so reloading the task mid-resume silently dropped the message.
Compare targeting by taskId; cleanup keeps object identity.
2026-07-28 10:52:13 -07:00
Saoud Rizwan e9ec82d2ef docs: remove stray double space in README CLI example (#12594)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 09:45:51 -07:00
Saoud Rizwan e3ff875e09 fix(cli): persist /settings general toggles (mode, auto-approve, compaction) across restarts (#12614)
* feat(core): persist plan/act mode, tool auto-approve, and compaction mode in global settings

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

* fix(cli): restore /settings general toggles across restarts

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

* fix(core): make global settings updates cross-process safe

Targeted setters previously did unlocked read-modify-write cycles over the
shared global-settings.json, so concurrent hosts (two CLIs, or CLI + VS Code)
could silently discard each other's changes. Route all setters through a new
updateGlobalSettings(mutate) helper that re-reads the latest on-disk state
under a short-lived lock file (with stale-lock reclaim and a bounded wait)
and replaces the file atomically via temp-file rename so readers never see
torn writes.

* Revert "fix(core): make global settings updates cross-process safe"

This reverts commit 198c1c831b.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-28 08:56:55 -07:00
Tomás Barreiro dc175c73a8 Cline free ux (#12593)
* Introduce the concept of free models that have the cline-free ID

* Add (free) to explicitly free models

* Render (free) in free models name

* Add pricing to the free model info

* Fix free model pricing

* fix tests

* SEt cline-free model pricing to 0

* revert pricing changes

* Add free limit error handling

* Include the reset time in the message

* Add button to switch model in VSCode

* add model not found error

* remove problematic tests

* fix review messages

* Fix model promotion ended

* Revert "revert pricing changes"

This reverts commit 7e5b2a34fd.
2026-07-28 06:20:28 +02:00
Tomás Barreiro d91f1ce166 Add support for cline-free models. (#12591)
* Introduce the concept of free models that have the cline-free ID

* Add (free) to explicitly free models

* Render (free) in free models name

* Add pricing to the free model info

* Fix free model pricing

* fix tests

* SEt cline-free model pricing to 0
2026-07-28 05:50:15 +02:00
Saoud Rizwan bc5a2e85a4 chore(desktop): release v0.0.6 2026-07-27 19:01:44 -07:00
Saoud Rizwan 9c56a726a7 Add persistent sidebar update indicator for staged desktop app updates (#12611)
* Add persistent sidebar update indicator for staged app updates

* Surface restart failures from the update indicator and reset its pending state
2026-07-27 18:37:51 -07:00
Saoud Rizwan 72c9bbfcaa Clarify desktop app auto-update setting: it governs the CLI, not the app (#12601)
* Reword desktop auto-update setting description to drop CLI mention

* Clarify desktop settings copy: auto-update toggle governs the CLI, not the app
2026-07-27 18:14:39 -07:00
Bee 53a7c3e80e fix(desktop): align startup appearance and session context (#12608)
* fix(desktop): align startup appearance and session context

* update cline logo size

* display full workspace name

* fix: fits in narrow screen size

* header in narrow screen

* Transient failures no longer replace a valid branch with no-git.

* header alignments

* account settings button row

* fix(desktop): improve collapsed sidebar settings layout

Use a compact overlay-friendly width and left-align navigation controls in collapsed settings. Adjust header padding, stack account details, anchor the expand button, and add layout regression tests.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-27 18:13:55 -07:00
John Choi 8751daaba9 refactor(ui): extract desktop session status (#12596) 2026-07-27 17:16:04 -07:00
Bee aa1fffbe80 fix(ui): use solid primary color for checked switch (#12600)
Update the switch's checked state background from translucent to solid primary for clearer visual feedback and improved contrast.
2026-07-27 16:55:43 -07:00
Saoud Rizwan 283c3ba937 ci: grant pull-requests write so promo-comment deletion works on PRs (#12606) 2026-07-27 16:51:00 -07:00
Saoud Rizwan 69c9a9ac28 ci: delete coding-agent promo comments on PRs (#12604) 2026-07-27 16:44:48 -07:00
Bee 4d238558d0 feat(deaktop): queue ui update (#12534)
* feat(deaktop): queue ui update

* fix(desktop): address queue review feedback

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-27 16:06:16 -07:00
Bee 51e5daf8eb feat(core): persist and restore connector sessions (#12125)
* feat(core): persist and restore connector sessions

Persist successful connector runs for autostart and disable persistence when connectors are stopped. Reconnect saved connector channels during CLI daemon or hub startup so previously connected adapters are restored after restarts without blocking startup.

* fix(core): harden connector autostart recovery

* fix(core): address connector autostart edge cases

* fix(core): address connector reconnect review feedback

* fix(core): address remaining connector review feedback

* fix(core): address connector persistence review feedback

* fix(core): address connector lifecycle review feedback

* fix(core): restart surviving connectors after hub restart

* fix(core): refresh Telegram identity after token rotation

* fix(hub): restart active connectors on start

* fix(connectors): address lifecycle review regressions

* fix(connectors): make reconnects instance-safe

* fix(connectors): harden restart failure handling
2026-07-27 15:59:23 -07:00
John Choi 28ef954258 feat(ui): add host-safe theme and Markdown exports (#12439)
* feat(ui): add host-safe theme and package contract

* fix(ui): preserve scoped theme contract

* refactor(ui): establish generated theme contract

* chore(ui): refresh committed build

* fix(ui): isolate markdown presentation

* chore(ui): prepare next package preview

* fix(ui): harden preview package contract

* fix(ui): enforce clean package builds in CI

* refactor(ui): rely on npm package builds

* test(ui): make package smoke failures actionable

* ci(sdk): restrict pull requests to main

* test(ui): verify the published package contract

* docs(ui): document the React types floor

* chore(ui): keep package checks out of shared workflows

* fix(ui): preserve standalone markdown cascade

* fix(ui): reject stale generated theme builds

* refactor(ui): narrow foundation to adoption needs

* test(ui): verify packed CSS exports exist

* fix(ui): restore publish contract safeguards

* refactor(ui): keep foundation adoption-focused

* test(ui): verify new packed CSS exports
2026-07-27 15:26:27 -07:00
Saoud Rizwan 9c4841ea07 chore(desktop): release v0.0.5 2026-07-27 15:21:49 -07:00
Saoud Rizwan 1bd200e906 ci: strip cloud-agent promo wrappers from PR bodies (#12588)
* ci: strip cloud-agent promo wrappers from PR bodies

* ci: make PR body strip vendor-agnostic, pin github-script to SHA
2026-07-27 15:10:05 -07:00
Saoud Rizwan fabbc144d6 perf(desktop): make the app feel snappy end-to-end (#12568)
* perf(desktop): make the app feel snappy end-to-end

Fixes several compounding sources of UI jank that made every click and
keystroke feel seconds-slow:

- Aurora background: drop per-frame 46-64px CSS blur re-rasterization;
  bake softness into gradients + a static mask and animate only
  opacity/transform (compositor-only). Onboarding/home idle went from
  ~10fps to a locked 60fps under 4x CPU throttling.
- Hide the app shell while the opaque onboarding overlay is up so a
  second aurora + hero animations are not composited underneath.
- Hero verb animation: opacity/transform only (no text blur filter).
- Composer: keystroke state now lives inside ChatInputBar (versioned
  promptDraft injections for quick actions/undo/resets), and mention/
  slash detection is derived instead of effect-synced; typing went from
  245/246 keystrokes over 50ms to 3/240.
- Chat streaming: coalesce per-token text/reasoning deltas into ~48ms
  flushes; memoize MessageBubble/ToolMessageBlock with stable callbacks
  so finished messages skip re-rendering during streams.
- Session history: only surface isLoadingHistory before the first load;
  background refreshes no longer re-render the whole app twice each.
- Provider catalog (~700KB): dedupe concurrent fetches with a short TTL
  so app boot issues one round-trip instead of three.
- Sidecar: session-log appends are now ordered async writes instead of
  writeFileSync per streamed token; git/folder-picker/editor discovery
  use async execFile so the native picker no longer freezes every
  pending command; editor discovery results cached for 60s.

* fix(desktop): address Bugbot review findings

- Invalidate the shared provider-catalog cache after any provider
  mutation (onboarding connect paths, account sign-in/out, settings
  save, add provider) so post-save reloads never see a pre-save copy.
- Clear the injected composer draft on send so a composer remount
  cannot repopulate the previous prompt.
- Mark the hidden app shell inert + aria-hidden while the onboarding
  overlay covers it, keeping covered controls out of the tab order.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-27 15:04:49 -07:00
Amy Duquette 372f029343 docs: add Poolside provider setup guide (#12586) 2026-07-27 21:38:35 +02:00
Tomás Barreiro 9d9b7aa6f8 Add data dirs when running specific environments (#12585) 2026-07-27 21:19:07 +02:00
Dominic Cooney 8c7095b805 test(cli): avoid hard-coded dialog background color (#12579)
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-07-27 11:07:54 -07:00
Dominic Cooney 05535e844c chore(vscode): remove unused OpenTelemetry dependencies (#12573)
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-07-27 10:29:13 -07:00
Tran Binh Minh c3dc4a95bc fix(cli): use unique keys for read_files rows (#12144)
read_files rendered file rows keyed by raw path, so the same path listed twice (e.g. find-skills reading a SKILL.md repeatedly) produced duplicate React keys and the two-children-with-the-same-key warning. Build index-namespaced keys instead, at both render sites.

Fixes #9784

Signed-off-by: Minhkunn <minh.12072k6@gmail.com>
Co-authored-by: Minhkunn <minh.12072k6@gmail.com>
2026-07-26 13:41:30 -07:00
Sufiyan Khan 9466fcc018 docs: fix typos, incorrect slash command, and hooks vs plugins mismatch (#12154)
* docs: fix typos and incorrect slash command reference

- Fix double period in MiniMax provider description
- Remove duplicate 'through' in kanban install description
- Fix /new -> /newtask (correct slash command name)

* docs(hooks): fix description to reference SDK Plugins, not SDK Hooks

The description said 'SDK Hooks page' but the content links to the
SDK Plugins page (/sdk/plugins). Align the description with the
actual destination.
2026-07-26 13:13:39 -07:00
Saoud Rizwan dd7a1c5fa6 fix(desktop): onboarding Cline API key path, stuck "Agent is working..." composer, OAuth sign-in cancel (#12564)
* fix(desktop): clear busy status when queued turns finish; add Cline API key onboarding path

* feat(desktop): allow cancelling a pending Cline browser sign-in during onboarding

* fix(desktop): address review findings on OAuth cancel, API key verification, and queued-turn status

* fix(desktop): cancel pending OAuth logins when the initiating transport connection closes

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-26 02:10:15 -07:00
Saoud Rizwan fe75eb6271 fix(vscode/core): agentic compaction silently fell back to basic, and manual /compact never reached the model (#12563)
* fix(vscode): resolve base URL and knownModels for compaction summarizer

The agentic compaction summarizer creates its LLM handler from the
session's ProviderConfig alone. For the OpenAI Compatible provider
stored under its SDK spelling (openai-compatible), resolveBaseUrl had
no mapping, so ProviderConfig was built without a baseUrl and the
summarizer silently hit the provider default endpoint (api.openai.com),
failed auth, and fell back to basic compaction - the UI still showed
'Context compacted' with no hint that agentic summarization never ran.

- resolveBaseUrl: accept the SDK spelling of the OpenAI Compatible
  provider, and fall back to the providers.json base URL (mirroring
  resolveApiKey) when legacy state has none.
- buildSessionConfig: expose knownModels at the top level of
  CoreSessionConfig, so manual compaction (sdk-compaction.ts) budgets
  against the real model context window instead of the 64k fallback.

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

* fix(core): project compaction sidecar even when auto-compaction is disabled

Manual /compact persists a compaction sidecar and promises the next turn
will use the compacted working context, but the runtime host only wired
the compaction-state-aware prepareTurn when compaction was enabled. With
Auto Compact off (the VS Code extension default), a manual /compact was
a silent no-op for the model: the sidecar was saved and the UI showed
'Context compacted', yet every subsequent request still sent the full
canonical transcript.

createCompactionStateAwarePrepareTurn already supports an undefined
compact fn (project existing state, never re-compact), so wire it
unconditionally; sessions without a sidecar are unaffected. Also keep a
resumed/initial sidecar instead of dropping it when compaction is
disabled.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-26 01:20:08 -07:00
Saoud Rizwan e4091c3686 Stop the task at the mistake limit like the CLI and remove the max-mistakes setting (#12561)
* fix(vscode): stop the task at the mistake limit like the CLI, drop the max-mistakes setting

When the SDK's consecutive-mistake limit is hit, the extension used to
block on an ask (Proceed Anyways / Start New Task) while the agent loop
kept running against the provider — reproduced 2,100+ consecutive API
requests behind the unanswered prompt.

Replicate the CLI's non-interactive resolver instead: show an error row
and resolve the decision as an immediate stop. The run aborts cleanly at
the turn boundary, the turn phase becomes awaiting_followup, and the
user continues whenever they want by sending a new message (which also
resets the SDK's mistake tracking on the next productive turn).

Also remove the extension's maxConsecutiveMistakes setting (state key,
settings RPC, webview state, proto fields now reserved). It was never
wired into the SDK session config — the SDK's own default governs — so
the setting was dead weight. Legacy mistake_limit_reached asks from
persisted conversations still render via the existing webview paths.

* fix(proto): reserve retired Settings field 139 (max_consecutive_mistakes)

The original removal added 'reserved 139' but the proto generator at the
branch base had no reserved-statement support and silently dropped it on
regeneration. Main (b4c640733) taught generate-state-proto.mjs to
preserve reserved statements, so after the merge the reservation now
survives. Also reserve the field name, mirroring the custom_prompt
removal pattern.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-26 00:53:41 -07:00
Saoud Rizwan bcf8c1d03e fix(vscode): relative read_files paths and diff-preview stalls no longer fail read/edit tools (#12558)
* fix(core): resolve relative read_files paths against the session cwd

The built-in FileReadExecutor resolved relative paths against process.cwd(),
which in a VS Code extension host is typically '/' rather than the workspace.
Every relative-path read failed with ENOENT, so models fell back to reading
files through the terminal. Resolve relative paths against the tool's
configured cwd in createReadFilesTool before invoking the executor.

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

* fix(vscode): never let a stalled diff preview open fail or delay an edit

Thrown errors from opening the edit diff preview were already swallowed, but
a hung vscode.diff call was unbounded: on auto-approve it burned the editor
tool's 30s execution timeout (failing the whole edit), and on manual approval
it delayed the approval ask indefinitely. Bound the preview open with a 5s
timeout; on timeout the edit proceeds without a preview and the late-opening
tab is closed once the open settles.

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

* style(core): order node:path import first for biome

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

* refactor(vscode): flatten the preview-open timeout into a plain race

Replace the custom timeout error class, the race helper with timer
bookkeeping, and the two-branch cleanup with a single Promise.race and one
settle-then-close line. Same behavior: a rejected or stalled preview open
never blocks the approval ask or fails the edit, and any late-appearing tab
is closed once the open settles.

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

* refactor(vscode): move the read_files cwd fix out of the SDK into the host

Revert the SDK change and instead override the read_files executor in the
extension, alongside the existing editor/apply_patch/askQuestion overrides.
The override resolves relative paths against the workspace root before
delegating to the SDK's built-in reader, since the extension host's
process.cwd() is usually '/' and every relative-path read failed with ENOENT,
pushing the model into terminal fallbacks. The SDK is left untouched.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-26 00:22:29 -07:00
Saoud Rizwan 5099eed5ee fix(desktop): consistent MCP server cards and single uninstall affordance (#12555)
* fix(desktop): consistent installed cards and single uninstall in marketplace views

* fix(desktop): surface marketplace setup guidance on matched installed cards

* fix(desktop): show setup guidance for all matched marketplace entries, not just first match

* fix(desktop): unambiguous entry-to-item matching and no stale installed card flash

* fix(desktop): drop orphaned installed keys optimistically instead of hiding cards during recheck

* fix(desktop): guard recheck races and avoid duplicate uninstall for ambiguous matches

* fix(desktop): keep uninstall action on ambiguous fallback marketplace cards

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-26 00:12:28 -07:00
Saoud Rizwan b4c640733b Remove dead "Use compact prompt" toggle from LM Studio provider settings (#12551)
* Remove dead 'Use compact prompt' toggle from LM Studio settings

The compact system prompt option was never wired up in the SDK-based
extension: the customPrompt value was stored in state and echoed back
to the webview, but nothing in the session factory or SDK ever read it
to alter the system prompt. Remove the checkbox (only shown for the
LM Studio provider) and all the dead state/proto plumbing behind it.

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

* Add changeset for compact prompt toggle removal

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

* Reserve removed custom_prompt field number/name in Settings proto

Teach generate-state-proto.mjs to preserve reserved statements in the
generated Secrets/Settings messages so removed fields keep their wire
identity reserved across regenerations, and reserve field 150 and the
custom_prompt name (plus the name in UpdateSettingsRequest).

Addresses Greptile review feedback on #12551.

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

* Never assign reserved proto field numbers to new Settings fields

If the highest-numbered field was removed and reserved, the generator
would hand that same number to the next new field, emitting both a
reserved statement and a live field at the same number. Parse reserved
numbers (including ranges) from the existing message, skip them when
assigning new numbers, and fail fast if an active field collides with
a reservation.

Addresses Bugbot review feedback on #12551.

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

* Format generate-state-proto.mjs

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-25 22:57:10 -07:00
Saoud Rizwan 40ba9d25eb fix(webview): use VS Code theme selection colors in dropdown menus (#12554)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-25 22:54:45 -07:00
Saoud Rizwan adb8ee0c4d fix(vscode): model ID under chat field stale / resets to gpt-4o for OpenAI Compatible (#12552)
* fix(vscode): fold SDK openai-compatible provider id to legacy openai spelling

The settings provider dropdown sources ids from the SDK catalog, so picking
OpenAI Compatible stored 'openai-compatible' into plan/actModeApiProvider.
Every provider-keyed code path (webview model label, planModeOpenAiModelId
slots, session factory) expects the legacy 'openai' spelling, so the model
id under the chat field went stale after Done and fell back to the catalog
default (gpt-4o).

- parseProviderId + toLegacyApiProvider now alias openai-compatible -> openai
- state-keys load transform migrates already-stored SDK spellings
- convertProtoToApiProvider normalizes provider ids written from the webview
- commitModelSelection writes the legacy spelling and posts state to the
  webview so model-only commits refresh the chat model label immediately
- session factory normalizes provider ids from state and providers.json

* fix(vscode): make toLegacyApiProvider alias lookup case-insensitive

parseProviderId lowercases before its alias lookup, but toLegacyApiProvider
(used directly by convertProtoToApiProvider and the state-keys load
transform) matched aliases case-sensitively, so a mixed-case
'OpenAI-Compatible' would not fold. Fall back to a lowercased lookup while
preserving original casing for unknown ids.

* fix(vscode): treat spelling-only provider differences as the same provider

Addresses the Bugbot finding on PR #12552: stale snapshots can still hold
the SDK spelling (openai-compatible) while new writes use the legacy
spelling (openai). Normalize both sides of the provider comparisons in
SdkProviderChangeCoordinator.providerForMode and
SdkController.isSelectionForActiveModeProvider so a spelling-only
difference neither restarts the active session nor skips the lightweight
in-session model update.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-25 22:53:58 -07:00
Saoud Rizwan 01617f9a05 chore: remove orphaned legacy auto-retry UI and dead onRetryAttempt plumbing (#12557)
The pre-SDK extension auto-retried failed API requests and surfaced
'Auto-retrying in X seconds' rows (say:'error_retry') plus a retryStatus
header on api_req_started. The SDK-based extension never emits either:
errors map straight to an api_req_failed ask with a manual Retry button,
and retrying is handled silently by the AI SDK / auth-refresh retry.

Remove the orphaned webview rendering (ChatRow error_retry case,
ErrorBlockTitle, combineErrorRetryMessages, isRequestInProgress chain,
stories), the unused say types and proto enum values (reserved), the
retryStatus field, and the never-invoked onRetryAttempt callback from
ApiHandlerOptions, sdk-api-handler, and @cline/llms provider config.

Legacy transcripts may still contain error_retry / api_req_retried rows;
readUiMessages now drops them so old tasks don't render raw JSON.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-25 22:52:11 -07:00
Saoud Rizwan b72d7f1a6f fix(vscode): keep webview alive when moved between primary and secondary sidebars (#12553)
* fix(vscode): keep webview alive when moved between sidebars

Moving the Cline view between the primary and secondary sidebars made the
view go blank with 'this.unsubscribeHostTelemetrySettings is not a function'.

Two fixes:
- The vscode host bridge streaming client returned the async IIFE's Promise
  instead of the cancel function its contract declares, so callers invoking
  the stored unsubscribe function threw a TypeError. It now returns a
  synchronous wrapper that resolves the real cancel function in the background.
- VscodeWebviewProvider disposed the whole Controller on WebviewView
  onDidDispose. VS Code destroys and re-resolves the view when it is moved
  between sidebars, so the re-resolved view was served by a dead controller
  (postStateToWebview no-ops after dispose) and rendered blank. onDidDispose
  now only releases view-scoped resources; the controller is disposed on
  extension deactivation via WebviewProvider.disposeAllInstances.

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

* fix(vscode): address review — don't clear active task on re-resolve, guard stale view dispose

- resolveWebviewView no longer calls clearTask on re-resolves (moving the
  view between sidebars must not terminate a running task); it only clears
  stale task state on the first resolve after activation.
- onDidDispose now only tears down view resources if the disposed view is
  still the active one, so a stale dispose event arriving after a newer view
  resolved cannot clobber the active view's listeners. resolveWebviewView
  also releases the previous view's resources up front.

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

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-25 22:47:40 -07:00
Saoud Rizwan d5b966d0a8 fix: center-align sign-in verification code box in chat (#12533)
The 'Enter this code in your browser' box shown after clicking
'Sign in to Cline' was left-aligned while the surrounding logged-out
message and button are centered. Center the label and the code/copy row.

Fixes #12531

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-07-25 18:48:45 -07:00
Sufiyan Khan 1bb0833287 fix(cli): isolate hub daemon abort handling (#12500) 2026-07-24 18:44:51 -07:00
Saoud Rizwan 2c64c4ce5b fix: OpenAI Compatible model list fails when base URL has a trailing slash (#12532)
* fix: normalize trailing slash in OpenAI Compatible base URL for model list fetch

* fix: construct OpenAiModelsRequest via proto create in refreshOpenAiModels test

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-24 15:28:51 -07:00
Bee 0bdf2e3468 fix(desktop): restore window dragging (#12529) 2026-07-24 22:07:34 +02:00
Dominic Cooney 98024c4243 fix(cli): patch @opentui-ui/dialog for opentui 0.4.x remove() contract (#12516)
@opentui-ui/dialog@0.1.2 is built against @opentui/core ^0.1.69, whose
Renderable.remove(id) took a string id. Core 0.4.x renamed it to
remove(child) and throws when handed anything but a renderable, so the
dialog package's removeDialog()/provider teardown aborted before
detaching the panel: the React portal content unmounted but the
imperative grey box stayed on screen over the chat after every dialog
close (model picker, help, command palette, ...).

The upstream package is abandoned at 0.1.2, so pin the fix with a bun
patch that passes the renderable object on all three bindings (react,
solid, core container). A tui-test opens and dismisses the help dialog
and asserts the panel's #262626 background is fully gone, not just its
text.

Fixes #12506

Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-07-24 15:18:15 +09:00
Saoud Rizwan 359e771ab3 Set up Cloud dev environment (CLI, VS Code extension, desktop app) (#12515)
* docs: add Cursor Cloud dev environment setup notes (AGENTS.md)

* docs: document VS Code extension + desktop app dev setup (AGENTS.md)

* docs: trim AGENTS.md cloud agent instructions

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-23 19:26:10 -07:00
Saoud Rizwan a21b21de84 chore(desktop): release v0.0.4 2026-07-23 18:50:17 -07:00
Bee ca6f6a6c23 feat(desktop): use the shared Cline Hub runtime (#12508)
* feat(desktop): use the shared Cline Hub runtime

* fix(hub): group code-sidecar-observer clients under Code App

The desktop observer client type was renamed from code-sidecar-approvals
to code-sidecar-observer, but the Code App grouping matchers in the hub
dashboard and menubar sidecar still only matched the old type. Since the
observer now registers on the shared Hub, it showed up as a separate
ungrouped client. Keep the old type matched for older desktop builds.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-23 18:21:39 -07:00
Bee 3e06abc366 feat(desktop): support chat without workspaces (#12412)
* feat(core): support pathless sessions with temporary workspaces

* fix(desktop): mark editor icons as decorative

* fix(core): omit absent auth request IDs

* test(sdk): restore request_id auth telemetry param in core-events test

The branch's drive-by request_id -> requestId rename was dropped while
resolving the merge conflict with #12444 (which added requestIdDetails on
main), so the public captureAuthLoggedOut/captureAuthRefreshSoftFailure
API keeps its original parameter name.

* refactor(sdk): root pathless session workspaces under the cline data dir

Move the workspace created for pathless session starts from
<os.tmpdir()>/cline/sessions/<id>-temp/project to
<cline-data-dir>/workspaces/<id>/project (default
~/.cline/data/workspaces/<id>/project), per PR review:

- OS tmp reapers (macOS ~3-day purge, systemd-tmpfiles, reboot cleanup)
  silently delete user work created in 'New Project' sessions
- /tmp is a shared namespace on Linux: the first user to create /tmp/cline
  owns it (EACCES for everyone else), and guessable session IDs let a local
  attacker pre-create the workspace directory
- under the data dir the workspace shares the session store's lifecycle and
  the existing CLINE_DATA_DIR / CLINE_DIR overrides for tests and sandboxes

isTemporaryWorkspacePath now matches the .cline/data/workspaces/<id>/project
segment shape, and the -temp suffix is gone since the id-scoped directory no
longer needs to mark itself as reapable.

* feat(sdk): open pathless sessions in one shared chat workspace

Instead of minting a workspace directory per session
(<data>/workspaces/<session-id>/project), all sessions started without a
cwd/workspaceRoot now share <cline-data-dir>/workspaces/chat (default
~/.cline/data/workspaces/chat). Starting a pathless session seeds the
directory with an AGENTS.md rules file (only when missing, so users can
edit it) that tells the agent to treat the session as a chat: don't create
or edit files unprompted, ask where a project should live when the user
wants one built, and default to a new named folder inside the chat
directory that later sessions can reference.

This avoids unbounded per-session directory sprawl, gives chat sessions a
stable home the user can revisit, and groups them naturally in the desktop
sidebar. The desktop app now labels the shared workspace "Chat" (menu
action "Just chat") instead of "New Project", and isChatWorkspacePath
matches only the chat directory itself, so project folders created inside
it behave as regular workspaces.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-23 18:09:03 -07:00
Saoud Rizwan 6518b05b6c feat(desktop): first-run onboarding flow (#12495)
* feat(desktop): add replay new-user-experience setting

Adds an onboarding state module (localStorage-backed, with a reset event
the app shell can subscribe to) and a 'New user experience' row in
Settings -> General with a Replay button so beta testers and designers
can re-run the first-run flow. The flow itself ships in the stacked
follow-up PR.

* feat(desktop): first-run onboarding flow

Full-screen first-run experience shown until completed once: a welcome
step (3D glass logo over the aurora background), a connect step offering
Cline sign-in (recommended) or bring-your-own API key against the
provider catalog, and a done step that drops the user into a fresh
thread. Completion is tracked by the onboarding state module from the
previous PR; the Settings replay row now re-enters the flow in place via
the reset event, so its toast is gone. Skipping is always available so
nobody gets trapped; the connected provider (and its default model when
known) is remembered so the chat composer opens pointed at it.

* fix(desktop): address greptile review on onboarding flow

- Filter the bring-your-own-key picker to providers a lone API key can
  fully configure: providers with structured config fields (Vertex gcp.*,
  Bedrock aws.*) or no API-key field at all (Claude Code) no longer appear,
  since connecting them here would report success without working.
- Record Cline as the active provider when a signed-in user hits Continue,
  so replaying onboarding doesn't leave the chat pointed at a previously
  selected provider.

* feat(desktop): accent color themes and switchable app icon (#12496)

* feat(desktop): accent color themes and switchable app icon

Settings -> General grows an appearance cluster next to Dark mode:

- Accent color: six palettes from the Figma exploration (violet default,
  graphite, cyan, pink, espresso, ember). Non-default accents re-anchor
  --primary/--primary-foreground/--primary-emphasis/--ring per light and
  dark mode via html[data-cline-accent] overrides in globals.css, tuned in
  OKLCH to mirror the brand token relationships; chart and sidebar tokens
  alias var(--primary) so they follow. Persisted in localStorage and
  applied at boot alongside the dark-mode sync.

- App icon: the four Figma variants (Classic, Sunrise, Steel, Midnight).
  The webview persists the choice, swaps the favicon in browser mode, and
  in the Tauri shell calls the new set_app_icon native command, which
  loads the matching bundled resource (icons/dock/*.png) and applies it
  via NSApplication.applicationIconImage on the main thread. macOS resets
  the dock icon every launch, so the shell re-applies the stored choice at
  boot; classic is also loaded from a resource because the objc2 binding
  warns against passing nil to restore the bundled icon. Other platforms
  no-op (Ok(false)).

* fix(desktop): don't let a stale app-icon failure roll back a newer selection
2026-07-23 16:59:50 -07:00
Bee f9f492319e feat(desktop): redesign channel setup as expandable cards (#12490)
* feat(desktop): redesign channel setup as expandable cards

Replace the add-channel dialog with inline expandable configuration forms, including per-channel validation and error handling.

Add comprehensive tests for channel configuration, conditional fields, security options, and connection workflows.

* Connect

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-23 16:54:29 -07:00
Saoud Rizwan c3ab557194 polish(desktop): cleaner chat markdown + external links that actually open (#12497)
* polish(desktop): cleaner chat markdown + external links that actually open

Links in chat markdown never opened in the packaged app: the confirm
dialog's window.open(_blank) is silently dropped by the Tauri shell.
Route opens through openExternalUrl (open_external_url sidecar command)
and only keep the confirmation dialog for deceptive links whose visible
text reads as a URL on a different host than the real destination —
ordinary external links now open directly in the default browser.

Visual pass on Streamdown output for the chat pane: collapse the
double-boxed code block card and drop the language header row, reveal
the copy button on hover only, turn off line numbers, single-box tables,
chat-scale the heading ramp (h1 was text-3xl next to 14px body), outside
list markers, and tighter block rhythm.

* fix(desktop): harden deceptive-link detection per review

Recurse into element children when extracting link label text so inline
formatting (e.g. a bolded hostname) can't dodge the deception check, and
compare port and (when the label states one) scheme in addition to
hostname so same-host links to an unexpected scheme or port still get
the confirmation dialog. An unparseable destination behind URL-shaped
label text is now treated as deceptive rather than waved through.

* fix(desktop): treat trailing-dot FQDN labels like their plain hostname

Browsers resolve 'github.com.' identically to 'github.com', but the
URL-shaped-label pattern rejected the trailing dot, so a deceptive label
like [github.com.](https://evil.example) skipped the deception check and
opened directly. Accept one trailing dot in the pattern and strip
trailing dots during hostname normalization on both sides, so the FQDN
form is deceptive exactly when the plain form is.

* fix(desktop): treat protocol-relative labels like their https form

A label spelled '//github.com' reads as a URL but failed the URL-shaped
pattern (which only tolerated an https?:// prefix), so it skipped the
deception check and opened an unrelated destination directly. Accept a
protocol-relative prefix in the pattern, and parse '//'-prefixed values
as https-relative in parseLinkParts — prepending 'https://' to them
produced an empty hostname and made the comparison a no-op.
2026-07-23 16:48:50 -07:00
Bee 9fa37a9128 feat(desktop): display image attachments in chat (#12502)
* feat(desktop): drag and drop files to attach them to the chat

The Tauri webview swallows OS file drags by default (dragDropEnabled),
so HTML5 drop events never fire. Disable it on the main window per the
Tauri v2 docs, then handle standard dragenter/dragover/dragleave/drop on
the chat pane: dropped files feed the same dedupe-and-append pipeline as
the paperclip picker, with a depth-counted 'Drop to attach' overlay while
files are dragged over. Image drops become data-URL images via the
existing serializeAttachments path.

* feat(desktop): display image attachments in chat

* 225x225

* fix(desktop): preserve queued attachments

* fix(desktop): distinguish queued image turns

* fix pending

* fixed

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-23 16:46:36 -07:00
Bee 9cc7796bbe feat(desktop): add custom overlay title bar navigation (#12504)
* feat(desktop): add custom overlay title bar navigation

Configure Tauri to use a hidden overlay title bar and host back/forward navigation in the draggable sidebar header. Preserve agent title width during editing to prevent layout shifts, with tests covering both behaviors.

* fix(desktop): reconcile deleted navigation entries

* fix(desktop): dedupe session deletion events

* fix(desktop): serialize session deletion state

* fix(desktop): use exported DesktopAppView type in page.tsx

AppView is a non-exported type local to agent-sidebar.tsx, so referencing
it in page.tsx was a TS2304 error hidden by the typecheck script's webview
exclusion and next's ignoreBuildErrors.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-23 16:46:07 -07:00
Saoud Rizwan 439baee17c feat(desktop): add replay new-user-experience setting (#12494)
Adds an onboarding state module (localStorage-backed, with a reset event
the app shell can subscribe to) and a 'New user experience' row in
Settings -> General with a Replay button so beta testers and designers
can re-run the first-run flow. The flow itself ships in the stacked
follow-up PR.
2026-07-23 16:43:33 -07:00
Bee 469debdb30 fix(schedules): default headless routines to yolo (#12489)
* fix(schedules): default headless routines to yolo

Centralize the Cline default model ID in @cline/shared while preserving the @cline/llms export. Keep explicit modes stable and disable ask_question for unattended scheduled runs.

* autoapprove

* fix unit test

* fix(schedules): harden headless routine execution

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-23 14:53:03 -07:00
John Choi 79589c8417 test(cli): relax cold history dispatch timeout (#12511) 2026-07-23 14:36:33 -07:00
Saoud Rizwan 8e5471de9c feat(vscode): show compaction progress and results in the webview (#12487)
* feat(vscode): show compaction progress and results in the webview

Port the CLI's compaction UX to the VS Code extension:

- Translate the SDK's compaction status notices into a say:'compaction'
  divider row with a spinner while running, updated in place (same ts) to
  'Context compacted · x → y tokens · n → m messages' when done, matching
  the CLI's divider. Dangling dividers finalize as failed/cancelled when
  the turn errors or ends mid-compaction.
- Manual /compact (button or slash command) drives the same divider from
  the compaction coordinator instead of plain info lines, capturing token
  counters from the SDK's status notices.
- Drop raw status-notice slugs ('auto-compacting') that previously
  rendered as info rows.
- Context window bar now reads the compacted size (tokensAfter) from a
  compaction row newer than the last API request, so it drops immediately
  after compaction instead of waiting for the next turn.
- Fix the Auto Compact Strategy selector showing 'basic' when unset; the
  effective default is agentic (core defaults strategy ?? 'agentic').

* fix(vscode): apply compaction shrink as a ratio to the context meter

Address review feedback from #12487:

- getLastApiReqTotalTokens: instead of substituting the compaction
  notice's tokensAfter (an SDK estimate on a different scale than
  provider-reported usage, which made the bar re-snap when the next
  request's real usage landed), scale the last provider-reported request
  total by the compaction's tokensAfter/tokensBefore ratio. Both
  counters come from the same estimator, so the ratio is scale-free.
  Multiple compactions since the last request compound. A completed
  divider without token counters leaves the total unscaled.
- Suppress only the known-internal status notices explicitly
  (compaction-budget-adjusted); an unrecognized status notice now falls
  through to an info row so future notices surface instead of silently
  vanishing.
- Cross-reference the two compaction-divider finalization paths (auto:
  translator finalizeDanglingCompaction; manual: coordinator catch) so
  terminal-state rule changes touch both.
- Post state to the webview before re-throwing in the coordinator's
  failure path, consistent with the other terminal branches.
2026-07-23 10:07:57 -07:00
Saoud Rizwan cd553d2343 feat(desktop): drag and drop files to attach them to the chat (#12498)
The Tauri webview swallows OS file drags by default (dragDropEnabled),
so HTML5 drop events never fire. Disable it on the main window per the
Tauri v2 docs, then handle standard dragenter/dragover/dragleave/drop on
the chat pane: dropped files feed the same dedupe-and-append pipeline as
the paperclip picker, with a depth-counted 'Drop to attach' overlay while
files are dragged over. Image drops become data-URL images via the
existing serializeAttachments path.
2026-07-23 10:06:16 -07:00
Mikołaj Kondratek 7b776225b9 fix(telemetry): report host identity on SDK-pipeline events (#12503)
* fix(telemetry): report host identity on SDK-pipeline events

On JetBrains standalone cline-core, SDK-pipeline events (task lifecycle,
token usage, tool usage, provider failures) reported the hardcoded
cline_type "VSCode Extension", platform "VS Code", and
platform_version "unknown", unlike the classic TelemetryService which
resolves these from HostProvider.env.getHostVersion().

Extend the host_plugin_version resolution in VscodeTelemetryPolicyService
to apply the full host identity (cline_type, platform, platform_version)
with the same mapping the classic pipeline uses, before the telemetry
gate opens. Fields the host does not report keep the construction-time
fallbacks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): fall back to unknown host identity, not VSCode labels

A failed getHostVersion lookup previously left the hardcoded VSCode
identity in place, hiding the failure as a plausible-looking row.
"unknown" makes the failure visible and matches the classic
TelemetryService's || "unknown" semantics.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): defer provider_created until host identity is applied

telemetry.provider_created was captured synchronously inside the core
factory, before VscodeTelemetryPolicyService resolves getHostVersion —
so that one event always carried the construction-time fallback identity
(pre-existing: it reported the hardcoded VSCode identity on JetBrains
and never had host_plugin_version).

Add an opt-in deferProviderCreatedEvent to the core telemetry factories
that skips the construction-time capture and exposes it as
ConfiguredTelemetryHandle.emitProviderCreated; the policy service emits
it right after applying the resolved host metadata. Other handle
consumers (CLI, hub daemon, examples) keep immediate emission.

Also close the subscription race on the same guarantee: a host setting
flip arriving while getHostVersion is still resolving now waits for the
metadata to be applied before opening the gate, and a slow initial
settings fetch no longer overwrites a newer subscription update.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): emit deferred provider_created on early dispose

If the policy service is disposed while the host-version lookup is
still pending, the deferred provider_created would never be captured —
the undeferred event was always emitted (with construction-time
identity) and exported by the shutdown flush. Emit-once semantics:
dispose fires the event with the fallback identity before shutting the
handle down, and the late metadata continuation cannot double-emit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 22:25:53 +09:00
Bee ae033761ab feat: auto generate built-in provider list (#12204)
* feat: auto generate built-in provider list

- Generate `providers.generated.ts` and `provider-ids.generated.ts` from `models.dev/api.json` alongside the model catalog
- Merge generated provider specs with handwritten built-in overrides for Cline, Codex, local/OAuth providers, routing metadata, and product defaults
- Include additional `models.dev` providers only when they are OpenAI-compatible for now
- Keep lightweight provider ID utilities from importing the full generated provider spec catalog

* Removed redundant handwritten definitions for providers that are fully described by generated metadata

* update unit test
2026-07-23 14:27:59 +02:00
Mikołaj Kondratek c961ae7730 feat(telemetry): emit host_plugin_version metadata on all events (#12478)
* feat(telemetry): emit host_plugin_version metadata on all events

The host already reports its Cline distribution version over the
hostbridge (getHostVersion.clineVersion — the JetBrains plugin version
on JetBrains, the extension version on VSCode), but telemetry never
attached it: extension_version is always the cline-core bundle version,
so JetBrains events could not be tied to a plugin release.

Attach it as a new optional host_plugin_version metadata field, omitted
when the host does not report one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(telemetry): loop over host version cases instead of interleaving stubs

Review feedback: the two host_plugin_version cases were interleaved via
onFirstCall/onSecondCall stubs across two service instances. Run one
mock-assert-reset cycle per case so the only differences between them —
the host version response and the expected reported value — are visible
in the case table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(telemetry): guarantee stub cleanup in host_plugin_version test

Review feedback: the loop installed process-global stubs and only
restored them on the happy path — a rejected create() or failed
assertion would leak exhausted stubs into subsequent tests and leave
the service undisposed. Use a sinon sandbox restored in finally, and
dispose the service there too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(telemetry): carry host_plugin_version on SDK-pipeline events too

Review feedback: main's production controller emits task lifecycle,
token usage, tool usage, and provider-failure events through a separate
SDK telemetry service whose metadata is built independently, so those
events still omitted the plugin version.

Add the optional host_plugin_version field to the shared SDK
TelemetryMetadata contract and resolve it from the authoritative
getHostVersion response during the policy service's init. The metadata
update is sequenced before the host telemetry setting is applied, and
events stay gated until that setting lands, so no event can be emitted
without the field in place. A failed host-version lookup degrades to
the previous behavior (field absent, telemetry still enabled).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 18:42:37 +09:00
Dominic Cooney e940b6a335 fix(vscode): preserve edit preview focus (#12491)
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-07-23 16:23:18 +09:00
David Knaack 5a0780a6f9 fix(llms): set metering header and use fetch adapter for sap ai core (#12337) 2026-07-23 00:05:27 -07:00
Bee 847276f4a5 feat(desktop): add one-time routines and run navigation (#12477)
* feat(schedules): add one-time routines and run navigation

* fix schedule review concerns

* fix repository lint errors

* fix optional auth request ID telemetry

* fix one-time schedule lifecycle

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-22 16:56:49 -07:00
Dominic Cooney 8b4d1973b5 fix(vscode): reclaim unobserved fallback terminals (#12352)
Classify unobservable terminal outcomes so cleanup and reporting share one source of truth. Reclaim managed sendText fallbacks at the disclosed next-acquisition boundary while preserving markerless, continued, detached, and uncertain-error terminals. Make cleanup, CWD reservations, process listeners, and detached logs failure-safe.
2026-07-22 16:22:11 -07:00
Dominic Cooney a5211a3a5c fix(vscode): make 'Proceed While Running' sticky to the command batch (#12359)
Register every foreground command before terminal acquisition so a parallel batch observes one Proceed While Running decision. If a command is still acquiring a terminal, settle its tool result immediately and transfer the approved command to an owned detached lifecycle that logs acquisition, output, completion, and failure. Abort before startup unregisters the handle and prevents the command from starting later.
2026-07-23 06:39:38 +09:00
Saoud Rizwan fb1333fdc2 fix(desktop): route external link opens through sidecar so they work in Tauri (#12481)
* fix(desktop): route external link opens through sidecar so they work in Tauri

The markdown 'Open external link?' dialog confirmed via window.open, which
the Tauri webview silently drops (no window opener configured), so clicking
'Open link' did nothing (ENG-2302). Route confirmation through
openExternalUrl, which invokes the open_external_url sidecar command inside
the Tauri shell and falls back to window.open in plain web mode.

Also fixes the marketplace 'Get value' env-var link, which relied on the
same dead target=_blank behavior.

* fix(desktop): open mailto/tel links and middle-clicked marketplace links

Greptile review fixes:
- open_external_url now allows mailto: and tel: alongside http(s) — the
  platform openers already dispatch any scheme to the OS protocol handler,
  the gate is just the allowlist. Streamdown's harden step blocks every
  other scheme before it reaches SafeMarkdownLink (test added to guard
  that assumption, since the sidecar allowlist relies on it).
- Protocol-relative URLs pass streamdown but fail the sidecar's new URL()
  parse; pin them to https before handing off.
- The marketplace 'Get value' link now intercepts middle clicks (auxclick)
  too, which bypassed the onClick handler and fell into the dead
  target=_blank path.
2026-07-22 14:27:01 -07:00
Renee Huang 59113c309c docs: update ClinePass wording to '2-5x the usage on popular open coding models compared to standard API rate' (#12479)
* docs: update ClinePass wording from '2-5x API rate limits' to '2-5x the usage on popular open coding models compared to standard API rate'

* docs: update ClinePass wording in cline-provider.mdx for consistency

* nit
2026-07-22 11:44:55 -07:00
TheRealSpencer 48d0c38f52 fix(security): bump axios to 1.18.0 to resolve multiple CVEs in axios (#12473)
* fix(security): bump axios to 1.18.0 to resolve multiple CVEs in axios

* fix(security): bump axios to 1.18.0 in docs project
2026-07-22 12:38:45 -05:00
aikido-autofix[bot] c7ab9ff839 fix(security): update js-yaml from 4.1.1 to 4.3.0 (#12456)
Co-authored-by: aikido-autofix[bot] <119856028+aikido-autofix[bot]@users.noreply.github.com>
2026-07-22 09:49:42 -05:00
Tomás Barreiro 045518d19f Add Request-ID to auth events (#12444)
* Add the X-Request-ID to the ClineOAuthTokenError

* Add request id to events

* address comments
2026-07-22 13:20:30 +09:00
Saoud Rizwan 099c6179e4 fix(desktop): resolve login shell PATH so agent can find gh and other CLI tools (#12429)
* fix(desktop): resolve login shell PATH so agent can find gh and other CLI tools

When the Tauri app is launched from Finder/the Dock on macOS it inherits
launchd's minimal PATH (/usr/bin:/bin:/usr/sbin:/sbin), so the sidecar and
every process it spawns for the agent (bash tool, MCP servers) can't find
tools installed via shell profiles, e.g. Homebrew's gh in /opt/homebrew/bin.
The same task works from the CLI because a terminal runs with the full
login-shell PATH.

At sidecar startup, ask the user's login+interactive shell for its PATH
(sentinel markers isolate it from profile noise, 5s timeout, kill on hang)
and merge it into process.env.PATH: shell entries first, current-only
entries preserved. No-op on Windows; CLINE_SIDECAR_SKIP_SHELL_PATH=1 is the
escape hatch. Failures never block startup.

Fixes CLINE-2740

* fix(desktop): address greptile review on shell PATH resolution

- Don't let shell resolution eat the Tauri endpoint-readiness window: kick
  it off first so it overlaps sidecar startup (awaited before the session
  manager exists, which is what spawns children), drop the shell timeout
  5s -> 2s, and give the fallback attempt half the budget so the combined
  worst case (3s) stays inside the 5s readiness poll.
- Handle non-POSIX login shells: run the marker printf inside /bin/sh so
  $PATH expansion never depends on the outer shell's rules (fish would
  space-join it), pass -i/-l/-c as separate flags, give csh/tcsh only -c
  (their -l is valid only as the sole flag), and retry with the platform
  default shell when $SHELL can't produce a PATH.
- Don't log the resolved PATH: the applied result now carries an entry
  count instead of the merged PATH string.

* fix(desktop): read login shell from the account database, document PATH resolution

$SHELL is set by a parent shell, so a GUI-launched process may not have it.
Use os.userInfo().shell (getpwuid — DirectoryServices on macOS, same source
as dscl UserShell; NSS/etc/passwd on Linux) as the authoritative source,
with $SHELL and the platform default as fallbacks. Also documents the whole
mechanism in the app README.

* fix(desktop): widen endpoint readiness poll, source csh login profile

- The 5s get_desktop_backend_endpoint poll was already tight for
  session-manager init on slow machines; shell PATH resolution (bounded 3s
  worst case) made it tighter. Poll 15s instead — it returns as soon as the
  ready line arrives, so only genuine failure waits longer.
- csh/tcsh can't take -l alongside -c, so mark them as login shells via the
  argv[0] dash convention (argv0: "-tcsh") to get ~/.login sourced on top
  of the always-read rc file.

* fix(desktop): never spawn a second sidecar while one is alive

ensure_desktop_backend_started treated a live child with a pending
endpoint as absent and fell through to spawn a duplicate, orphaning the
first process. Hold the process lock across the whole check-and-spawn
(concurrent callers serialize), return early for any live child, fail
the endpoint poll fast when the child exits instead of respawning, and
stop a stale stdout-reader from wiping a successor's endpoint. The spawn
is injectable so regression tests cover repeated and concurrent startup
checks (exactly one spawn while pending) and dead-child replacement.

* style(desktop): tighten mergePaths and csh comment per review

* docs(desktop): codify backend state lock ordering
2026-07-21 18:11:55 -07:00
Bee 26037b17ac feat(core): default to agentic compaction (#12317)
* feat(core): default to agentic compaction

Use agentic compaction when no valid strategy is configured while preserving explicit basic selection. Add a session compaction CLI and package script for testing and comparing compaction strategies.

* createHandlerMock

* fix(core): let the agentic compaction cut land on assistant boundaries

Agentic auto-compaction only accepted typed user messages (turn starts)
as cut boundaries. The canonical host transcript — one typed task
followed by a long assistant tool_use / user tool_result loop — has no
turn start past index 0, so findCutIndex snapped to 0 and
runAgenticCompaction returned undefined: the UI showed "auto-compacting"
then "auto-compaction-skipped" on every turn while the context kept
growing. Re-compaction had the same failure permanently, because the
projected transcript starts with a compaction summary message, which is
excluded from turn starts.

Assistant messages are equally safe boundaries: an assistant's tool_use
keeps its result in the user message that follows it, so a cut there
never orphans half of a tool pair. Typed-user protection is preserved —
when a typed turn exists past index 0 the cut still stays at or before
it, so the latest typed prompt is never folded into the summary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* add compaction fixtures for testing

* basic compaction improvement

* feat: attach metadata to the merged compaction message

* fix(core): address review comments on compact-session script

- add cline provider to the API key env defaults (CLINE_API_KEY)
- accept legacy string-content messages in readMessages
- print usage instead of a stack trace when --provider/--model are missing

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(core): preserve basic compaction across restores

## Summary

- keep tool-result message IDs stable across restore/persist round-trips
- preserve concluding assistant responses as real messages during basic compaction
- freeze prior compaction output so later passes only fold newly added history
- accumulate removed-message and usage metadata across repeated compactions
- update the basic compaction fixture and regression coverage

## Problem

Tool-result IDs were re-suffixed every time persisted messages were converted
back into agent messages. Because compaction state hashes the source message
prefix, restoring a session changed that hash and invalidated an otherwise
successful compaction, causing the full transcript to be sent again.

Basic compaction also reprocessed its own output on subsequent passes. This
could stack duplicate system notices, discard assistant conclusions retained by
the previous pass, and replace cumulative compaction statistics with values
from only the latest pass.

## Solution

Only add tool-result suffixes when splitting a mixed message, leaving already
split and single-result message IDs unchanged. Mark non-user compaction
survivors as preserved, carry those messages through future passes verbatim,
and budget older turns' final assistant answers as first-class messages.
Compaction metadata now adds prior removed-message and usage totals to the work
performed by the current pass.

## Validation

- 66 focused codec and compaction tests pass
- @cline/core typecheck and smoke typecheck pass
- Biome checks pass for all changed TypeScript files
- git diff --check passes

* fix unit test

* fix compaction defaults and fallback

* fix basic compaction credential lookup

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 17:41:58 -07:00
Saoud Rizwan 1adce5e56d chore(desktop): release v0.0.3 2026-07-21 17:31:29 -07:00
Saoud Rizwan 73583ea178 polish(ui): reasoning trigger reads just 'Thinking' — drop status text and brain icon (#12460)
The collapsed reasoning block header showed 'Thought process · Complete'
with a brain icon; PM feedback is that the status text and icon read as
noise. The trigger is now just the label + disclosure chevron, with
'Thinking' as the default label in both streaming and complete states.
Removes the now-unused cline-chat-reasoning-status style and BrainIcon.
2026-07-21 17:28:25 -07:00
Saoud Rizwan 21a0141b86 chore(desktop): release v0.0.2 2026-07-21 17:00:20 -07:00
Saoud Rizwan ecb71ba7cc fix(desktop): make scheduler row actions work and add hover tooltips (#12428)
* fix(desktop): make scheduler row actions work and add tooltips (CLINE-2745)

- Replace the view icon's window.alert (a no-op inside the Tauri webview)
  with a proper schedule-details dialog
- Trigger schedule.trigger with wait: false so 'run now' queues the run
  and returns immediately instead of blocking until the whole agent run
  finishes (which outlived the webview's 120s request timeout)
- Add hover tooltips to all schedule row actions (view, edit, run now,
  pause/resume, delete, enable switch)
- Show a spinner on the run-now button while triggering and toast on
  success/failure
- Return lastExecutions from list_routine_schedules so 'Last result'
  actually populates (it was always '-')
- Mount <Toaster /> in the root layout; toast() calls app-wide were
  previously rendered nowhere

* fix(desktop): per-schedule last executions and concurrent row actions

- list_routine_schedules backfills the latest execution for schedules
  whose runs fell outside the 50-newest global window (skipping
  schedules that have never run), so every row can show a last result
- busy/triggering row state is now a set keyed by schedule id, so one
  action finishing no longer clears another row's in-flight spinner

* fix(desktop): reject same-row schedule actions synchronously

Two rapid clicks on the same row action could both fire before React
rendered the disabled state; the first completion then cleared the
shared busy id while the second request was still pending (and run-now
would enqueue two runs). Guard entry through a ref that mirrors
busyScheduleIds so the duplicate click is rejected before any request
is sent.
2026-07-21 16:41:05 -07:00
Saoud Rizwan f5224abdf5 feat(desktop): auto-updates + automated signed releases from GitHub Actions (#12420)
* feat(desktop): auto-update via Tauri updater with restart prompt

The Rust shell now checks the desktop-latest GitHub release feed on launch
and every 2 hours, downloads and stages updates in the background, and
exposes get_update_status/restart_to_apply_update commands. The webview
polls the status and shows a persistent toast with a one-click restart once
an update is staged; ignored updates apply on next launch. Updater
artifacts are only produced with the CI config overlay
(tauri.release.conf.json) so local packaging keeps working without the
updater signing key. Also mounts the previously-unmounted Toaster so
existing toast() calls render.

* ci(desktop): add desktop-publish release workflow and publish-desktop skill

desktop-publish.yml mirrors cli-publish: dispatch with a desktop-vX.Y.Z
tag + confirm gate, validates the tag against package.json and
tauri.conf.json, builds signed+notarized DMGs for aarch64 (native) and
x86_64 (cross-compiled sidecar via bun --target), generates the updater
manifest, publishes the versioned GitHub release, refreshes the rolling
desktop-latest auto-update feed, and posts to Slack. Adds the release
skill, changelog, and README docs for the required GitHub secrets.

* fix(desktop): address review — outlast sidecar shutdown window, dedupe update toast across remounts

stop() now polls for 7s before escalating to kill, past the sidecar's own
5s SHUTDOWN_TIMEOUT_MS graceful-shutdown budget, so clicking Restart now
(or quitting) during session persistence can't SIGKILL the sidecar
mid-write. notifiedVersion moves to module scope so a page remount doesn't
re-toast an update the user already dismissed.

* docs(desktop): move publish-desktop skill to .cline/skills, slim README release section

Match the publish-cli convention: the skill lives in .cline/skills/ and is
symlinked from both .agents/skills/ and .claude/skills/ so all agents pick
it up. The README's release section shrinks to a pointer + the two
never-lose invariants (desktop-latest feed, updater private key); the repo
secrets table moves into the skill, which also fixes its dangling reference
to a 'Release automation' README section and escapes the pipe that broke
the GFM table cell.
2026-07-21 16:37:18 -07:00
Saoud Rizwan 85484abf7a feat(desktop): align settings page with hub dashboard (#12427)
* feat(desktop): align settings with hub dashboard (ENG-2286)

- Break out Customizations into its own sidebar nav group (Plugins,
  Skills, MCP, Hooks, Rules, Agents, Tools), mirroring the hub
  dashboard's customizations break-out, replacing the single
  Customizations entry (Rules-only) and the MCP Marketplace entry
- Port the hub's account view: signed-out state with working Sign
  in/Sign out (the old Sign Out button had no handler), auth-error
  detection, disabled tabs when signed out, PageFrame/PageHeader layout
- Port the hub's add-provider view for consistent PageFrame layout

* fix(desktop): merge the two MCP sidebar entries into one

The sidebar showed MCP twice: 'MCP Servers' under Settings (full
management: add/edit/toggle/delete) and 'MCP' under Customizations
(marketplace browse with uninstall-only cards). Keep the single 'MCP'
entry under Customizations to match the hub sidebar, and route it to
McpServersContent with the marketplace embedded: the management cards
now render as the marketplace view's Installed section, so one page
covers add/edit/toggle/delete plus catalog install.

* fix(desktop): stop long marketplace taglines forcing page-wide overflow

line-clamp (webkit-box) paragraphs report their full unwrapped text
width as intrinsic min-content, and grid/flex items default to
min-width:auto, so long MCP taglines pushed the whole marketplace grid
(and the page) wider than the viewport. Add min-w-0 at each grid-item
level so cards clamp to the container and the tag row scrolls within
itself.
2026-07-21 16:33:54 -07:00
Saoud Rizwan 0b0e2fbab8 feat(desktop): add open-in-editor and copy-path actions to diff view (#12434)
* feat(desktop): add open-in-editor and copy-path actions to diff view

Adds per-file actions to the session diff view (CLINE-2738):
- copy the file path (resolved to an absolute path against the session cwd)
- open the file in a code editor via a new open_file_in_editor sidecar
  command that prefers editor CLIs (code/cursor/windsurf/zed/subl) and
  falls back to macOS app bundles, then the OS default opener

* fix(desktop): handle Windows editor shims and mount Toaster for failure feedback

Address greptile review on #12434:
- route .cmd/.bat editor shims through cmd.exe (spawn can't launch them
  directly) and attach spawn error listeners so async launch failures
  fall back to the OS opener instead of crashing the sidecar
- mount the app-wide Toaster (same lines as #12428) so copy/open failure
  toasts are actually visible

* fix(desktop): guard Windows shell launches against cmd metacharacters

cmd.exe re-parses metacharacters inside arguments even when Node quotes
them (the reason spawning .cmd files without a shell is banned), so a
file path like 'report & evil.cmd' handed to the cmd /c shim launch
could execute a second command. Reject such paths with a clear error on
win32 and skip shim executables containing metacharacters (CodeQL
js/shell-command-injection-from-environment on #12434).

* feat(desktop): editor picker dropdown + copy button next to path in diff view

Review feedback on #12434:
- Renee: open-in-editor is now a dropdown listing the editors actually
  installed on the machine (new list_available_editors sidecar command;
  PATH CLIs + macOS app bundles), plus a system-default entry.
  open_file_in_editor accepts an optional editor id; omitted keeps the
  old auto-cascade, so older sidecars and existing callers still work.
- Beatrix: copy-path button now sits right after the filename (GitHub
  style) instead of grouped at the right edge; an invisible flex spacer
  keeps the dead space clickable as a collapse toggle.

* feat(desktop): brand icons + kanban editor set in diff-view editor picker

Match the kanban open-in dropdown: monochrome brand glyphs (VS Code,
Cursor, Windsurf, Zed, Xcode, IntelliJ IDEA) rendered inline with
currentColor so they follow the theme, an 'Open in' menu header, and a
system-default entry with a generic icon. Catalog grows to the kanban
editor list (adds VS Code Insiders via code-insiders, IntelliJ via
idea, Xcode via xed; macApps is now a list so IntelliJ CE is found).
Sublime Text keeps a generic file-code glyph (kanban has no sublime
icon).
2026-07-21 16:31:48 -07:00
Saoud Rizwan 1e2e8fe81b fix(desktop): preserve MCP server oauth tokens and metadata across dialog edits (#12426)
* fix(desktop): preserve oauth and metadata when upserting MCP servers

upsert_mcp_server rebuilt the settings record from scratch, so editing a
remote server through the dialog silently wiped its oauth block (tokens)
and any plugin-ownership metadata. Merge machine-managed fields from the
existing record (following previousName across renames) into the upserted
entry.

* fix(desktop): drop MCP server oauth tokens when transport or URL changes

Editing a remote server's URL or transport previously carried the old
server's OAuth tokens onto the new registration, sending credentials
issued for one endpoint to a different one. Preserve oauth only when
the effective transport type + URL are unchanged (rename-safe).

* fix(desktop): treat legacy "http" MCP transport as streamableHttp alias

Core config-loader maps transportType "http" to streamableHttp, so a
legacy record resaved through the dialog is the same endpoint; without
normalizing, mcpTransportIdentity saw it as changed and dropped oauth.

* fix(desktop): default typeless URL-based legacy MCP records to sse

Core config-loader resolves a legacy flat record with a url but no
type/transportType as sse, while the sidecar defaulted to stdio. That
skewed mcpTransportIdentity (dropping oauth on a no-op edit) and made
list_mcp_servers report such records as stdio to the dialog.
2026-07-21 16:24:28 -07:00
Tomás Barreiro 78c6724c6a Add auth metadata to the auth telemetry (#12274)
* Add session and user id to auth telemetry events

* Add the auth metadata

* Address comments

* Add metadata to successful events

* remove user ids from the types

* fix tests

* address comments

* replace startedAtMs with sessionDurationMs

* fix tests

* update based on latest main

* fix imports
2026-07-22 00:41:43 +02:00
Bee f2a895cf86 fix(desktop): rebuild sessions when switching providers (#12454)
* fix(desktop): rebuild sessions when switching providers

Recreate active sessions with their existing transcript and compaction state before sending to a different provider. Preserve provider-specific connection settings and distinguish provider changes from model-only updates.

Add coverage to verify provider switches rebuild the session before sending.

Currently SendSessionInput has no provider/model configuration, so the desktop client must perform that lifecycle transition before sending. The cleaner long-term API would make provider selection part of an atomic turn request—something like send({ sessionId, prompt, providerId, modelId })—and let Core decide whether rebootstrap is necessary.

* fix(desktop): harden provider session transitions

* fix(desktop): make provider rebuilds transactional
2026-07-21 15:20:15 -07:00
Saoud Rizwan 1585999251 fix(desktop): make account page functional (#12424)
* fix(desktop): make account page functional

The account page rendered data but every interaction was dead:

- Sign Out button had no click handler at all. Wire it to clear the
  cline provider auth (same flow as cline-hub), show a signed-out card
  with a working Sign In (browser OAuth) instead of a raw error + Retry,
  and refresh the shared account context so the sidebar identity updates.
- Organization rows were static divs. Make them switchable (including a
  Personal row) via the existing cline_account switchAccount operation,
  with a pending spinner and overview + context reload after switching.
- External links (+ Credit, + Create org, open dashboard) used
  target=_blank anchors, which are silently dropped inside the Tauri
  shell (no window opener configured). Route them through a new
  open_external_url sidecar command that opens the host default browser
  (http/https only); plain web mode falls back to window.open.
- + Credit pointed at the organization credits page even for personal
  accounts; use dashboard/account?tab=credits when no org is active.
- Guard the browser-open spawn with an error listener so a missing
  opener binary can't crash the sidecar with an unhandled error event.
- Disable Usage/Billing tabs while signed out (they can only error).

Closes CLINE-2737

* fix(desktop): harden external URL opener and auth error classification

- open URLs on Windows via rundll32 instead of cmd /c start so URL
  metacharacters cannot be parsed as shell operators
- surface opener spawn failures instead of always reporting opened: true
- classify only definitive signals (missing token, re-auth required,
  status 401) as signed-out; transient refresh/permission errors keep
  the retryable error UI

* fix(desktop): reject external URL open when the launcher exits non-zero

The opener promise resolved on the spawn event, so a launcher that
started but failed to hand off (xdg-open exits 3 when no handler is
available) still reported opened: true. Reject on a fast non-zero exit;
if the launcher is still running after a 2s grace window, assume the
handoff worked rather than blocking on a launcher that lingers.
rundll32 exits 0 even on failure, so Windows stays best-effort.
2026-07-21 15:19:02 -07:00
Saoud Rizwan 2c556a4c94 feat(desktop): simplify Add MCP Server dialog with Local/Remote server types (#12425)
Replaces the raw stdio/sse/streamableHttp transport dropdown with a
plain-language Local vs Remote choice (CLINE-2748). Local (stdio) stays
the default per the MCP spec's "Clients SHOULD support stdio whenever
possible"; picking Remote defaults to Streamable HTTP with SSE offered
as a legacy option. Working directory and Metadata JSON move behind an
Advanced collapsible (auto-expanded when editing a server that uses
them), and the server list badge now shows friendly transport labels.
2026-07-21 14:53:33 -07:00
Saoud Rizwan 9a80fa04c2 fix(desktop): keep thinking indicator visible until first model output (#12432)
* fix(desktop): keep thinking indicator visible until first model output

The webview only rendered the Thinking indicator while the chat status
was 'starting', but Core reports 'running' as soon as the turn is
dispatched -- well before the first streamed token arrives. The spinner
flashed for the RPC roundtrip and then disappeared, leaving ~1s of dead
air (model time-to-first-token) before the assistant bubble appeared.

Keep the indicator up while the session is running and the model has
not produced output yet: no streaming assistant message, last visible
message is the user's prompt, and no approvals/questions pending.

Closes CLINE-2739

* test(desktop): tighten thinking indicator test formatting
2026-07-21 14:52:21 -07:00
Saoud Rizwan 22a1fa2c84 feat(cli): upgrade opentui 0.1.102 -> 0.4.3 (#12453)
* feat(cli): upgrade opentui 0.1.102 -> 0.4.3

Brings the TUI stack up from April's 0.1.102 to the current 0.4.x line
(0.4.4/0.4.5 are <7 days old and blocked by the registry release-age
gate; bump again once they age out).

- @opentui/core + @opentui/react 0.1.102 -> 0.4.3
- opentui-spinner ^0.0.6 -> ^0.0.7 (0.0.7 peers on @opentui/core ^0.3.4)
- react-reconciler pin 0.32.0 -> 0.33.0 to match @opentui/react 0.4.x

@opentui-ui/dialog stays at 0.1.2 (abandoned upstream, peers ^0.1.69 so
bun warns on install) but its runtime surface (DialogProvider,
useDialog, useDialogKeyboard) works against core 0.4.3 - the tui-test
command-palette spec renders a real dialog in a pty and passes.

Validation: tsc clean, unit 889/890 (the one failure repros on an
untouched main checkout - stale bun pm pack guard expectation), tui-test
62/62 across repeated runs.

* fix(cli): force single opentui generation via root overrides

The previous commit left @opentui-ui/dialog's ^0.1.69 peer range
unsatisfied by core/react 0.4.3, so bun recorded nested
@opentui/core@0.1.102 + @opentui/react@0.1.102 copies under the dialog
package in bun.lock. Local installs happened to link the dialog against
the hoisted 0.4.3 store variant (which is why tui-test passed), but a
fresh install from the lockfile - CI, release builds - would follow the
nested entries and run two renderer generations in one process: dialog
components extending 0.1.102 Renderable classes inside a 0.4.3 renderer
tree.

Pinning @opentui/core and @opentui/react in the root overrides block
forces every consumer, dialog included, onto 0.4.3. The nested lockfile
entries are gone and a runtime identity check confirms
DialogContainerRenderable's prototype chain reaches the same class
objects as the 0.4.3 core the app imports.

Side effect: changing overrides makes bun fully re-resolve the
lockfile. The only drift is ~108 @radix-ui entries nested under the
vscode webview-ui workspace moving to newer patch versions (~1.1.15 ->
~1.1.19); webview-ui's full build (tsc -b && vite build) passes with
them. This drift would land at the next release anyway since bun run
version deletes and re-resolves bun.lock.

Re-validated: tsc clean, tui-test 62/62, unit 889/890 (same single
pre-existing bun pm pack guard failure that repros on untouched main).
2026-07-21 14:49:26 -07:00
Parafee41 57d364ffc2 fix(cli): keep status delivery failures non-fatal (#12401)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-21 14:18:02 -07:00
TheRealSpencer 0912f34286 fix(deps): bump mermaid to 11.16.0 and protobufjs to 7.6.5 (#12445)
Address mermaid CVEs (CVE-2026-41148/41149/41150/41159) and
protobufjs CVEs (CVE-2026-54269, CVE-2026-48712) by pinning
patched versions via package deps and workspace overrides.
2026-07-21 15:38:59 -05:00
Etisha Garg bdb216c110 Revert "docs: add Kimi K3 to ClinePass documentation (#12380)" (#12407)
This reverts commit cc29955c2d.
2026-07-21 10:02:58 -07:00
Mikołaj Kondratek c92d4e7553 fix(sdk): preserve file line endings in the editor tool executor (#12305)
* fix(sdk): preserve file line endings in editor tool executor

The native editor executor split and joined file content on "\n" only.
On CRLF files (common on Windows), insertInFile left existing lines with
trailing "\r" while inserted lines were LF-only, producing mixed line
endings. Because reads go through readline with crlfDelay (which strips
"\r"), the model always emits LF-only old_text, so subsequent exact-match
replaceInFile calls failed; multi-line replace on pure-CRLF files was
broken the same way.

Detect the file's dominant EOL and normalize: insertInFile now splits
content and new_text on /\r\n|\n/ and joins with the detected EOL, and
replaceInFile normalizes old_text/new_text to the file's EOL before
matching. The str_replace diff output also splits on /\r\n|\n/ so it no
longer embeds stray "\r" in diff lines sent back to the model.

Reported via JetBrains marketplace review #141234 (DeepSeek + CLion on
Windows).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sdk): address review — accurate EOL doc, literal $-sequences in replace

Reword the detectLineEnding JSDoc: it is a presence check for CRLF, not a
majority vote, so say so instead of claiming "dominant" EOL.

Use a replacer function in replaceInFile so "$"-sequences in new_text
($&, $', $`, $$, $n) are inserted literally instead of being expanded by
String.prototype.replace. Pre-existing bug surfaced during review; adds a
regression test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(sdk): clarify why EOL detection is a CRLF presence check

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 16:33:37 +09:00
Renee Huang b919a7e86c docs: mark .clineignore as deprecated soon (#12410)
* docs: mark .clineignore as deprecated soon

Add a deprecation notice to the .clineignore page and update pages that recommend it. Enforcement of ignore rules is extremely difficult (agents can get around them via @ mentions or shell commands), and the feature is orphaned in the VS Code/JetBrains extension (ClineIgnoreController), not part of the Cline SDK or CLI.

* docs: update clineignore deprecation wording

* wording changes

* update clineignore docs with plugin reference

* fix plugin example url

* edit clineignore docs file

* update formatting for clineignore doc

* clean up clineignore docs file

---------

Co-authored-by: Cline <bot@cline.bot>
Co-authored-by: TheRealSpencer <spencer@cline.bot>
2026-07-20 18:01:37 -07:00
Bee eefbe9fb18 feat(desktop): log telemetry events in desktop app (#12416)
* feat(desktop): log telemetry events in desktop app

* Address feedback
2026-07-21 02:50:56 +02:00
Bee 402b9994d8 feat(desktop): tool use group (#12415)
* feat(desktop): tool use group

* focus block

* apple p2 feedback
2026-07-20 15:24:05 -07:00
Bee fce1b97512 feat(desktop): improve session overviews and clarify workspace errors (#12414)
* feat(desktop): improve session overview and workspace errors

* address p1
2026-07-20 14:41:30 -07:00
Bee e7b0cec8e1 fix(desktop): diff view layout with proper scrolling (#12411)
Use full-height flex sizing, prevent header shrinking, and allow the scroll area to contract so file diffs remain scrollable within the view.
2026-07-20 23:04:53 +02:00
Bee 353ddc10f4 feat(desktop): add account context and window title utilities (#12348)
* fix(desktop): filter project paths

Best effort to remove desktop and user's home directory from showing up in project list in the desktop app.

* feat(desktop-app): add account context and window title utilities

- Add AccountContext provider and hooks for managing Cline account identity
- Add account-context.tsx and account-context.test.tsx
- Add desktop-window-title.ts and desktop-window-title.test.ts
- Update workspace-paths.ts with new utility functions
- Update agent-sidebar.tsx and agent-sidebar.test.tsx to use account context
- Update page.tsx to integrate account context
- Update sidecar/commands.ts to support account operations
- Update core SDK exports

This adds proper account identity management and window title utilities for the desktop app.

* dedup normalizeWorkspacePath

* home page update
2026-07-20 21:40:39 +02:00
Ara cabeb61036 Add minimal task lifecycle telemetry (#11851)
* Add minimal task lifecycle telemetry

* fix(vscode): use runner-safe auto approval assertions

* fix task lifecycle telemetry cancellation ordering
2026-07-20 20:45:02 +02:00
Etisha Garg cc29955c2d docs: add Kimi K3 to ClinePass documentation (#12380) 2026-07-20 09:23:02 -07:00
Saoud Rizwan c2faf38d72 chore(cli): release v3.0.46 2026-07-18 23:14:41 -07:00
Saoud Rizwan 4dab17769c fix(cli): detect real insufficient_credits error from Cline API (#12394) 2026-07-18 23:11:46 -07:00
Saoud Rizwan 396032cd3b chore(cli): release v3.0.45 2026-07-18 21:03:37 -07:00
Saoud Rizwan f33ab3a872 chore(sdk): release v0.0.65 2026-07-18 20:46:28 -07:00
Saoud Rizwan 2ca8364ffc docs: add Kimi K3 to ClinePass model list and reference pricing (#12393) 2026-07-18 20:35:48 -07:00
Saoud Rizwan 2ef81be703 feat(llms): make Claude Code and Codex provider packages optional peers (#12379)
ai-sdk-provider-claude-code and ai-sdk-provider-codex-cli were hard
dependencies of @cline/llms, so every npm install of the cline CLI
pulled their native binaries (~250MB claude-agent-sdk platform binary,
~105MB @openai/codex) even for users who never select those providers.

Move both to optional peerDependencies (kept as devDependencies so
monorepo builds still bundle the JS) and load them via literal dynamic
imports in community.ts, mirroring the existing opencode-sdk pattern.

The Claude Code provider now resolves the claude executable explicitly:
bundled platform package when present, otherwise a user-installed
claude from PATH, passed via defaultSettings.pathToClaudeCodeExecutable.
The agent SDK's own resolution cannot be used from Bun-compiled
binaries because it anchors on the virtual bunfs where node_modules
lookups never see packages on disk. Codex already degrades gracefully
(npx -y @openai/codex, then codex on PATH).
2026-07-18 20:30:24 -07:00
Saoud Rizwan 359445ae0c fix(sdk): stop exposing the team spawn tool to teammates (#12371)
* fix(sdk): retry runs once after refreshing expired OAuth credentials

Teammate and subagent sessions inherit the OAuth access token as a
snapshot at spawn time and had no refresh path: when the token expired
while the lead was blocked (e.g. in team_await_runs), their next model
call died with the provider's raw 401 body. Only the lead's turn-start
sync and runWithAuthRetry could refresh, and neither runs mid-turn.

Add an onAuthError hook to AgentConfig, wired once per session by
LocalRuntimeHost: it refreshes credentials through the shared
single-flight RuntimeOAuthTokenManager and propagates the new key to
the lead, delegated defaults, and all teammates via the existing
updateConnection channels. SessionRuntime retries a run once when it
failed with an auth-like error and the refresh succeeded, continuing
from the persisted trail so completed iterations aren't replayed.

Also fix isLikelyAuthError to lowercase string inputs; the server's
'Unauthorized: ...' message only matched when wrapped in an Error.

* fix(sdk): report errored teammate runs as failed instead of completed

Model-stream failures return results with finishReason 'error' rather
than throwing, so executeQueuedRun marked such runs 'completed' with
the error buried in resultSummary. Throw into the existing failure
path so the run reports status 'failed' (with run.error set and a
RunFailed event) and the retry machinery engages when maxRetries
allows.

* fix(sdk): stop exposing the team spawn tool to teammates

Spawning is lead-only, enforced at execution time, so teammates that
saw team_spawn_teammate in their toolset burned turns on 'Only the
lead agent can manage teammates.' rejections before falling back to
doing the work themselves.
2026-07-18 20:21:35 -07:00
Saoud Rizwan d9e2e9c76b fix(sdk): report errored teammate runs as failed instead of completed (#12370)
* fix(sdk): retry runs once after refreshing expired OAuth credentials

Teammate and subagent sessions inherit the OAuth access token as a
snapshot at spawn time and had no refresh path: when the token expired
while the lead was blocked (e.g. in team_await_runs), their next model
call died with the provider's raw 401 body. Only the lead's turn-start
sync and runWithAuthRetry could refresh, and neither runs mid-turn.

Add an onAuthError hook to AgentConfig, wired once per session by
LocalRuntimeHost: it refreshes credentials through the shared
single-flight RuntimeOAuthTokenManager and propagates the new key to
the lead, delegated defaults, and all teammates via the existing
updateConnection channels. SessionRuntime retries a run once when it
failed with an auth-like error and the refresh succeeded, continuing
from the persisted trail so completed iterations aren't replayed.

Also fix isLikelyAuthError to lowercase string inputs; the server's
'Unauthorized: ...' message only matched when wrapped in an Error.

* fix(sdk): report errored teammate runs as failed instead of completed

Model-stream failures return results with finishReason 'error' rather
than throwing, so executeQueuedRun marked such runs 'completed' with
the error buried in resultSummary. Throw into the existing failure
path so the run reports status 'failed' (with run.error set and a
RunFailed event) and the retry machinery engages when maxRetries
allows.
2026-07-18 20:19:17 -07:00
Saoud Rizwan d859a86a6f fix(sdk): retry runs once after refreshing expired OAuth credentials (#12369)
* fix(sdk): retry runs once after refreshing expired OAuth credentials

Teammate and subagent sessions inherit the OAuth access token as a
snapshot at spawn time and had no refresh path: when the token expired
while the lead was blocked (e.g. in team_await_runs), their next model
call died with the provider's raw 401 body. Only the lead's turn-start
sync and runWithAuthRetry could refresh, and neither runs mid-turn.

Add an onAuthError hook to AgentConfig, wired once per session by
LocalRuntimeHost: it refreshes credentials through the shared
single-flight RuntimeOAuthTokenManager and propagates the new key to
the lead, delegated defaults, and all teammates via the existing
updateConnection channels. SessionRuntime retries a run once when it
failed with an auth-like error and the refresh succeeded, continuing
from the persisted trail so completed iterations aren't replayed.

Also fix isLikelyAuthError to lowercase string inputs; the server's
'Unauthorized: ...' message only matched when wrapped in an Error.

* feat(telemetry): emit user.auth_run_retry when a run is retried after credential refresh

Addresses Greptile review on the auth-retry PR: the refresh itself was
already instrumented (auth_refresh_soft_failure / auth_logged_out fire
inside getValidClineCredentials), but the new retry transition was not.
The recovered flag counts runs that would previously have died with the
raw provider 401 — the direct production measure of this fix working.
2026-07-18 20:08:18 -07:00
Saoud Rizwan 0b7b9c1b3d fix(llms): add Kimi K3 to bundled ClinePass model fallback (#12392)
* fix(llms): add cline-pass/kimi-k3 to bundled model catalog fallback

* fix(llms): derive cline-pass default model from catalog authored order

Adding kimi-k3 (newest releaseDate) to the bundled cline-pass catalog
would have flipped firstGeneratedModelId — which sorts by release date —
to cline-pass/kimi-k3, silently changing the default model for new
ClinePass setups. Use the catalog's authored order instead, which mirrors
the recommended-models endpoint's curated order (intended default first,
subscription models before free ones).
2026-07-18 20:03:19 -07:00
Dominic Cooney 557d725690 fix(vscode): shell mismatch between prompt, execution, and user configuration on Windows (#12331)
* Rationalize shell identification and prompting, especially on Windows.

* Probe all pwsh install locations for the Windows default shell.

The default-shell fallback only checked the Program Files pwsh path,
so Microsoft Store installs of PowerShell 7 fell back to Windows
PowerShell while VS Code's own terminal launched pwsh. Share one
candidate list between the sync default-shell check and the async
PowerShell prober. Also drop an 'as string' cast that hid the
setting's type from the checker.

* Address shell resolution review feedback

* Resolve array-valued terminal profile paths on macOS and Linux too

VS Code permits terminal profile 'path' to be string | string[] on every
platform, not just Windows. The resolver (env expansion, first-existing
selection, PATH lookup) is now platform-generic: it uses the host path
module's separators and delimiter, probes PATHEXT only on Windows, and
treats env var names case-insensitively only on Windows. The macOS and
Linux getters route through it instead of returning the raw config value,
which crashed getShellKind() for array values.

* Apply terminal profile changes at the model-request boundary

A terminal profile change previously triggered a deferred session rebuild
to refresh the run_commands tool description. While a task was running the
rebuild waited, so the description could name one shell while commands
executed in another for the rest of the turn.

Instead of rebuilding, createShellTool now accepts a shell provider
function and re-derives the description each time the runtime reads it,
which happens exactly when a model request is built. The VS Code tool
snapshots {profileId, shell} in that provider; both execution paths (the
background spawn and the foreground terminal, via a new profile parameter
on getOrCreateTerminal) consume the snapshot. Commands produced by an
in-flight inference therefore run with the shell the model was told about,
and a mid-turn profile change takes effect when the tool results are sent
back: the next request names and uses the new shell.

The profile-change session rebuild path (handleTerminalProfileChanged) is
removed along with its deferred-rebuild window.

* Use the real createShellTool in the vitest @cline/core stub

The stub's hand-rolled createShellTool duplicated the 'shell must be a
string' invariant instead of exercising the code that enforces it
(getShellKind via description building), so the array-valued-profile
regression test proved only that the stub threw, not that the real tool
survives. Re-export the real implementation from SDK source — the same
pattern the stub already uses for the apply-patch and editor executors —
and assert on the actual generated descriptions, including that a profile
change is reflected at the next description read.

* Harden shell profile path resolution edge cases

- Warn and skip profile paths containing variable references beyond
  \ (e.g. \) instead of silently probing a
  literal path that can never exist; later candidates and the platform
  default still apply.
- Document that an overriding bash executor in createBuiltinTools bypasses
  the resolved canonical shell and must honor it to keep the run_commands
  description truthful.
2026-07-18 04:23:00 +02:00
Saoud Rizwan 7274d8badc feat(ui): add agent chat components, Storybook, and npm releases (#12374)
* feat(ui): add shared agent chat components and Storybook

* ci(ui): add standalone npm publishing

* docs(ui): keep release commands environment-neutral

* refactor(ui): simplify package validation

* refactor(ui): tighten package and release contracts

* docs(ui): remove duplicate install guidance

* ci(ui): make publishing workflow manual-only
2026-07-17 18:22:57 -07:00
Bee d1837366c0 chore(llms): update model catalog (#12366)
Update model catalog with bun run build:models
Version updated to 1784318695007
2026-07-17 13:28:59 -07:00
Saoud Rizwan c380daf4a3 docs(ui): add adoption primer (#12367) 2026-07-17 13:21:15 -07:00
Bee c564045d81 chore(cli): includes version numbers in hub status output (#12358)
Includes version numbers in hub status output and doctor command to make debugging with user easier.
2026-07-17 05:37:54 +02:00
Saoud Rizwan 9a5e1751b2 chore(cli): release v3.0.44 2026-07-16 18:38:54 -07:00
Saoud Rizwan 131e25e1a1 chore(sdk): release v0.0.64 2026-07-16 18:14:58 -07:00
Saoud Rizwan a7ff007af9 chore(cli): release v3.0.43 2026-07-16 17:52:46 -07:00
Bee ef27f45080 fix: max output token handling (#12031)
* fix: max output token handling

* shared

* max reasoning budgetTokens

* fix unit test

* fix: address review feedback on max output token handling

- OpenRouter effort branch sends only reasoning.effort (OpenRouter rejects
  effort combined with reasoning.max_tokens)
- OpenAI Responses forwards explicit caller maxTokens for API-key usage;
  ChatGPT OAuth and synthesized gateway defaults are still omitted
- Gateway lifts the synthesized default output cap above explicit Anthropic
  reasoning budgets so max_tokens > thinking.budget_tokens holds

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: address second-round review feedback on max output token handling

- Replace the gateway-only requestedMaxTokens field with a defaultedMaxTokens
  flag set when the gateway synthesizes a cap, so explicit maxTokens from
  direct provider callers is forwarded by default (greptile P1)
- Check the parsed hostname instead of a URL substring when detecting the
  ChatGPT OAuth backend (CodeQL)
- Drop the empty else-if branch in toAiSdkMessages in favor of an explicit
  emptiedByDroppedReasoning condition (greptile P2; biome rejects the
  suggested bare continue)
- Dedupe isPositiveFiniteNumber by exporting it from gateway.ts (greptile P2)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor: extract isPositiveFiniteNumber into providers/utils.ts

Move the shared helper to its own module as suggested in review instead
of exporting it from gateway.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: remove unrelated VS Code changes

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-16 17:50:22 -07:00
Dominic Cooney e3c6d51072 fix: recognize frontmatter with a leading UTF-8 BOM (#12277)
* fix(vscode): recognize SKILL.md frontmatter with a leading UTF-8 BOM

SKILL.md files saved with a UTF-8 BOM (e.g. by Windows Notepad's 'UTF-8 with BOM' encoding) were silently skipped and not recognized as skills, because gray-matter/regex-style frontmatter parsers require '---' at byte offset 0 and never accounted for the leading BOM byte sequence Node's utf-8 decoder does not strip.

Fixes the shared parseYamlFrontmatter() helper (used by skills, rules, workflows, and remote skill entries in the VS Code extension) and every duplicated ad-hoc frontmatter regex across the SDK/CLI/hub/desktop-app/example-plugin code paths to strip a leading BOM before matching.

Adds regression tests exercising the exact reported scenario (BOM-prefixed SKILL.md silently missing name/description) in frontmatter.test.ts, skills.test.ts, skill-frontmatter-toggle.test.ts, user-instruction-config-loader.test.ts, and configured-agent-config.test.ts.

Fixes https://github.com/cline/cline/issues/12151

* refactor(shared): centralize UTF-8 BOM stripping

* refactor(shared): add UTF-8 file readers

* docs: guide UTF-8 configuration reads
2026-07-17 09:37:20 +09:00
Saoud Rizwan 48bac25548 chore(sdk): regenerate lockfile for v0.0.63 2026-07-16 17:13:44 -07:00
Saoud Rizwan 37f5f104f3 chore(sdk): release v0.0.63 2026-07-16 16:47:40 -07:00
Saoud Rizwan 3577b52404 feat(core): emit mistake-limit telemetry from the session runtime (#12355)
Moves the task.mistake_limit_reached capture (#12354) from the VS Code
SdkController wrapper into @cline/core so every host (CLI, VS Code,
hub daemon) emits it via its session telemetry service.

The MistakeTracker gains an onLimitTelemetry hook fired exactly once
per limit hit, before the limit decision is resolved — including when
no onConsecutiveMistakeLimitReached callback is configured (the
default-stop path, which the extension-side capture missed). The
orchestrator wires the hook to captureMistakeLimitReached using its
reserved telemetry field, reading sessionId/modelId/providerId at fire
time so mid-session connection updates are reflected.

The now-redundant extension wrapper and TelemetryService method are
removed to avoid double-counting in VS Code.
2026-07-16 16:39:50 -07:00
Max 1843bc8ed0 fix(vscode): persist selected account organization (#12345)
* fix(vscode): persist selected account organization

* fix(vscode): ignore stale organization responses

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-16 16:08:07 -07:00
Max fead00ec57 fix(vscode): avoid duplicate OpenAI provider settings (#12346)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-16 16:07:48 -07:00
Max 238107d21c fix(vscode): preview auto-approved apply patches (#12349)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-16 16:05:24 -07:00
Saoud Rizwan 2063a661bd feat(vscode): capture telemetry when consecutive mistake limit is reached (#12354) 2026-07-16 15:58:21 -07:00
Dominic Cooney ec02d5862e Fix debug harness: run under node, drop ws dependency. (#12319)
The harness rotted after the npm-to-bun migration: the 'ws' package it imported is no longer in the dependency tree, and Playwright's _electron.launch() times out under bun (the debugee Electron starts but Playwright never finishes attaching; the same launch attaches in under a second under node). Use the runtime's built-in WebSocket for the CDP client and document that the harness must be run with node.
2026-07-17 07:43:32 +09:00
Mikołaj Kondratek 8452084842 fix: auto-discover OS trust anchors in the CLI wrapper (#11498)
* fix: auto-discover OS trust anchors in the CLI wrapper

The 3.x CLI ships as a Bun-compiled binary. Bun does not read the OS
trust store unless NODE_USE_SYSTEM_CA is set, and even with the flag its
Windows enumeration covers only the `Root` store, not `CA`/Intermediate
(verified empirically across the CLINE-2353 Windows repro rounds). So a
corporate MITM root is not trusted out of the box and inference fails
with "unable to get local issuer certificate". The pre-3.0 (Node) CLI
had no app-level CA handling either; users only succeeded by setting
NODE_EXTRA_CA_CERTS manually. The reporter's ask: have it just work
without the env var.

This follows the CLINE-2353 SDK fetch-threading change. That made the
inference client honor a host-provided proxy/CA-aware fetch, but on the
CLI Bun's global fetch is already proxy-aware and a fetch function
cannot cross the hub-daemon process boundary, so the CLI's missing piece
is trust material, not the fetch. Env vars do inherit across spawns.

The npm `bin/cline` wrapper runs on Node (not Bun), so it can read the
full OS store via tls.getCACertificates("system") (Node >= 22, no flag
required) — including the Windows `CA` store Bun skips — and hand the
certs to the Bun child via NODE_EXTRA_CA_CERTS, which both runtimes
honor. This mirrors the JetBrains plugin's configureCertificates(),
replacing "harvest from the IDE trust store" with "harvest from the OS".

The merge logic lives in a dependency-free, injectable-module CommonJS
helper (bin/ca-certs.cjs) so it is unit-testable and ships verbatim in
the generated wrapper package (publish copies bin/ wholesale). A
user-set NODE_EXTRA_CA_CERTS is merged ahead of the system certs; a
self-reference to the managed bundle is detected to avoid re-appending
every launch; when no system certs are available the user's setting is
left untouched. Writes are atomic (temp + rename) and owner-only.

Adds ca-certs.test.ts (13 cases) covering harvest filtering, user-bundle
PEM/DER/missing handling, newline-separated merge, managed-path
self-reference, and the no-system-certs no-op.

* fix: harden CLI auto-CA harvesting (review follow-ups)

Follow-ups from the CLINE-2353 review of the CLI auto-CA wrapper.

- H1: a legacy NODE_EXTRA_CA_CERTS set to an OS-path-delimited list
  ("a.pem;b.pem", the CLINE-2324 footgun Node never split) was stat'd as
  one file, failed, and silently dropped the user's certs. readUserCerts
  now tries the whole value as one file first, then splits on the OS path
  delimiter and reads each existing PEM, merging them all.
- M1: skip the rewrite when the managed bundle is already current, instead
  of re-harvesting and rewriting on every launch (mirrors the JetBrains
  hash-and-skip). configureNodeExtraCaCerts now returns a typed outcome
  (unchanged | written | write-failed-reused | write-failed |
  no-system-certs) with cert counts.
- M2: tolerate rename-over-existing failures (Windows EPERM/EBUSY when a
  concurrent child holds the file open) by removing the target and
  retrying, then falling back to a previously-written bundle. Combined
  with M1 the steady state no longer rewrites at all.
- M3: the wrapper prints a one-line diagnostic under CLINE_DEBUG=1
  (cert counts + managed path, or a warning when no OS certs were found
  or the write failed). Runs once per startup.
- M4: corrected the now-stale CLI guidance in shared/net.ts (the CLI no
  longer requires users to set NODE_EXTRA_CA_CERTS manually).
- L1: documented the auto-trust behavior, the managed ~/.cline bundle,
  the merge-not-replace override semantics, and CLINE_DEBUG in the CLI
  README.
- L4: trimmed the helper's file header; DI is still injectable for tests.

ca-certs.test.ts grows to 20 cases: adds readUserCerts (single path,
delimited split, missing-segment skip, managed-bundle exclusion, empty),
the unchanged/second-run skip, and a write-failure outcome via an
fs that throws.

* fix: address CLI auto-CA review issues (temp cleanup, cert count, test)

- writeBundle now hoists the temp path so the outer catch removes a
  partially-written temp file (e.g. ENOSPC / ACL failure mid-write).
  Previously only the inner double-rename failure cleaned up, so repeated
  disk-full/permission failures left a stale .tmp per launch in ~/.cline.
  The inner Windows-rename fallback now lets its failure fall through to
  the single cleanup path instead of duplicating rmSync.
- userCertCount now counts individual certificates (via countCerts, which
  tallies BEGIN CERTIFICATE markers) rather than the number of PEM files,
  so a user bundle with N intermediates reports N and is comparable to
  systemCertCount. countCerts is exported for testing.
- Adds tests for the write-failed-reused branch (stale bundle reused when
  the rewrite fails but the old file is still readable) and for countCerts
  (one file holding two certs reports 2).

* fix: warn when the CLI wrapper's Node cannot read the OS trust store

tls.getCACertificates("system") needs Node >= 22.15; on older hosts the
auto-CA harvest silently did nothing, which is indistinguishable from a
broken corporate proxy. Distinguish the missing-API case as its own
outcome (api-unavailable) and print a non-debug warning when the user
has no NODE_EXTRA_CA_CERTS of their own. Found in round-5 Windows
validation (wrapper under Node 22.1.0).

* fix: copy only certificate blocks into the managed CA bundle

Combined cert+key PEMs (nginx/haproxy-style server.pem) passed the
old contains-a-certificate check, so a user NODE_EXTRA_CA_CERTS
pointing at one duplicated the private key into the managed bundle,
where it outlives rotation of the original and gets no permission
tightening on Windows. Extract complete BEGIN/END CERTIFICATE blocks
instead; files with none are treated as not PEM, and certificates-only
files pass through byte-identical so the unchanged-skip stays stable.
Raised in PR review.

* fix: show the old-Node trust warning once per Node version

The api-unavailable warning printed on every CLI invocation, turning
an actionable nudge into stderr noise for users pinned to an old Node.
Stamp the warning per Node version under the cline dir: it shows once,
re-arms when the Node version changes, and a bookkeeping failure never
suppresses the diagnostic. Raised in PR review.
2026-07-16 10:14:15 -07:00
Dominic Cooney a41129a5db fix(vscode): restore 'Proceed While Running' for foreground terminal commands (#12320)
* First cut of 'proceed while running' for foreground tasks.

* Address review: flush partial line on detach; cap log before write; freeze partial output at detach.

* fix(vscode): cap detached command log replay
2026-07-15 22:46:37 -07:00
Saoud Rizwan 1ea34be611 chore(cli): release v3.0.42 2026-07-15 20:03:31 -07:00
Saoud Rizwan e72bc3cd14 chore(sdk): release v0.0.62 2026-07-15 19:46:58 -07:00
Tomás Barreiro 9c907af826 Send the Feature Flag Event when rolling out (#12325)
* Send the Feature Flag Event when rolling out

* Update apps/vscode-rollout/scripts/smoke-loader.mjs

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-15 19:39:37 -07:00
Saoud Rizwan e8d3d82522 fix(core): omit telemetry from hub tool contexts (#12326) 2026-07-15 19:33:12 -07:00
Max 7f9d2e96d9 fix(ollama): restore native API routing so context window and timeout settings work (#12286)
The 4.0.0 SDK migration routed Ollama through the generic OpenAI-compatible
vendor (/v1/chat/completions), which cannot express Ollama's options.num_ctx.
Every model loaded at Ollama's 4096-token server default, truncating Cline's
prompt and breaking most features (CLINE-2603, CLINE-2566, CLINE-2572).

- Add a native Ollama vendor backed by ai-sdk-ollama (wraps the official
  ollama client); num_ctx derives from the resolved gateway model's
  contextWindow at the adapter boundary, defaulting to 32768
- Persist the Model Context Window setting in providers.json via the
  pre-existing provider-neutral contextWindow field (legacy
  ollamaApiOptionsCtxNum state key kept as read fallback / write mirror),
  and surface it as the selected model's contextWindow so the chat
  indicator, compaction budgets, and num_ctx all agree
- Project ProviderConfig.maxInputTokens (where ProviderSettings.contextWindow
  lands) onto the selected gateway model in both gateway builders so
  CLI/Core hosts honor the configured value too
- Stop falling back to the bundled Ollama-Cloud catalog when /api/tags is
  empty; local-model-source providers keep the user's committed model
  instead of silently selecting a cloud model (nemotron)
- Wire Request Timeout (ms) with the legacy semantics (response must start
  within requestTimeoutMs || 30000; streaming never cut off mid-generation)
- Settings UI: gate the context-window field until provider config loads,
  skip unchanged writes, drop the custom prompt checkbox

Fixes CLINE-2603, CLINE-2566, CLINE-2572

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-15 17:55:29 -07:00
Saoud Rizwan d618f8073a fix(vscode-rollout): align bundle versions and harden combined publish workflows (#12321)
* fix(vscode-rollout): align bundle versions in the stable AB workflow

Found by Max in local testing: the union manifest's version (what the
Marketplace and auto-update see) is the stitch input, but each bundle's
About tab and telemetry extension_version read that bundle's OWN
package.json — so the stable combined VSIX reported three different
versions (dispatch input / main's 4.0.0 / legacy's 4.0.8) depending on
where you looked. The nightly channel doesn't have this problem
(nightlify.mjs stamps one version into everything); this gives the stable
channel the identity-preserving equivalent: scripts/set-version.mjs stamps
the dispatch version into each checkout after install, before its build.

Also fixes a latent ab-package bug while restructuring the steps: the
next-bundle build never ran build:sdk, so the @cline/* workspace deps had
no dist and esbuild would fail on a fresh CI checkout (the workflow has
never run end-to-end — the publish environment gate blocked pre-merge
dispatches). Split install/build:sdk/align/build into separate steps,
mirroring the nightly workflow.

* fix(vscode-rollout): assert bundle sub-manifest versions in identity guardrails

Greptile round on #12321: the stable guardrail didn't assert version at
all. Went one further than the suggestion — both workflows' guardrails now
also assert each bundle sub-manifest's version (and name, for nightly)
matches the expected version, which is the check that actually regression-
guards the set-version.mjs/nightlify.mjs stamping (About tab + telemetry
extension_version read the sub-manifests, not the union). Expected version
routed through env rather than interpolated into the script body. Adds the
conventional paired test for set-version.mjs.

* fix(vscode-rollout): don't fail the nightly run when the tag push is rejected

First real combined publish (run 29454994164) published to both registries
successfully but the run went red at the last step: the default
GITHUB_TOKEN cannot create a ref whose commit modifies workflow files, and
HEAD was the #12253 squash merge which rewrote this very workflow. There
is no workflows permission grantable to the token, so this recurs any
night HEAD touched .github/workflows. The tag is bookkeeping — mark the
step continue-on-error so a successful publish isn't reported as a
failure. (Today's missing tag was pushed manually.)
2026-07-15 17:39:44 -07:00
Saoud Rizwan eb21ba583c fix(ci): restrict nightly publishing to main (#12322) 2026-07-15 15:35:09 -07:00
Saoud Rizwan f29c25395c feat(vscode-rollout): A/B loader and packaging for staged SDK extension rollout (#12253)
* feat(vscode-rollout): A/B loader and packaging for staged SDK extension rollout

Ship one marketplace VSIX containing a tiny loader plus two complete
extension bundles: next/ (SDK-based apps/vscode from main) and legacy/
(the legacy-extension branch). The loader picks one bundle per window
from a PostHog-flag-driven, sticky, one-way cohort assignment, activates
it with a Proxy-scoped ExtensionContext so each bundle resolves its
resources from its own subdirectory, and falls back to legacy (with
partial-registration cleanup and version pinning) if the next bundle
crashes during activation.

Includes the union-manifest generator with per-cohort when-clause
gating, the VSIX stitcher, a node-level loader smoke test, and the
ext-vscode-ab-package workflow that builds both refs and packages
(optionally publishes) the combined VSIX.

* fix(vscode-rollout): address rollout review feedback

* feat(vscode-rollout): versioned kill-switch, user-setting override, launch-cadence telemetry

Review follow-ups from #12253:

- Kill-switch is now scoped by version instead of boolean: the PostHog flag's
  payload carries {"maxKilledVersion": "x.y.z"} and the loader demotes only
  combined VSIXes <= that version, so killing a broken release never blocks
  the release that fixes it. Arming with no payload still demotes everything,
  and the old boolean memento format is normalized on read.

- cline.rollout.bundleOverride user setting (auto | next | legacy) as a
  manual escape hatch editable straight from settings.json: beats flags and
  the kill-switch in both directions, applies on window reload, reported as
  'override' on the activation event. Injected into the union manifest by
  gen-manifest so neither bundle has to know about it.

- parseRolloutFlags hardens flag typing: only a literal boolean true promotes
  (multivariate variants, numbers, junk fail safe), kill payloads are parsed
  defensively from /decide's JSON-string encoding.

- Activation events now carry ms_since_last_activation so the real window-
  reload cadence bounds how fast the rollout percentage gets dialed up.

- Walkthrough manifest invariant relaxed from byte-equality to structural
  equality (ids/media/completionEvents): the branches already diverge on one
  MCP step description, and since walkthrough markdown at the VSIX root comes
  from next regardless, hard-failing on copy tweaks bricked the release
  pipeline while protecting nothing. Copy divergence now warns and ships
  next's text.

* feat(vscode-rollout): identity-aware namespace, authoritative activation telemetry, nightly indicator

- Derive the setting section and sdkBundle context key from the packaged
  manifest name (cline.* for stable claude-dev, cline-nightly.* for the
  nightly identity, whose packaging rewrites the whole ID namespace);
  gen-manifest derives the same prefix for gates and the injected
  bundleOverride setting.
- Call the activated bundle's reportRolloutActivation export (merged on
  both branches) with attempted/actual/fallback — the authoritative
  extension.rollout.bundle_activated event, attributed via the bundle's
  variant-built telemetry. On crash fallback the LEGACY bundle reports it.
- Rename the loader's direct PostHog event to
  extension.rollout.loader_decision: it collided byte-for-byte with the
  bundles' event name under a different schema. It keeps the loader-side
  metadata (override, launch cadence, loader_version, extension_name) and
  gains double_failure for the both-bundles-dead case.
- Fix duplicate activation events on crash fallback: the recursive legacy
  activation no longer emits a second, contradictory fallback:false event.
- Nightly-only status bar indicator (Cline: Next / Cline: Legacy) so
  dogfooders can see which bundle a window is running.
- Union diverged engines to the newer requirement instead of hard-failing:
  main's VS Code engine (^1.101.0) has legitimately moved ahead of
  legacy-extension's (^1.84.0), which bricked every combined build.
- Smoke scenarios for all of the above.

* feat(vscode-rollout): publish the nightly as the combined A/B VSIX

Convert ext-vscode-publish-nightly.yml (cron + dispatch) from the
standalone SDK build to the combined loader + next + legacy package,
published as saoudrizwan.cline-nightly at <major>.<minor>.<unix-seconds>:

- scripts/nightlify.mjs reproduces publish-nightly.mjs's identity mutation
  (claude-dev -> cline-nightly, "cline. -> "cline-nightly., displayName,
  activity bar title) with the version as an explicit argument so ONE
  version reaches both bundle manifests and the union manifest. Runs after
  dependency install and before each bundle build.
- Both bundle builds get CLINE_ROLLOUT_VARIANT (next/legacy) in the nightly
  AND stable workflows — without it the merged rollout telemetry
  (extension_variant common prop + the authoritative bundle_activated
  capture) silently no-ops.
- dry-run dispatch input builds and uploads the installable .vsix without
  publishing or tagging; publish/tag steps are additionally gated to main,
  so the PR branch can be dispatched for pre-merge verification.
- Identity guardrails before packaging: nightly workflow asserts
  cline-nightly, the stable ab-package workflow asserts claude-dev.
- The nightly tag now records the legacy bundle sha in its message.
- README: nightly channel section (identity mapping, the two telemetry
  events and their owners, dry-run verification), and a note that the
  PostHog flags govern nightly only until the stable combined VSIX ships.

The single-bundle publish-nightly.mjs path remains for manual
feature-branch pre-release publishes; CI no longer invokes it.

* chore(vscode-rollout): harden nightly workflow gating

- Restore a job-level branch allowlist on the publish job (main + the
  rehearsal branch). Advisory defense-in-depth: the enforced gate is the
  PublishNightly environment's deployment-branch policy in repo settings,
  which must list the same branches; a dispatched branch runs its own copy
  of this file.
- Route the legacy-ref dispatch input through env instead of interpolating
  it into the run script body (script-injection hygiene; dispatch already
  requires write access).

* add otel vars to rollout build (#12316)

- Extension will not emit otel metrics to otel without these vars, so
adding those into the slow-rollout build workflow

Co-authored-by: Max Paulus 🥪 <max@cline.bot>

* fix(vscode-rollout): pass OTel env to the nightly legacy bundle build

Legacy's esbuild inlines OTEL_* at build time and its standalone publish
workflow passes them, so the combined nightly's legacy bundle was being
built with the OTel logs/metrics pipeline dead. Companion to #12316,
which fixes the same gap in ext-vscode-ab-package.yml (both bundles
there).

* feat(vscode-rollout): make the rollout two-way, remove the kill-switch

The one-way cohort + versioned kill-switch existed to avoid demoting users
whose SDK-bundle tasks aren't listed by legacy and whose rotated creds may
need a re-login. Decision: those are acceptable, temporary UX costs on an
emergency-only path — not worth a second flag and permanent mechanism
complexity (payload parsing, version scoping, killed-up-to cache format).

Now there is ONE knob: each background refresh caches exactly what
ext-sdk-bundle-rollout says for the next window. Dialing the percentage
down demotes; 0% pulls everyone back to legacy on their next reload.
Fail-safe direction preserved: only a literal boolean true promotes —
variant strings / numbers / a deleted flag all resolve to legacy; malformed
/decide responses leave the cache untouched. Local crash pinning (next
threw -> pin this version to legacy on this machine) is unchanged and
independent of the flag.

Removes KILLSWITCH_FLAG/KILLSWITCH_STATE_KEY/isVersionKilled/
normalizeKilledUpTo/compareVersions/nextCachedBundle; parseRolloutFlags
becomes parseRolloutAssignment returning the bundle to cache. Smoke
scenarios replaced with two-way promote/demote coverage.

---------

Co-authored-by: Max <maxpaulus43@gmail.com>
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-07-15 15:07:45 -07:00
Max 84c9b587a6 refactor(vscode): resolve model metadata host-side (#12130)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-07-15 13:37:02 -07:00
Saoud Rizwan 6dca234d8e chore(cli): release v3.0.41 2026-07-15 11:02:17 -07:00
Renee Huang 9217eacbbd fix: update broken ACP editor integrations redirect to CLI reference (#12312)
* fix: update broken ACP editor integrations redirect to point to CLI reference

* feat: add ACP Editor Integrations page under CLI section

- Create cli/acp-editor-integrations.mdx with ACP overview, supported editors, quick start, and usage guide
- Add page to CLI navigation group in docs.json
- Restore redirect from /cline-cli/acp-editor-integrations to /cli/acp-editor-integrations (page now exists)

* Revert "feat: add ACP Editor Integrations page under CLI section"

This reverts commit 2728b9c2ad.
2026-07-15 10:57:59 -07:00
Saoud Rizwan adbb42a99c chore(sdk): release v0.0.61 2026-07-15 10:29:47 -07:00
Saoud Rizwan 50d1578a7e feat(ui): add shared Cline theme package (#12285)
* feat(desktop): refresh navigation and visual foundation

* feat(desktop): align settings with Cline Hub

* feat(desktop): improve chat markdown rendering

* fix(desktop): resolve review feedback blockers

* fix(desktop): refine inline code sizing

* fix(desktop): preserve workspace choices during startup

* feat(ui): add shared Cline theme package

* fix(desktop): restore sidebar session sorting

* fix(desktop): harden session startup state

* fix(desktop): tighten settings navigation state

* fix(desktop): tighten markdown link handling

* fix(ui): harden theme contract validation

* test(desktop): cover late workspace restoration
2026-07-15 00:04:08 -07:00
Saoud Rizwan 5ef3b81369 feat(desktop): improve chat markdown rendering (#12276)
* feat(desktop): refresh navigation and visual foundation

* feat(desktop): align settings with Cline Hub

* feat(desktop): improve chat markdown rendering

* fix(desktop): resolve review feedback blockers

* fix(desktop): refine inline code sizing

* fix(desktop): preserve workspace choices during startup

* fix(desktop): restore sidebar session sorting

* fix(desktop): harden session startup state

* fix(desktop): tighten settings navigation state

* fix(desktop): tighten markdown link handling

* test(desktop): cover late workspace restoration
2026-07-14 23:49:22 -07:00
Saoud Rizwan ec3a57771d fix(cli): block compaction during active turns (#12296) 2026-07-14 23:23:09 -07:00
Saoud Rizwan a695dab23a feat(desktop): align settings with Cline Hub (#12275)
* feat(desktop): refresh navigation and visual foundation

* feat(desktop): align settings with Cline Hub

* test(desktop): support webview component tests

* fix(desktop): resolve review feedback blockers

* fix(desktop): preserve workspace choices during startup

* fix(desktop): restore sidebar session sorting

* fix(desktop): harden session startup state

* fix(desktop): tighten settings navigation state

* test(desktop): cover late workspace restoration

* chore: preserve upstream merge contents
2026-07-14 23:18:48 -07:00
Saoud Rizwan 77af52661c fix(telemetry): attach organization context to cached-credential identity (#12288)
* fix(telemetry): attach organization context to cached-credential identity

CLI cached credentials only stored the account id, so telemetry identity
resolved from them (headless runs via #11581, the hub daemon via #12177)
carried user_id but no organization_id - making CLI/hub usage invisible
to organization-scoped dashboards even where per-user attribution works.

- AuthSettingsSchema gains optional organizationId/organizationName/
  memberId
- loadClineAccountSnapshot persists the active organization into the
  cached cline provider settings after fetching /me (cleared when the
  user is on their personal account), so the context survives across
  processes without a network call
- the CLI runtime identify and the hub daemon identity refresh read the
  persisted fields and pass them to identifyAccount; the daemon re-keys
  its refresh on account+organization so an org switch re-identifies a
  long-lived daemon

* fix(telemetry): strip stray NUL byte, drop needless reshaping of daemon identity resolve
2026-07-14 23:10:27 -07:00
Saoud Rizwan 0f4acccd08 feat(desktop): refresh navigation and visual foundation (#12268)
* feat(desktop): refresh navigation and visual foundation

* test(desktop): support webview component tests

* fix(desktop): resolve review feedback blockers

* fix(desktop): preserve workspace choices during startup

* fix(desktop): restore sidebar session sorting

* fix(desktop): harden session startup state

* test(desktop): cover late workspace restoration
2026-07-14 23:02:06 -07:00
Bee f8c73cd8cc feat(core): persist and refresh workspace git info (#12295) 2026-07-15 07:30:16 +02:00
Max 55a31a0d8a feat(vscode): add rollout telemetry to SDK extension (#12292)
* feat(vscode): add shared rollout telemetry contract

* feat(vscode-sdk): propagate rollout metadata

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-14 22:29:52 -07:00
Bee 04438c0d54 feat: shows compaction progress status in UI (#12137)
* feat: shows compaction progress status in UI

* fixes p2

* fix: complete compaction lifecycle delivery

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-15 13:09:10 +08:00
Bee f053ec48e4 refactor(llms): owns provider-specific header policy (#12187)
* refactor(llms): owns provider-specific header policy

* header
2026-07-15 03:23:23 +02:00
Bee 0df406723c refactor(core): normalize read file request path aliases (#12287)
* fix(core): normalize read file request path aliases

Accept `file_path` and `filePath` in read file requests and normalize them to the canonical `path` field. Apply alias handling to direct, array, and nested inputs to prevent model-generated variants from failing validation.

Clarify path descriptions by removing redundant wording.

* update test
2026-07-15 08:46:00 +08:00
Dominic Cooney 12703bf407 Improve VS Code terminal reliability: OSC 633 parser, exit codes, timeout handling (#11972) 2026-07-14 16:34:36 -07:00
Bee 4a97b46f5f refactor(core): simplifies context compaction trigger (#12217)
* refactor(core): simplifies context compaction trigger

Simplifies automatic context compaction so it always triggers when input usage reaches 80% of the model’s effective maximum input-token limit.

* add bound

* feedback apply

* complete

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-15 02:11:38 +08:00
Saoud Rizwan 36fc3327ac fix(cli): highlight API key fallback hint (#12283) 2026-07-14 10:51:05 -07:00
Max 2b48dc411f make old tasks incompatible with new cline extension (#12127)
Preserve pretty legacy task display after resume

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-14 10:00:23 -07:00
Tomás Barreiro da6fe718d0 Rename sessionStartedAt to sessionStartedAtMs (#12279) 2026-07-14 16:08:17 +02:00
Tomás Barreiro 3515333e23 Review against camelCase telemetry (#12280) 2026-07-14 15:59:19 +02:00
Saoud Rizwan bd9ac5872b ci(sdk): create GitHub release and post to Slack on latest SDK publish (#12223)
* ci(sdk): create GitHub release and post to Slack on latest SDK publish

* ci(sdk): use random heredoc delimiter for changelog output
2026-07-14 01:31:45 -07:00
Saoud Rizwan fb15324ad2 fix(cli): prevent use-after-free when setting terminal title during TUI teardown (#12229)
* fix(cli): prevent use-after-free when setting terminal title during TUI teardown

* fix(cli): re-check renderer destruction before title reset in teardown microtask

* test(cli): cover terminal title teardown lifecycle
2026-07-14 01:31:01 -07:00
Saoud Rizwan 7a27c04ffa fix(core): stop reporting benign git states as workspace init errors (#12189)
* fix(core): stop reporting benign git states as workspace init errors [ENG-2244]

A freshly initialized repo with no commits makes 'git rev-parse HEAD'
fail, which generateWorkspaceInfoWithDiagnostics recorded as a workspace
init error and surfaced as workspace.init_error telemetry on every
session bootstrap. Filter out git failures that reflect normal
repository states; genuine failures (missing directory, real git
breakage) are still reported.

* fix(core): drop 'bad revision' from benign git error filter

Review feedback: 'fatal: bad revision HEAD' can also indicate a corrupt
.git/HEAD (checkIsRepo still succeeds), which is a genuinely broken
workspace that should keep reporting. The remaining patterns cover the
empty-repo message variants.
2026-07-14 01:30:33 -07:00
Saoud Rizwan c37b252f65 fix(vscode): restore multi-root mention resolution and validate stored task cwd (#12190)
* fix(vscode): restore multi-root mention resolution and validate stored task cwd [ENG-2245][ENG-2244]

The SDK adapter's ensureWorkspaceManager() was a stub returning
undefined, which silently disabled multi-root file mention resolution:
parseMentions only searched the primary cwd, so @-mentions of files in
secondary workspace roots failed with not_found. Build a real
WorkspaceRootManager from the host's workspace folders (cached until
the folder set changes) via a new WorkspaceRootManager.fromPaths().

Also validate that a resumed task's stored cwdOnTaskInitialization
still exists before using it — stale paths (deleted/moved dirs) fed
git-based workspace init and produced init-error telemetry.

* fix(vscode): use JSON.stringify for workspace manager cache key

Review feedback: a delimiter-joined key is ambiguous for paths
containing the delimiter (and the previous separator was an embedded
NUL byte). JSON.stringify is unambiguous and order-preserving.

* test(vscode): cover stored task cwd validation
2026-07-14 01:30:21 -07:00
Saoud Rizwan 2872138900 feat: suggest model IDs from OpenAI-compatible endpoints in extension and CLI (#12231)
* fix(vscode): use the requested provider's stored credentials when listing OpenAI-compatible models

The OpenAI-compatible settings pane already fetches GET <baseUrl>/models to
suggest model IDs, but the host handler always read the built-in "openai"
provider's stored settings. Custom OpenAI-compatible providers only expose a
masked API key to the webview, so their model-list requests went out
unauthenticated and the suggestion dropdown stayed empty.

Add provider_id to OpenAiModelsRequest and read that provider's stored API
key and custom headers in refreshOpenAiModels. Old clients omit the field,
which defaults to "openai" and preserves the previous behavior.

* feat(cli): suggest model ids from OpenAI-compatible endpoints in the model picker

The CLI showed a bare free-text input for openai-compatible providers and
never asked the endpoint what it serves. Fetch GET <baseUrl>/models with the
provider's stored API key/headers when opening the picker; when the endpoint
answers, show the standard fuzzy list (which keeps the "Create custom model
ID" row for manual entry). Any failure or empty answer falls back to the
existing free-text input.

* fix: resolve OpenAI-compatible model discovery config
2026-07-14 01:26:48 -07:00
Saoud Rizwan dc4620c529 fix(vscode): add to system prompt about plan/act modes and nudge about mode switches (#12227)
* feat(shared): move plan/act mode prompt instructions into the shared prompt builder

The CLI's #12057 fixes (mode-tag explanation, plan-mode contract,
mode-switch notice tracker) were CLI-only wiring, so the VSCode extension
never told the model what the <user_input mode> attribute means and plan
mode kept mutating files (CLINE-2576, CLINE-2607, CLINE-2579). Promote
the pieces every host needs into @cline/shared:

- buildClineSystemPrompt now appends MODE_TAG_INSTRUCTIONS for every mode
  and PLAN_MODE_INSTRUCTIONS for plan sessions, composed into the rules
  slot in the exact order the CLI historically built by hand, so CLI
  output is byte-identical after the refactor.
- The plan-mode contract gains an explicit run_commands paragraph:
  the tool intentionally stays available in plan mode (essential for
  read-only investigation) but is inspection-only there -- no file
  mutations, no state-changing commands. The mitigation for plan-mode
  mutations is prompting plus mode-switch notices, not tool removal.
- createModeSwitchNoticeTracker moves from apps/cli/runtime/interactive
  to @cline/shared next to formatModeSwitchNotice; the CLI re-exports it
  so its import surface and tests stay unchanged.
- deriveTitleFromPrompt gets a regression test pinning that titles never
  pick up mode-notice text.

* fix(vscode): teach the model about plan/act modes and surface mode switches

Port the CLI's #12057/#12058 plan-mode fixes to the extension:

- The session factory drops its local PLAN_MODE_INSTRUCTIONS copy; the
  shared prompt builder now emits both the mode-tag explanation and the
  plan-mode contract (including the read-only run_commands rule), so the
  extension's system prompt finally explains the <user_input mode>
  wrapper its own messages have carried all along.
- Manual Plan/Act toggles record a mode-switch notice in
  SdkModeCoordinator (shared round-trip-cancelling tracker, scoped to
  the rebuilt session so it never leaks across tasks), recorded only
  after the session replacement actually commits. The model-initiated
  switch_to_act_mode path passes source: "tool" and records nothing,
  matching the CLI: its tool result and continuation prompt already
  announce the switch.
- SdkSessionLifecycle.fireAndForgetSend -- the single funnel for
  outbound turn sends -- consumes the notice and prepends
  formatModeSwitchNotice() to the next message, exactly like the CLI's
  run-interactive stamping.
- Display boundaries never render the raw tag: the queued-prompt echo
  in the message translator now goes through formatDisplayUserInput,
  and isSyntheticUserPrompt strips notices before matching so a stamped
  continuation prompt cannot shift edit/regenerate ordinals.
2026-07-14 01:26:20 -07:00
Saoud Rizwan 2ac5c85e69 fix(vscode): restore editor diff view for SDK edit tools (#12219)
* feat(sdk): expose edit-executor internals for host diff previews

Extract computePatchChanges() from createApplyPatchExecutor so hosts can
compute a patch's per-file proposed content without writing to disk
(behavior-identical refactor; the executor now calls the helper), and
widen the @cline/core root exports with createEditorExecutor,
createApplyPatchExecutor, computePatchChanges, PatchActionType and the
related types. Needed by the VS Code adapter to restore the editor diff
view for SDK edit tools.

* fix(vscode): restore editor diff view for SDK edit tools

Adds SdkDiffEditCoordinator, which owns per-toolCallId diff sessions over
the legacy DiffViewProvider abstraction (HostProvider factory, so the
external/JetBrains gRPC DiffService path keeps working):

- the diff editor opens populated before the approval ask renders (the
  SDK surfaces tool input only after the model stream completes, so the
  approval callback is the only pre-execution point with full input)
- an overridden editor executor saves through the diff document:
  user edits in the editable right pane and post-save auto-formatting
  flow back to the model via formatResponse.fileEditWithUserChanges,
  plus 'new problems' diagnostics
- Reject/abort reverts (new files: file + created dirs removed)
- auto-approved edits open the diff during execution with the legacy
  3.5s diagnostics settle; Background Edit keeps the headless disk path
- apply_patch gets a preview-only diff of its first changed file; on
  approve the preview is reverted and the untouched SDK executor applies
  the whole patch
- any diff-pipeline failure reverts and falls back to the SDK disk
  executor, preserving canonical error strings

Fixes #11934 (CLINE-2580).

* refactor(vscode): make edit diff preview a read-only virtual-document diff

Reworks the diff view restoration after EDH testing showed the editable
real-document design breaking on same-file multi-edits (tab reuse opened
the actual file instead of a diff; sibling saves closed other sessions'
tabs; right-pane edits misbehaved).

New design per review:
- EditPreview abstraction (mirrors CommentReviewController pattern):
  VscodeEditPreview renders vscode.diff with BOTH sides as virtual
  cline-diff documents (unique fragment per preview, so same-file edits
  get distinct tabs and close is an exact tab match, never the real
  file); ExternalEditPreview uses the existing openMultiFileDiff/
  closeAllDiffs host-bridge RPCs. New createEditPreview factory on
  HostProvider.
- The preview never touches disk: executors close the preview and
  delegate to the SDK's default disk executors, whose results and error
  strings reach the model unchanged. Reject/abort just closes a tab.
- Dropped by design decision: editing in the diff view, user-edit
  feedback to the model, and diagnostics passback (the SDK already
  prompts the model to check).
- Auto-approved edits show a brief preview that lingers ~1.5s after the
  write; an abort cuts the linger short without failing the applied edit.
- A newer same-file preview supersedes an older pending one (approvals
  resolve sequentially), eliminating cross-session interference.
- Legacy DiffViewProvider stack returns to untouched dead code.

* fix(vscode): state that denied edits did not modify the file

Repro: ask Cline to edit a file, then answer the approval with feedback
instead of Approve/Reject. The denial reached the model as just
{"error":"make them bigger"} — nothing said the edit was NOT applied —
so the model treated the feedback as iteration on an applied change and
built its next old_text against content that never landed on disk. From
then on old_text no longer matched the real file and the diff preview
silently stopped appearing (and the eventual executor run would fail the
same way).

Denial reasons now come from buildToolApprovalDenialReason(): edit tools
get 'The user denied this edit. The file was NOT modified and still
contains its original content.' (legacy parity), and all tools get user
feedback wrapped in <feedback> tags instead of the bare prompt as the
whole reason. isKnownToolApprovalDenial also matches the new edit-denial
marker so translator suppression keeps working.

* feat(vscode): simulated streaming animation for edit previews

Brings back the legacy 'yellow sweep' feel on the virtual diff preview.
The SDK only surfaces complete tool input, so this is a deliberate
simulation of the legacy streaming look (which legacy also showed when
it already had the full content in memory).

The sweep covers the whole file like legacy did, with diff-aware pacing:

- Park at the top: whole document under the faded-yellow overlay, cursor
  highlight on line 0, viewport pinned to the top, ~400ms hold so the
  animation unambiguously starts from the top.
- Zip through unchanged spans in small fast steps (~8 lines per 16ms
  frame, capped per span) so they read as continuous motion.
- Slow down through each change: one line per 45ms frame with a ~350ms
  minimum dwell per hunk so even a one-line change visibly pauses.
- Changed runs come from a real line diff (diffLines), so multi-hunk
  edits slow at EACH hunk and the gaps between hunks zip; pure deletions
  pause at the deletion point.
- Zip frames chase the cursor (InCenter) for continuous scroll; typing
  frames scroll only when leaving the viewport (no per-frame judder).
- After the sweep reaches the bottom: short beat, then settle centered
  on the first changed line for review.

Mechanics: edit previews move from base64-query cline-diff URIs to a new
mutable cline-edit-preview content provider (content set programmatically,
re-rendered via onDidChange) so the virtual right side can update in
place. DecorationController is reused as-is. The approval ask renders
while the animation plays (legacy simultaneity); close() cancels
mid-animation; files >3000 lines render the final diff immediately.
External hosts keep the static openMultiFileDiff preview.

* chore(vscode): remove test artifact comment from memory-monitor

* fix(vscode): address review nits — skip diff computation for large files, close partially-opened previews

- buildEditPreviewAnimation (which runs a full line diff) now runs after
  the MAX_ANIMATED_LINES guard; oversized files use a cheap prefix scan
  just to aim the viewport.
- If preview.open() throws after partially opening, the tab is closed
  directly — the session was never registered, so discardPreview could
  not have reached it.

* fix(vscode): keep tsconfig valid JSON for test setup

* fix(vscode): bound diff preview animation
2026-07-14 01:25:36 -07:00
Tomás Barreiro ab68fd7f34 Store startedAt in auth metadata when starting a Cline session (#12270)
* Store startedAt in auth metadata when starting a Cline session

* Inject the sessionStartedAt when creating the auth credentials

* Remove injecting sessionStartedAt when it's not stored already

* Address review

* fix merge inconsistencies
2026-07-14 03:50:00 +02:00
Saoud Rizwan b4ed8a226e chore(cli): release v3.0.40 2026-07-13 12:25:49 -07:00
Saoud Rizwan cbf40961db fix(hub): make markdown code component assignable to streamdown Components
The custom MarkdownCode node type used a narrow { metastring?: string }
shape that is not assignable from the hast Element passed by
react-markdown/streamdown, so a clean rebuild (fresh dependency resolve,
as done by the release version.ts) fails the `satisfies Components`
check. Widen node.properties to Record<string, unknown> and validate the
metastring value at read time.
2026-07-13 12:02:49 -07:00
Saoud Rizwan 2d05ba52da chore(sdk): release v0.0.60 2026-07-13 11:25:21 -07:00
Saoud Rizwan 5d3778b5cf feat(cli): manual API key escape hatch for Cline OAuth providers (#12254)
* feat(cli): manual API key escape hatch for Cline OAuth providers

Add a way to configure the cline / cline-pass providers with a dashboard
API key from the /settings provider flow, for users where OAuth login
isn't working:

- "Enter API key manually" option in the already-configured dialog
- K keybinding in the OAuth login dialog to switch to key entry
- Saving clears stored OAuth tokens (on both the shared cline storage
  entry and any direct cline-pass entry) since the auth handler prefers
  auth.accessToken over apiKey — a stale token would otherwise keep
  winning over the manual key
- isProviderConfigured now counts a persisted API key for OAuth
  providers so escape-hatch users aren't forced back into OAuth on
  every provider switch

* fix(cli): move API key fallback to OAuth dialog
2026-07-13 11:14:11 -07:00
Saoud Rizwan 8d0eb54a1f feat(telemetry): track auth refresh outcomes to measure the hard-logout fix (#12256)
* fix(sdk): stop misclassifying transient refresh failures as invalid_grant

getValidClineCredentials returned null for BOTH a rejected refresh token and
any transient error (network down, timeout, 5xx) that happened to land after
the access token expired. Callers treat null as 'session dead' — the
extension wipes providers.json over it, logging out every Cline process on
the machine, which is what CLI users then hit as 'Unauthorized: please
re-authenticate'. A laptop waking from sleep past the ~1h token expiry with
a background job (balance/banners/remote-config) refreshing before the
network is up was enough to trigger it — no refresh-token rotation involved.

Now: null means the refresh token was REJECTED (re-auth required); transient
failures throw so callers keep stored credentials and retry later. The
extension's refreshAccessToken catch and the CLI's error surface already
handle the throw correctly with no changes.

* fix(sdk): write providers.json atomically

providers.json was written with a bare writeFileSync while being read
concurrently by every other Cline process (CLI, extension, hub). A reader
catching a partial write parses garbage, which read() silently treats as
EMPTY settings — indistinguishable from being logged out — and any
subsequent save from that process persists the empty state, erasing every
configured provider.

Stage to a pid-unique temp file and rename into place; rename is atomic on
POSIX and replaces on Windows, so readers only ever see a complete file.

* feat(telemetry): track auth refresh outcomes to measure the hard-logout fix

Adds the observability needed to verify in production that the
transient-vs-invalid_grant fix is working, and to diagnose any logouts that
remain:

- user.auth_refresh_soft_failure — fires when a refresh fails for a reason
  that does NOT invalidate the session (network error, timeout, 5xx) and
  stored credentials were kept. Instances with tokenExpired=true were hard
  logouts before the fix, so this is the 'prevented logout' counter. Emitted
  from the SDK (CLI path) and from the extension's refresh/restore catches
  under the same event name so dashboards aggregate both clients.
- user.auth_logged_out now carries the HTTP status and errorCode that caused
  it, and the extension emits it (with a distinct reason) at every site that
  clears providers.json: refresh_rejected, restore_refresh_rejected, and
  handleDeauth's LogoutReason (user_initiated / cross_window_sync / …), which
  was previously accepted and ignored. Extension-triggered logouts were
  completely invisible before — including the legacy-extension cross-window
  cascade, which this now measures directly.

Success looks like: auth_logged_out volume drops after release while
auth_refresh_soft_failure appears in its place, and any remaining logouts
carry a reason/status we can act on.

* fix(telemetry): route auth refresh events through SDK
2026-07-13 11:13:05 -07:00
Saoud Rizwan a3989acc38 fix(sdk): don't log users out when token refresh fails due to network/server errors (#12255)
* fix(sdk): stop misclassifying transient refresh failures as invalid_grant

getValidClineCredentials returned null for BOTH a rejected refresh token and
any transient error (network down, timeout, 5xx) that happened to land after
the access token expired. Callers treat null as 'session dead' — the
extension wipes providers.json over it, logging out every Cline process on
the machine, which is what CLI users then hit as 'Unauthorized: please
re-authenticate'. A laptop waking from sleep past the ~1h token expiry with
a background job (balance/banners/remote-config) refreshing before the
network is up was enough to trigger it — no refresh-token rotation involved.

Now: null means the refresh token was REJECTED (re-auth required); transient
failures throw so callers keep stored credentials and retry later. The
extension's refreshAccessToken catch and the CLI's error surface already
handle the throw correctly with no changes.

* fix(sdk): write providers.json atomically

providers.json was written with a bare writeFileSync while being read
concurrently by every other Cline process (CLI, extension, hub). A reader
catching a partial write parses garbage, which read() silently treats as
EMPTY settings — indistinguishable from being logged out — and any
subsequent save from that process persists the empty state, erasing every
configured provider.

Stage to a pid-unique temp file and rename into place; rename is atomic on
POSIX and replaces on Windows, so readers only ever see a complete file.
2026-07-13 08:58:26 -07:00
Saoud Rizwan d199b1bff9 fix(desktop-app): allow loopback origins for Next dev resources (#12251)
Next 16 blocks dev-resource requests (/_next/webpack-hmr, dev fonts) from
origins that don't match the dev server's own hostname. Browsing the web
dev mode via 127.0.0.1 left the page hanging with 'Blocked cross-origin
request to Next.js dev resource' warnings. allowedDevOrigins is dev-only,
so production/Tauri builds are unaffected.
2026-07-12 22:18:45 -07:00
Saoud Rizwan d41eed1198 feat(desktop-app): env-configurable sidecar bind host, trusted origins, and webview WS endpoint (#12250)
* feat(desktop-app): env-configurable sidecar bind host, trusted origins, and webview WS endpoint

Allows running the desktop app's web dev mode (dev:web + dev:sidecar) inside
a Docker container with published ports:

- CLINE_SIDECAR_HOST: sidecar bind hostname (default remains 127.0.0.1)
- CLINE_SIDECAR_TRUSTED_ORIGINS: comma-separated extra browser origins for
  the sidecar's origin allowlist (validation itself stays on)
- NEXT_PUBLIC_SIDECAR_WS_ENDPOINT: overrides the webview's hardcoded
  ws://127.0.0.1:3126/transport fallback so a browser on the Docker host can
  dial the published port

All defaults are unchanged, so local/Tauri behavior is unaffected when the
env vars are absent. When bound to 0.0.0.0 the printed ready endpoint
advertises 127.0.0.1 since a wildcard bind is not dialable.

* chore(desktop-app): untrack next-env.d.ts

It was added to .gitignore previously but never removed from the index, so
it kept showing as modified: Next.js rewrites the routes.d.ts import path
depending on whether 'next dev' or 'next build' ran last. The file is
regenerated by Next on every dev/build run, and the app's typecheck
(tsconfig.dev.json) excludes webview/, so nothing needs it tracked.

* style(desktop-app): format SIDECAR_HOST declaration
2026-07-12 21:59:07 -07:00
Tomás Barreiro 6309971089 Add the ClinePass limit error to the CLI (#12191)
* Add the ClinePass limit error to the CLI

* Update apps/cli/src/runtime/run-agent.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* format code and improve instructions

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-07-11 02:41:55 +02:00
Saoud Rizwan 2d2c669421 fix(cli): reload provider config when switching models (#12232) 2026-07-10 16:57:50 -07:00
Dominic Cooney c5f146a418 Add debug logging for Cline credential lifecycle (ENG-2213) (#12000)
* fix(auth): add early SDK debug logging for Cline credential lifecycle (ENG-2213)

Adds targeted debug-level logging at key points in the Cline/Cline Pass
credential lifecycle to diagnose intermittent logout issues. Credentials
are never logged in cleartext; an 8-hex-digit SHA-256 hash is used instead.

The SDK has two logger layers:
1. ClineCore.logger — session-scoped, threaded from ClineCore.create({logger})
   into session config and the agent event bridge.
2. setSdkLogger() — early/module-level, for components that operate before
   or outside of ClineCore sessions: ProviderSettingsManager (constructed
   at startup), RuntimeOAuthTokenManager, and cline.ts auth functions
   (token refresh). These can't be reached by the session-scoped logger.

Both VS Code (common.ts) and CLI (main.ts) call setSdkLogger() once at
startup. When no logger is registered (or the host filters out debug),
every call is a no-op — logging is never collected in normal use.

Instrumentation points (SDK core, shared by both surfaces):
- ProviderSettingsManager.read(): logs provider IDs, last-used, and whether
  Cline auth is present (with hashed access/refresh token fingerprints)
- ProviderSettingsManager.saveProviderSettings(): logs the provider being
  saved, tokenSource, whether Cline auth was present before/after, and
  flags authDropped when a previously-present Cline auth block disappears
- RuntimeOAuthTokenManager.resolveProviderApiKeyInternal(): logs each
  decision point (no_settings, no_credentials, refresh_start, refresh_null,
  refreshed+saved, not_refreshed) with hashed token fingerprints
- cline.ts refreshClineToken(): logs the refresh request URL, response
  status/errorCode on failure, and new token hashes on success
- cline.ts getValidClineCredentials(): logs the outcome at each branch
  (no_current_credentials, still_valid, needs_refresh, invalid_grant,
  transient_failure_kept_current, transient_failure_expired)

VS Code extension (auth-service.ts):
- readClineCredentials/writeClineCredentials/clearClineCredentials: logs
  credential presence and hashes at each disk I/O point
- refreshAccessToken: logs refresh start, null result (cleared), changed
  (written), or unchanged outcomes
- fetchUserInfoFromApi: logs the GET /api/v1/users/me request and response
  status

What to collect when investigating:

VS Code extension:
- Open the "Cline" output channel (View -> Output -> select "Cline")
- Look for lines containing: [SdkAuthService], providers.read,
  providers.save, oauth.resolve, cline.refresh, cline.getCredentials
- Debug logging is emitted at the DEBUG level; it appears in the output
  channel when IS_DEV=true or in development builds

CLI:
- Set CLINE_LOG_LEVEL=debug environment variable before running cline
- Collect the log file at ~/.cline/data/logs/cline.cli.log (or the path
  set by CLINE_LOG_PATH)
- Look for the same event names as above

Files changed:
- sdk/packages/core/src/auth/auth-debug.ts (NEW): hashSecret,
  setSdkLogger, getSdkLogger, sdkDebug
- sdk/packages/core/src/auth/cline.ts: refresh/getCredentials logging
- sdk/packages/core/src/services/storage/provider-settings-manager.ts:
  read/save logging
- sdk/packages/core/src/runtime/orchestration/runtime-oauth-token-manager.ts:
  resolve logging
- sdk/packages/core/src/index.ts: export early logger utilities
- apps/vscode/src/sdk/auth-service.ts: credential lifecycle logging
- apps/vscode/src/common.ts: register SDK early logger
- apps/cli/src/main.ts: register SDK early logger

* fix(vscode): inline SDK debug metadata into log message string (ENG-2213)

* fix(auth): gate debug logging on CLINE_LOG_LEVEL at runtime (ENG-2213)

* fix(auth): use interpolated debug strings, remove log-level gating (ENG-2213)

* refactor: move early logger to sdk/packages/core/src/logging/early-logger.ts

* fix: address review feedback — early logger registration, log after write, remove getSdkLogger from public API

* fix(vscode): add ISO timestamps to all log lines

* fix core import

* fix import

* fix tests

---------

Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>
Co-authored-by: BarreiroT <tomasmbarreiroi@gmail.com>
2026-07-10 23:40:46 +02:00
Saoud Rizwan 261ee4c313 fix(vscode): show requested line range on read-file chat rows (#12225)
The webview already knew how to render readLineStart/readLineEnd on
readFile tool rows, but the SDK message translator never populated
them, so successive ranged reads of the same file all rendered as
identical bare paths. Extract start_line/end_line from read_files
input (per-file and single-path forms) and render open-ended reads
(start_line only) as "start+".
2026-07-10 13:25:30 -07:00
Saoud Rizwan d45b051c04 fix(cli): detect bun global installs after symlink resolution in auto-update (#12224) 2026-07-10 12:15:26 -07:00
Bee 78c83cdf33 fix(cli): preserve session id when in same session (#12188) 2026-07-10 17:59:55 +08:00
Bee 3266121fa1 feat(desktop): add typography spec (#12215)
* feat(desktop): add typography spec

* remove unused background component
2026-07-09 19:34:28 -07:00
Bee 6467de65a2 fix(plugin): follow up fix for agent-squad (#12216)
Follow up on my last PR where the last commit revert the removal of the regex field from zod schema
2026-07-09 19:33:04 -07:00
Bee 65fe885638 fix(sdk): remove regex from zod schema for agent-squad plugin example (#12214)
* fix(sdk): remove regex from zod schema for agent-squad plugin example

The `HandoffPathInput` schema used negative lookaheads to reject absolute paths and `..` traversal segments. When converted to JSON Schema, this regex caused consumers without lookaround support to fail with `invalid JSON schema: regex lookaround is not supported`.

This change removes the lookaround-based regex from the published schema and moves those checks to runtime validation. It preserves validation for allowed characters, absolute paths, traversal segments, and maximum length while strengthening cross-platform directory containment checks using Node’s path utilities.

* add back logger examples
2026-07-10 10:19:27 +08:00
Bee c3033d6f13 fix(vscode): refreshGroqModels caused cacheReadsPrice undefined error (#12213) 2026-07-10 08:52:06 +08:00
Max cfb1327a1b fix vscode hmr not working (#12212) 2026-07-09 16:49:35 -07:00
Alex Taboada 264af96e1b fix(vscode): prevent infinite loading when initializing task with an image (#12203) 2026-07-09 18:16:52 +02:00
Robin Newhouse 10cb9bd97a Add compaction budget hardening (#12142)
* Add compaction budget projection contract

* Tighten budget projection contract types

* Tighten dropped block action paths

* Add pure compaction budget projection engine

* Fix budget projection truncation accounting

* Drop provider-native blocks during budget projection

* Recompute protected tail after thinking pruning

* Align budget projection test tool results

* Clean up budget projection fixture indentation

* fix(core): narrow compaction protected tail

* Fix budget projection action accounting

* Budget agentic compaction summary input

* Harden agentic summary budget fallback

* Align agentic compaction test tool result

* Align agentic file ops with projected input

* Budget basic compaction projections

* Clarify basic projection budget logging

* Align basic sanitization image expectation

* Align basic compaction budget expectation

* Emit compaction budget emergency telemetry

* Tighten compaction budget telemetry types

* Preserve compaction status notice reasons

* fix(core): account compaction tokens consistently

* fix(core): align skipped compaction token accounting
2026-07-09 02:15:55 -07:00
Saoud Rizwan 2ee18e7f0c chore(cli): release v3.0.39 2026-07-08 21:15:22 -07:00
Saoud Rizwan 0b65506a2b chore(sdk): release v0.0.59 2026-07-08 20:00:37 -07:00
Saoud Rizwan 3502608081 fix(telemetry): emit telemetry from the detached hub daemon process (#12177)
* feat(sdk): emit telemetry from the hub daemon process

The detached hub daemon hosts the LocalRuntimeHost that emits
task.conversation_turn and task.tokens for every hub-backed session
(CLI in prefer-hub mode, desktop app, connectors), but the daemon
entrypoint never created a telemetry handle - startHubWebSocketServer
received telemetry: undefined and every capture in the daemon-side
runtime was a no-op. Sessions billed normally on the backend while
reporting nothing to OTel.

- create a ConfiguredTelemetryHandle in the daemon entry and pass it to
  the websocket server and schedule runtime handlers
- identify from the cached cline account at startup and re-resolve
  periodically, since the long-lived daemon often starts before login
  or outlives an account switch
- flush and dispose the handle on graceful and fatal shutdown

* fix(sdk): flush daemon telemetry when server startup fails

If startHubWebSocketServer throws, dispose the telemetry handle before
rethrowing so failed daemon starts are visible in telemetry instead of
dying silently.

* fix(sdk): bound daemon telemetry flush and reuse settings manager

- Race dispose's flush against a 5s deadline so a hung exporter can't
  keep a crashed daemon alive holding the hub port (before this PR the
  daemon exited immediately on fatal errors; the flush must not change
  that materially).
- Construct ProviderSettingsManager once instead of every identity
  refresh; its constructor runs legacy-migration and provider
  registration side effects, and getProviderSettings re-reads the file
  per call anyway.
- Test the dispose-on-startup-failure path and the cline-hub-daemon
  platform metadata.

* fix(sdk): label daemon telemetry cline_type as hub

Review feedback from @abeatrix: daemon-hosted sessions can be triggered
by the CLI, desktop app, or connectors, so daemon-emitted events should
not share the CLI process's cline_type. Existing values are "cli" and
"VSCode Extension"; the daemon now reports "hub" (with the finer
platform=cline-hub-daemon kept as-is).
2026-07-08 19:35:26 -07:00
Saoud Rizwan ed3107f9ec Revert "docs: add Cline free models page (#12183)" (#12185)
This reverts commit 6bce48aad4.
2026-07-08 19:32:36 -07:00
Renee Huang 6bce48aad4 docs: add Cline free models page (#12183)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-08 19:22:32 -07:00
Bee 1e1b6af51c fix(sdk): set versioned Cline client-identity headers for Cline provider (#12182)
* fix(sdk): set versioned Cline client-identity headers for Cline provider

* address feedback

* feat: add platform metadata to client context

Include platform, platformVersion, and isMultiRoot in extension client
context for CLI, ACP, and VS Code sessions. This provides downstream
core/session logic with richer runtime information and distinguishes ACP
clients from the standard CLI client.

* lint

* clean up

* fix: resolve client host identity via HostProvider for standalone compatibility

cline-session-factory.ts is also bundled into the standalone cline-core
(JetBrains), where the 'vscode' module resolves to the generated Proxy-stub
module: vscode.env.appName and vscode.version return Proxy objects, which
would flow into X-PLATFORM/X-PLATFORM-VERSION header values and fail at
request serialization.

Resolve the identity through HostProvider.env.getHostVersion() instead —
the VS Code hostbridge returns the identical values (vscode.env.appName,
vscode.version, ClineClient.VSCode, extension version), and JetBrains'
hostbridge returns its real host values, so the standalone stops reporting
itself as the VS Code extension as a bonus. Multi-root detection goes
through HostProvider.workspace.getWorkspacePaths() for the same reason.
Both resolvers degrade gracefully (undefined/false) if the host bridge is
unavailable, in which case the header builder falls back to source-derived
values.

* Add unit test as proof

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-08 19:08:25 -07:00
Saoud Rizwan ee49900232 chore(greptile): update telemetry review rules for the monorepo layout (#12180)
The .greptile config was written for the pre-merge standalone cline/sdk
repo and never updated after the monorepo merge:

- the sdk-telemetry-doc-update rule enforced an Event Catalog in DOC.md,
  a file that does not exist in this repo (it now emits a false P2 on
  every PR touching core-events.ts, e.g. #12177)
- rules.md cited PR #357, apps/vscode/src/hub-daemon.ts, and
  apps/vscode/src/telemetry.ts - none of which exist here
- the 'Hub Daemon Metadata Forwarding' section described an argv-based
  metadata payload that was never implemented in this repo; replaced
  with the actual daemon-owned telemetry pattern from #12177
- the opted-out-test rule now describes the real convention: assert the
  event flows through capture (no-op for OptedOutTelemetryService), not
  captureRequired
2026-07-08 16:43:14 -07:00
Saoud Rizwan a1d5589d19 feat: allow selecting Cline free models on the ClinePass provider (#12140)
* feat(llms): include Cline free models in the cline-pass catalog

* feat(vscode): show Subscribed/Free model tabs on the ClinePass provider

* feat(cli): show Subscribed/Free sections in the ClinePass model picker

* fix(cli): drop redundant browse-all entry from ClinePass picker

* fix(cli): show only subscribed models in ClinePass onboarding picker

* feat(cli): include free models and quota explainer in ClinePass onboarding picker

* fix: shorten ClinePass free section copy

* fix(cli): strip redundant free markers from sectioned picker names

* fix: drop free from ClinePass free section copy

* fix: tighten ClinePass free section copy

* refactor: address review feedback on ClinePass free models

- single buildFeaturedModelEntries(providerId) dispatcher, builders private
- rename isClineProvider to isClineManagedProvider (includes cline-pass)
- use isClineManagedProvider in the free-model cost check
- themed tab border, pretty names on free model cards
- clearer cline-pass cost test name

* fix: address ClinePass free-model review blockers

- Stop re-sorting the cline-pass live catalog by release date in
  mergeKnownModels: free models carry OpenRouter release dates, so the
  sort could put a free model first and make it the fallback default
  when the bundled default id rotates out of the live clinePass bucket.
  Preserve the normalize-time order (pass models first) and pin it with
  an end-to-end resolveProviderConfig test.
- Add the browse-all escape to the CLI ClinePass picker when the
  clinePass bucket is empty (bundled fallback after a fetch failure),
  so a subscriber isn't left with a free-models-only picker.
- Rename ErrorRow's local isClineManagedProvider to
  isClineUsageBillingProvider: it only matches the cline provider,
  unlike the shared util of the same name that also matches cline-pass.
2026-07-08 15:08:27 -07:00
Tomás Barreiro 721fda2e99 Add ClinePass limit error (#12162)
* Add ClinePass limit error

* refactor regex

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-08 14:54:20 -07:00
Saoud Rizwan 5e78861eb5 fix(vscode): update ClinePass onboarding option copy (#12173) 2026-07-08 13:21:12 -07:00
Robin Newhouse 29798f59f3 Persist VS Code manual compaction sidecar (#11900)
* Persist VS Code manual compaction sidecar

* Fix compaction test isolation

* Address PR feedback on compaction comments

* Fix compaction test core mock hoisting

* fix(vscode): avoid compaction session rebuild

* fix(core): validate active compaction from persisted transcript

* fix(vscode): harden manual compaction sidecar flow
2026-07-08 13:18:22 -07:00
Ara 177d0eb07f Remove Cline model picker recommendation copy (#12170) 2026-07-08 12:15:53 -07:00
Bee 869a87a220 fix(core): use no-emit TypeScript config for checks (#12139)
* fix(core): use no-emit TypeScript config for checks

Update the core package TypeScript config to run checks without emitting files,
allowing broader workspace sources via the package parent rootDir. Simplify the
dev config so it only extends the main package config and avoids duplicated
compiler overrides.

* feedback

* remove dead code
2026-07-08 11:15:16 -07:00
Max 0cfd0bbe05 Fix VS Code F5 webview debug flow (#12027)
* fix vscode f5 settings

- fixed the hot module reloading issue while debugging the extension.
- also fixed issue where deb:webview task wasn't showing as complete

* fix vscode webview dev cleanup

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-07-08 10:53:41 -07:00
yanalialiuk 08f656532f docs: add Atomic Chat local provider setup guide (#11966)
* docs: add Atomic Chat local provider setup guide

Document Atomic Chat alongside Ollama and LM Studio in the local models
overview and add a dedicated provider configuration page.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Update overview.mdx

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
2026-07-08 10:48:58 -07:00
Tomás Barreiro 10dece6677 Remove all ClinePass GLM 5.1 references (#12107)
* Remove all ClinePass GLM 5.1 references

* fix other references
2026-07-08 14:59:10 +02:00
Sufiyan Khan 885a2936b6 docs(authorizing): remove model-specific wording from generic setup step (#12156)
Step 4 in the IDE setup flow says 'Choose your desired Claude model'
but applies to all providers (OpenAI, Gemini, DeepSeek, local, etc.).
Drop 'Claude' to keep it provider-agnostic.
2026-07-08 21:22:33 +09:00
Dominic Cooney 90c427740d perf(sdk): stop listSessions hot loop from hanging the extension host (#11967)
* perf(sdk): stop listSessions hot loop from hanging the extension host

getStateToPostToWebview rebuilt the full task history on nearly every
streaming/session event, and each rebuild ran persistence-service.listSessions,
which synchronously read + Zod-parsed every session manifest. The 10s metadata
cache meant to absorb this was wiped on every per-turn updateTaskUsage, so each
state post paid the full synchronous scan, saturating the extension-host event
loop (observed as a tight listSessions/readFileUtf8 loop in CPU profiles).

- Debounce/coalesce postStateToWebview: trailing 50ms debounce plus a single
  queued follow-up so bursts collapse into one rebuild; dispose() tears it down.
- Add an async, title-only manifest reader (readSessionManifestTitle) and use it
  in listSessions to resolve titles concurrently off-thread, instead of a
  synchronous readFileSync + full SessionManifestSchema (Zod) parse per row. The
  existing sync manifest methods are left intact.
- On single-session updates, patch just the changed record in the merged-history
  cache in place instead of invalidating it, so frequent per-turn usage updates
  no longer force the next state post to re-enumerate and re-merge every session.

* refactor(sdk): strengthen session history cache patching

Replace patchMetadataHistoryCacheRecord (boolean-returning, metadata-only,
no re-sort) with updateCachedSessionRecord (void, updates prompt +
metadata + updatedAt, re-sorts via shared comparator).

- Void return eliminates the ignorable fallback contract.
- Mirrors all fields the persistence layer writes (prompt, metadata,
  updatedAt) so cache and disk stay consistent.
- Re-sorts after patching so the updated record bubbles to the correct
  position, using a shared compareSessionHistoryRecordsByRecencyDesc
  comparator also used by listHistory.
- Derives updatedAt from the HistoryItem timestamp instead of constructing
  a second clock value.
- Self-invalidates on cache miss so callers never manage the fallback.

Adds tests for in-place patching, re-sorting, per-turn usage hot path,
and cache-miss invalidation.

* fix(sdk): await in-flight state post during dispose

Greptile feedback: dispose() did not await a concurrently-running
runDebouncedStatePost, so an in-flight flushStateToWebview could access
torn-down resources after disposal.

Track the runDebouncedStatePost promise in statePostInFlightPromise.
In dispose(), after setting isDisposed and clearing the timer, await
the in-flight promise (swallowing errors) before tearing down downstream
resources. The !this.isDisposed guard in the loop prevents further
iterations after disposal.

* fix(sdk): address review feedback on state-post debounce and cache patch

Three issues from code review of the listSessions hot-loop fix:

1. dispose() could await the wrong promise. A second debounced timer
   firing while a flush was already running overwrote
   statePostInFlightPromise with a throwaway resolved promise from the
   join path, so dispose() could return while the original flush was
   still executing. Extract the debounce/coalesce state machine into
   StatePostDebouncer, and only track the promise from the call that
   actually starts a new flush loop.

2. postStateToWebview() swallowed flush errors, resolving every pending
   caller even when flushStateToWebview() threw. Callers awaiting
   postStateToWebview() now see the rejection, matching pre-debounce
   behavior.

3. Cache patching derived the cached updatedAt from HistoryItem.ts,
   but the persistence adapter always stamps updatedAt with the
   wall-clock write time. Callers like toggleTaskFavorite() reuse an
   old HistoryItem whose ts predates the write, which let the cached
   ordering diverge from disk until the 10s TTL expired. Stamp the
   cache patch with the write time instead.

Adds unit tests for StatePostDebouncer covering the dispose race and
error-propagation regressions, and a sdk-task-history test for the
stale-updatedAt cache-ordering regression.

* fix(sdk): don't patch cache when session update write didn't land

Beatrix's review feedback: updateSession() ignored the { updated:
boolean } result from host.update() and unconditionally patched the
metadata cache. When persistence returns updated: false (session
deleted/missing, or an optimistic-concurrency retry exhausted by a
racing writer), the webview could show a fake updated record until the
cache TTL expired.

Check the write result: only patch the cache when updated === true,
otherwise invalidate it so the next read re-enumerates from disk.

---------

Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-07-08 13:23:18 +09:00
alex-lum e6028168f2 fix(sdk/cli): emit user_id in SDK/CLI telemetry identity attributes (CLINE-2406) (#11581)
* fix(sdk/cli): emit user_id in telemetry identity attributes

Per CLINE-2406, downstream analytics expects an explicit user_id field
in authenticated SDK/CLI OpenTelemetry log attributes.

Changes:
- sdk/packages/core/src/services/telemetry/core-events.ts: add
  user_id: account.id alongside the existing account_id in
  identifyAccount() updateCommonProperties call.
- sdk/packages/core/src/services/telemetry/core-events.test.ts: new
  identifyAccount suite verifying user_id, account_id, distinct_id, and
  org context fields for authenticated user without org, with active org,
  absent/blank id handling, and no-op when telemetry is undefined.
- apps/cli/src/main.ts: after loading Cline provider settings in the
  runtime path, read auth.accountId and call identifyTelemetryAccount so
  subsequent task.* and workspace.* events carry user_id. Document
  user.extension_activated as pre-auth by design for subcommand flows.
- apps/cli/src/main.test.ts: three new tests covering saved accountId
  triggers identity, missing accountId skips identity, non-Cline
  provider skips identity.

* fix(sdk/cli): address review feedback on telemetry identity

- Use trimmed distinctId for user_id in identifyAccount() to keep
  user_id and distinct_id consistent when IDs have whitespace
- Remove fragile type cast in CLI main.ts; ProviderSettings already
  exposes auth.accountId via AuthSettingsSchema
2026-07-07 19:20:03 -07:00
Bee c3f75b3ff0 chore: Cline Code Desktop App update (#12012)
* wip: Cline Code Desktop App

Add Bun/Tauri desktop packaging commands for macOS, Windows, and Linux, including output to dist/desktop. Enforce macOS signing and notarization requirements for shareable builds while allowing an explicit unsigned local test path.

Document desktop packaging prerequisites, ignore generated build artifacts, and wire runtime session connection updates needed by the desktop app.

Clean up and update sidecar functions.
Safe to merge as this is not a published app.

* fixes

* chat

* apply

* ClinePass support

* add build instructions and use system theme

* fix: diff status

* update tool calls display

* connection updates

* lint fix

* fix keydown
2026-07-08 09:08:34 +08:00
Bee 88ce3e0b11 fix(core): emit accurate str_replace diffs (#12102)
* fix(core): emit accurate str_replace diffs

* fixes
2026-07-07 16:02:17 -07:00
Bee dd719dce86 fix(llms): OpenAI Codex model metadata for GPT Subscription provider (#12129)
* fix(llms): OpenAI Codex model metadata for GPT Subscription provider

* add unit tests

* Update stale unit tests

* clarify doc string

* Update docs format

* update old test
2026-07-07 15:57:21 -07:00
Robin Newhouse d5db7eb853 Preserve canonical session history during compaction ENG-1967 (#10651)
* Preserve canonical history with compaction sidecar

* Clarify prepareTurn request projection semantics

* Harden hub compaction sidecar ownership

* Handle compaction sidecar edge cases

* Address compaction sidecar review feedback

* Tighten compaction sidecar safety

* Extract atomic session file writes

* Assert compaction boundary role delimiter

* Simplify compaction source hashing

* Fix compaction smoke test type guard

* Fix async interactive runtime tests

* Avoid dangling compaction path in manifests

* Address compaction sidecar review nits

* fix(cli): await async runtime helper in restart test
2026-07-07 13:19:05 -07:00
Max 11d5ebe8bc chore: schedule nightly VS Code extension publish (#12124)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-07-07 10:30:04 -07:00
Saoud Rizwan 6f7cc4907f chore(cli): release v3.0.38 2026-07-06 19:08:18 -07:00
Saoud Rizwan 27e3541569 chore(sdk): release v0.0.58 2026-07-06 18:52:52 -07:00
Bee ae9c5b4d9d fix(core): tolerate orphan line-range entries in read_files input (#12104)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-07-06 18:48:50 -07:00
Bee f86ca6b36b fix(test): fix stale palette tests (#12105)
Commit 9a9300846 ("restyle chat input…", which folded in PR #12075 "replace cyan accent with new plan/act palette") deliberately rebranded the TUI accents in palette.ts:

Dark act: ANSI "cyan" → #79b8ff (and plan "yellow" → #ffea7f, success "brightGreen" → #99e89b)
Light act: #0969da → #0f72cb (and plan #9a6700 → #867100), re-derived in OKLCH to keep the same hue as the new dark accents with ≥4.5:1 contrast on white
But palette.test.ts:27-35 still asserts the old values ("preserves the existing named ANSI colors" — a test description that's now literally obsolete). So getModeAccent("act", "dark") correctly returns #79b8ff, and the test expecting "cyan" fails.

The fix is to update the two tests to the new palette values (and rename the first test, since the colors are no longer named ANSI colors).
2026-07-06 18:23:49 -07:00
Saoud Rizwan 0e0b11032e chore(sdk): release v0.0.57 2026-07-06 18:06:43 -07:00
Saoud Rizwan a93d850aee feat(cli): tint assistant markdown accents by the mode they were produced in (#12101)
* feat(cli): tint assistant markdown accents by the mode they were produced in

Markdown's prominent elements (headings, bold, list markers, links) were
hardcoded to the act accent. getSyntaxStyle now takes the entry's mode
and colors those elements with the matching accent -- plan segments
render yellow-tinted markdown, act segments blue -- completing the
per-mode transcript coloring. Code token colors stay constant across
modes; styles are cached per theme+mode pair. Unstamped entries follow
the current mode, same fallback as the glyph accent.

* fix(cli): resolve entry mode once for glyph and markdown accents

Address review: the accent and mode props used parallel fallback chains
that could drift; both now derive from a single resolved entryMode. Also
cover the light-theme plan/act markdown accents in tests.
2026-07-06 17:21:45 -07:00
Saoud Rizwan fe25258b0d feat(cli): polish status bar usage display and ClinePass model name (#12077)
* feat(cli): restyle chat input with horizontal rules and slim user bubbles

Replace the tinted-background input field with a minimal frame: full-width
horizontal rules above and below the textarea and a bold accent-colored
prompt glyph, with no background fill. User message bubbles in the
transcript match the new look: a slim neutral-gray bar with the same
glyph, no vertical padding, and no mode-colored tint.

Palette gains getInputRuleColor (neutral adaptive mid-gray for the rules)
and getUserMessageBackground (neutral bubble tint), both built on a shared
OKLAB lift helper extracted from getModeInputBackground. Home view's
robot cursor-tracking offset is adjusted for the input's new left edge,
and the spacer line between the chat input and status bar is removed.

* feat(cli): replace cyan accent with new plan/act palette

Swap the TUI accent colors: act mode goes from ANSI cyan to #79b8ff and
plan mode from ANSI yellow to #ffea7f on dark themes. Light themes get
counterparts derived in OKLCH with the same hue but darkened to hold
>=4.5:1 contrast on white (#0f72cb act, #867100 plan), selected via the
existing getModeAccent theme switch.

All hardcoded "cyan" fg literals across dialogs, model selector, config
view, and onboarding now reference palette.act so the accent is a single
source of truth. The selection highlight follows the act color. The
subliminal OKLAB chroma nudge on input backgrounds/foregrounds now leans
blue (-a, -b) instead of cyan (-a, +b) to match the new accent hue.

* feat(cli): soften success green and use act accent in markdown

Swap the success/diff green from ANSI brightGreen and #22c55e to a muted
sage #87af87 on dark themes (auto-approve banner, git diff +stats, diff
view added-sign color); light themes keep the darker #116329 for contrast
on white. Markdown prominent elements (headings, bold, list markers,
links, table headers) now use the act accent via themePalette instead of
hardcoded one-dark cyan #56b6c2.

* feat(cli): harmonize dark syntax colors with brand accent palette

Rebuild the dark syntax highlighting family around the brand anchors:
functions use the act blue, strings and inline code use the success sage,
types/italics use a dimmed plan yellow, and the remaining hues (keyword
purple, variable coral, number orange, operator ice-blue) are regenerated
in OKLCH at the same pastel lightness/chroma weight (~L 0.78, C 0.11) so
code blocks read as part of the same palette. Light theme keeps its
GitHub-light set.

* fix(cli): brighten success green to match accent palette weight

#87af87 sat at roughly half the OKLCH chroma of the act/plan anchors and
read as gray next to them; #8bd28d (L 0.80, C 0.12) matches their weight.

* fix(cli): brighten success green a step further (#99e89b)

* feat(cli): polish status bar usage display and ClinePass model name

- Cost always renders with two decimals ($0.00) instead of switching to
  four decimals under a cent; the turn summary line drops its three-decimal
  format for the same reason.
- Token count next to the context bar is now just the number; the word
  'tokens' was redundant with the bar right beside it.
- Context window bar shrinks from 8 to 6 cells.
- ClinePass models resolve their friendly models.dev name like every other
  provider and get a (ClinePass) suffix: 'GLM 5.2 (ClinePass)' instead of
  'ClinePass/glm-5.2'.
- ClinePass no longer shows '$0.00 (included with subscription)' -- cost is
  simply hidden for subscription providers.

* fix(cli): place ClinePass suffix after reasoning effort in model name

* fix(cli): format ClinePass model name as 'ClinePass: <model>' prefix

* feat(cli): color transcript entries by the mode they were produced in (#12083)

* feat(cli): color transcript entries by the mode they were produced in

Previously the whole transcript retinted to the current mode's accent on
every plan/act toggle. Entries now record the agent mode active when
they were produced and keep that accent permanently, so a session reads
as a visible history of plan (yellow) and act (blue) segments.

How the mode is captured:
- Live sessions: appendEntry in SessionProvider stamps entries from a
  uiMode ref, covering every creation site including mid-run
  switch_to_act_mode flips (which already call setUiMode through the
  runtime dialog bridge).
- Resumed sessions: hydrateSessionMessages recovers the mode from the
  persisted <user_input mode="..."> wrappers via a new shared
  parseUserInputMode helper, and flips to act at switch_to_act_mode tool
  calls. Transcripts without wrappers stay unstamped and keep the
  current-mode fallback accent, matching the old behavior.
- Restores: the /history resume and checkpoint-restore paths insert
  hydrated history via replaceEntries instead of appendEntry loops, so
  live-entry stamping cannot overwrite hydration's stamps (which would
  lock resumed transcripts to the resume-time accent).

The load-bearing core fix: readPersistedMessagesFile stripped the
user_input wrappers and mode notices from user text on every read
('display sanitization'). That read path also feeds session restarts
(mode toggle, compaction-mode change, model change, fork, recovery),
which re-persist what they read -- so every restart laundered the mode
markers off disk and out of the model's seeded context, leaving nothing
for hydration to recover. Reads now return persisted messages verbatim
and formatting is the display surface's job: the CLI TUI, history
titles, and the VS Code SDK history loader already formatted at their
boundaries; the cline-hub webview history mapping and the CLI HTML
export (which used normalizeUserInput and leaked mode_notice text) now
do too. Connectors only surface assistant text, and the remaining
readMessages consumers are programmatic (usage math, re-seeding,
compaction input) where raw is correct.

* fix(shared): match parseUserInputMode exactly to what the writer emits

Drop the case-insensitive flag and the 'zen' value from the wrapper
regex: formatUserInputBlock only ever writes lowercase act/plan/yolo, so
anything else the parser accepted (uppercase look-alikes in adversarial
content, a zen value with no writer) could never be real persisted data.
2026-07-06 16:00:17 -07:00
Saoud Rizwan 53d1567731 feat(cli): default thinking level picker cursor to Medium instead of Off (#12092)
* feat(cli): default thinking level picker cursor to Medium instead of Off

* chore(cli): drop explanatory comments from thinking level defaults
2026-07-06 15:51:40 -07:00
Saoud Rizwan 9a93008463 feat(cli): restyle chat input with horizontal rules and slim user bubbles (#12074)
* feat(cli): restyle chat input with horizontal rules and slim user bubbles

Replace the tinted-background input field with a minimal frame: full-width
horizontal rules above and below the textarea and a bold accent-colored
prompt glyph, with no background fill. User message bubbles in the
transcript match the new look: a slim neutral-gray bar with the same
glyph, no vertical padding, and no mode-colored tint.

Palette gains getInputRuleColor (neutral adaptive mid-gray for the rules)
and getUserMessageBackground (neutral bubble tint), both built on a shared
OKLAB lift helper extracted from getModeInputBackground. Home view's
robot cursor-tracking offset is adjusted for the input's new left edge,
and the spacer line between the chat input and status bar is removed.

* feat(cli): replace cyan accent with new plan/act palette (#12075)

* feat(cli): replace cyan accent with new plan/act palette

Swap the TUI accent colors: act mode goes from ANSI cyan to #79b8ff and
plan mode from ANSI yellow to #ffea7f on dark themes. Light themes get
counterparts derived in OKLCH with the same hue but darkened to hold
>=4.5:1 contrast on white (#0f72cb act, #867100 plan), selected via the
existing getModeAccent theme switch.

All hardcoded "cyan" fg literals across dialogs, model selector, config
view, and onboarding now reference palette.act so the accent is a single
source of truth. The selection highlight follows the act color. The
subliminal OKLAB chroma nudge on input backgrounds/foregrounds now leans
blue (-a, -b) instead of cyan (-a, +b) to match the new accent hue.

* feat(cli): soften success green and use act accent in markdown

Swap the success/diff green from ANSI brightGreen and #22c55e to a muted
sage #87af87 on dark themes (auto-approve banner, git diff +stats, diff
view added-sign color); light themes keep the darker #116329 for contrast
on white. Markdown prominent elements (headings, bold, list markers,
links, table headers) now use the act accent via themePalette instead of
hardcoded one-dark cyan #56b6c2.

* feat(cli): harmonize dark syntax colors with brand accent palette

Rebuild the dark syntax highlighting family around the brand anchors:
functions use the act blue, strings and inline code use the success sage,
types/italics use a dimmed plan yellow, and the remaining hues (keyword
purple, variable coral, number orange, operator ice-blue) are regenerated
in OKLCH at the same pastel lightness/chroma weight (~L 0.78, C 0.11) so
code blocks read as part of the same palette. Light theme keeps its
GitHub-light set.

* fix(cli): brighten success green to match accent palette weight

#87af87 sat at roughly half the OKLCH chroma of the act/plan anchors and
read as gray next to them; #8bd28d (L 0.80, C 0.12) matches their weight.

* fix(cli): brighten success green a step further (#99e89b)
2026-07-06 15:50:50 -07:00
Saoud Rizwan 678b0ae951 docs: polish README model table grammar and wording (#12098)
Claude-Session: https://claude.ai/code/session_017VJNCE1o6zzcVfpjpTVnt5

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-06 15:29:12 -07:00
cline-cloud[bot] 8b6f2cf0b7 Raise live catalog default input tokens (#11930)
* fix(llms): raise catalog default input tokens

* Lower live catalog default input tokens to 128000

* Lower compaction DEFAULT_MAX_INPUT_TOKENS to 128000

---------

Co-authored-by: Cline Bot <bot@cline.bot>
Co-authored-by: John Simone <john@cline.bot>
2026-07-06 10:48:51 -07:00
Saoud Rizwan 25ef0939cc chore(cli): release v3.0.37 2026-07-03 19:31:47 -07:00
Saoud Rizwan 4f770011e9 chore(sdk): release v0.0.56 2026-07-03 19:11:03 -07:00
Saoud Rizwan a1d69fee6f fix(llms): stop AI SDK from rejecting malformed tool calls before flexible tool executors can handle them (#12061)
* fix(llms): stop rejecting malformed tool calls before tools can handle them

Weak models emit tool calls with schema mismatches (bare string for a
string[] arg) or unparsable JSON. The AI SDK adapter rejected both
before execution, so the lenient union schemas in the core tool
executors never ran. Drop the strict validate callback (tools own input
validation) and add experimental_repairToolCall backed by the shared
jsonrepair parser for arguments that fail JSON parsing.

* docs(llms): fix stale comment referencing removed validate callback
2026-07-03 18:55:31 -07:00
Saoud Rizwan 3575b38122 fix(shared): stop deleting mode_notice from outbound prompts (#12058)
* fix(shared): stop deleting mode_notice from outbound prompts

The mode-switch notice from #12057 never reached the model:
prepareTurnInput sanitizes every outbound prompt with normalizeUserInput
before wrapping it, and #12057 put the mode_notice strip inside
normalizeUserInput -- so the host deleted the notice on every send. The
transcript confirms it: messages sent after a toggle carry the
user_input wrapper but no notice, and models asked about it confabulate
having seen one because the system prompt describes the tag.

Move the strip into a dedicated stripModeNotices() applied only at
display boundaries: formatDisplayUserInput (TUI hydration, title
inference), deriveTitleFromPrompt (session titles), and the TUI queued
prompt echo. normalizeUserInput now preserves notices, with a
regression test pinning the outbound behavior. Side benefit: notices
survive the message-builder history normalization and the pending
prompt queue, so queued sends deliver them too.

* docs(shared): correct formatModeSwitchNotice JSDoc after strip relocation

* refactor(shared): generalize notice stripping to stripTagElements

stripModeNotices becomes a thin policy wrapper (DISPLAY_HIDDEN_TAGS owns
the what-to-hide list in one place) over a generic stripTagElements that
removes whole elements for any tag list -- the remove-element counterpart
to xmlTagsRemoval. Call sites at display boundaries now carry comments
explaining why stripping happens there and not in normalizeUserInput,
which also sanitizes model-bound prompts.

* revert(shared): drop stripTagElements generalization, keep simple stripModeNotices

The generic tag stripper added API surface without a second use case;
stripModeNotices goes back to the direct implementation. The display-vs-
model call-site comments from the same commit stay.
2026-07-03 15:45:34 -07:00
Saoud Rizwan b5468e1227 feat(cli): make plan/act mode switches visible to the model (#12057)
* feat(cli): make plan/act mode switches visible to the model

The mode signal already rides on every user message via the
<user_input mode="..."> wrapper, but nothing ever told the model what
the attribute means, and a manual plan/act toggle produced no inline
signal at all -- only an invisible system prompt swap the model cannot
diff. Two additions:

- The CLI system prompt now explains the mode attribute (both modes,
  since after a switch the transcript still contains messages tagged
  with the other mode) and that the newest message's mode governs.
- A user-initiated toggle stamps the next user message with a
  <mode_notice> block marking the switch, e.g. "The user switched from
  act mode to plan mode before sending this message." Round trips that
  return to the mode the model last saw cancel out. The model-initiated
  switch_to_act_mode path is excluded: its continuation prompt already
  announces the switch.

The notice vocabulary lives in @cline/shared next to the user_input
wrapper it extends, and normalizeUserInput hides the whole element from
transcript display the same structural way it strips the wrapper tags.

* fix(shared): strip mode_notice elements without polynomial regex

CodeQL flagged the lazy dot-all pattern (js/polynomial-redos): with the
global flag, every unmatched opening tag re-scans to the end of the
string, which is quadratic on adversarial transcript content. Replace
it with an indexOf-based splice that removes matched elements in linear
time and leaves unclosed tags intact, with a regression test on 50k
repeated open tags.
2026-07-03 15:05:22 -07:00
Saoud Rizwan 10d1c41b7a fix(cli): prevent empty session from racing a mode-change restart (#12056)
restartWithMessages cleared startupPromise and tore down the active
session before the replacement registered, leaving a window with no
active session and no startup in flight. A message submitted in that
window (e.g. typed right after a plan/act Tab toggle) made ensureReady
boot a blank fresh session, which then won the active slot over the
restarted session carrying the conversation history -- the model
responded as if the conversation had just started.

Publish the restart itself as the in-flight startupPromise so any
concurrent ensureReady waits for the restart instead of booting an
empty session. The barrier is cleared once the restart settles,
keeping failed restarts retryable by the next ensureReady.
2026-07-03 14:10:12 -07:00
Saoud Rizwan b823358867 chore(cli): release v3.0.36 2026-07-03 13:39:40 -07:00
Saoud Rizwan b876945c6d fix(cli): end plan-mode run on switch_to_act_mode and auto-continue with act tools (#12054)
* fix(cli): end plan-mode run on switch_to_act_mode and auto-continue with act tools

The CLI's switch_to_act_mode tool only queued the mode change; it was
applied after the whole turn finished. The model kept running the rest
of the turn with plan-mode tools (no editor) despite the tool result
claiming it now had edit access, so it fell back to editing files via
run_commands (sed/heredocs).

Mirror the VS Code extension's approach: the switch tool now completes
the run (lifecycle.completesRun), the pending mode change rebuilds the
session with act-mode tools, and a canned continuation prompt resumes
the approved plan automatically. Pending mode changes are tagged with
their source (tool vs UI toggle) so a Tab toggle racing a natural turn
completion can never auto-start plan execution the user did not
approve. The synthetic continuation prompt is hidden from transcript
hydration, and the plan-mode prompt/tool description now warn that
switching immediately starts execution.

* refactor(cli): show act-mode continuation prompt on resume instead of filtering it

Displaying the synthetic user message honestly beats exact-string
matching at the display layer, which was brittle and did not cover
other transcript consumers anyway. The live TUI still never echoes it;
it only appears as a user bubble when resuming a session. A
synthetic-message marker plumbed through SendSessionInput is the
principled follow-up if hiding it becomes worth the SDK surface
change.

* Revert "refactor(cli): show act-mode continuation prompt on resume instead of filtering it"

This reverts commit 969a24f9c9.
2026-07-03 13:10:29 -07:00
Saoud Rizwan 453cdea040 chore(cli): release v3.0.35 2026-07-03 10:26:32 -07:00
Saoud Rizwan 091eccdfe2 test: update GLM 5.2 context window assertions for refreshed catalog 2026-07-03 10:13:23 -07:00
Saoud Rizwan a2a46ae600 chore(sdk): release v0.0.55 2026-07-03 09:56:58 -07:00
Saoud Rizwan 82c9e77de2 style: apply formatter to pre-existing drift 2026-07-03 09:56:52 -07:00
Robin Newhouse dfd0e022a4 Add VS Code SDK compaction strategy setting (#11892)
* Add VS Code SDK compaction strategy setting

* Move compaction strategy setting into SDK

* Preserve stub global settings on compaction update

* Keep ApiProvider settings as proto strings

* Address compaction strategy review feedback
2026-07-02 23:58:47 -07:00
Robin Newhouse f0ec6a35bb fix: advertise run commands as shell strings (#12038) 2026-07-02 21:44:25 -07:00
Morgan Carr c09d54f5a2 fix(cli): format structured commands in history export (#12023)
Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
2026-07-01 15:53:27 -07:00
Tomás Barreiro 984d70a351 Add the subscription promo code when linking to the dashboard subscription page (#12019)
* Add the subscription promo code when linking to the dashboard page

* revert tests
2026-07-02 00:47:30 +02:00
Bee bbe7b6fd49 fix(hub): hydrate tool results in session message mapping (#12011)
* fix(hub): hydrate tool results in session message mapping

Map historical tool call/use and tool result blocks into webview tool events, including same-message results and following user result messages. Add tests to verify hydrated outputs and block ordering so restored sessions render completed tool interactions correctly.

* feedback
2026-07-01 14:28:13 -07:00
Bee be97d951fa fix: first-prompt truncation (#12022)
* Fix basic compaction first-prompt truncation

Issue: shallow sessions on high-output models such as OpenRouter MiniMax M3 could auto-compact immediately and reduce the initial task prompt to only the leading <user_input> wrapper. Harbor still passed the full task into Cline and session metadata retained it, but the model conversation could receive a truncated first message and respond that the request was empty or cut off.

Root cause: the output-runway target used maxInputTokens - maxTokens for every basic compaction. For MiniMax M3, maxTokens is nearly maxInputTokens, producing a tiny target. Basic compaction then used its last-resort first-user truncation path, and raw messages still contain the user_input envelope, so prefix truncation preserved the wrapper instead of the actionable task.

Fix: only use the output-runway target after the transcript has at least five user-assistant pairs, so early/shallow tasks use the normal trigger-based target. Also prevent first-user truncation unless that first user message alone exceeds the trigger budget, preserving normal first-turn prompts while still allowing genuinely oversized prompts to be reduced. Added regression tests for the MiniMax-style shallow prompt case and the oversized-first-prompt escape hatch.

* Fix compaction budget for huge-output models

Avoid collapsing context-derived input budgets when a model reports an output limit nearly equal to its context window, such as MiniMax M3. In those cases, treating context-output as the input budget causes auto-compaction to trigger on normal-sized prompts.

Only use contextWindow - maxTokens when the derived value remains at least half of the context window. Also simplify long-conversation basic compaction targeting to maxInputTokens * 0.5 instead of applying the default target ratio to maxInputTokens - maxTokens.

Adds regression coverage for MiniMax-style context-only metadata so an 18k-token prompt does not compact against an incorrectly collapsed 12k input budget.

* Guard compaction estimator against cumulative metrics

* Address basic compaction target review comments
2026-07-01 13:51:50 -07:00
Robin Newhouse c331a8f4b6 fix(core): use curated default for legacy provider migration (#12030) 2026-07-01 12:42:42 -07:00
Ara f180f1584d Add first-request failure telemetry (#11852)
* fix(vscode): capture first request failure telemetry

* Fix provider failure telemetry review feedback

* test(vscode): avoid extension host mock matchers

* fix(vscode): use session metadata for provider failure telemetry

* test(vscode): document pre-session auth telemetry skip

* fix(vscode): use turn-scoped provider failure gate

* fix(vscode): include cline pass in auth failure checks

* refactor(vscode): keep provider failure turn count in gate
2026-07-01 10:23:36 -07:00
Ara 9197d15abf feat(vscode): recognize Tencent TokenHub provider (#12028) 2026-07-01 09:58:45 -07:00
Ara 6c0d5c97b1 feat(llms): add Tencent TokenHub provider (#12014) 2026-07-01 09:58:10 -07:00
Dominic Cooney 9a8be88e85 fix(protos): self-heal protoc download when bun skips grpc-tools postinstall (#12003) 2026-07-01 08:31:04 +09:00
Ara 60f4a482ca Add onboarding intent telemetry (#11848)
* Add onboarding intent telemetry

* Track prompt submit intent from chat UI
2026-06-30 14:19:51 -07:00
John Choi dbe15202e1 fix(cli): update ClinePass tests for forced-enabled behavior (#11990)
#11986 (Forcefully enable ClinePass on the CLI) hardcoded isClinePassEnabled: true in session-runtime.ts, provider-catalog.ts, and main.ts and removed the ext-cline-pass feature-flag check, but left the corresponding tests asserting the old flag-driven / disabled behavior. They fail on main (and every branch that merges it).

- session-runtime.test.ts: expect getLastUsedProviderSettings called with isClinePassEnabled: true.

- provider-catalog.test.ts: drop the obsolete getBooleanFlagEnabled('ext-cline-pass') assertion (source no longer reads the flag) and its now-unused mock; keep the isClinePassEnabled: true expectation.

- main.test.ts: the ClinePass flag is no longer read during startup, so getBooleanFlagEnabled is never called. Re-target the 'seed identity before flags' ordering assertion at refreshCliFeatureFlagsInBackground (which is still invoked after seeding), and wire that mock through featureFlagMocks.
2026-06-29 22:35:52 +02:00
Bee 8d102db392 chore: remove console logs from compaction test (#11983)
Follow up on #11894, this PR removes the console logging code from the compaction unit test.

Co-authored-by: John Choi <97497948+johnwschoi@users.noreply.github.com>
2026-06-29 13:22:05 -07:00
Max 3dfd5dc31c fix(cli): recover missing interactive sessions on message reads (#11984)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-29 13:21:27 -07:00
John Choi 43ce9f3694 fix(vscode): exclude vitest-owned tests from the mocha integration compile (#11981)
The vscode test (Extension Integration Tests) job is red on main: the updateAutoApprovalSettings suite (added in #11929) is vitest-native but was being collected and run by the Mocha @vscode/test-cli runner, where vitest-only matchers (toHaveBeenCalledWith / toHaveBeenCalledOnce / not.toHaveBeenCalled) are not registered, failing with 'is not a function'.

build-tests.js already excludes bun:test-owned tests from the Node-based out/ tree (single source of truth for the runner split). Extend that same mechanism to also exclude vitest-owned tests (files importing from 'vitest'), so neither bun nor vitest suites are ever compiled into the mocha out/ tree. Verified locally: detection catches the state suite (123 non-mocha test files total) while preserving the existing 60 bun __tests__ exclusions. No coverage lost — these suites run under test:vitest / bun.
2026-06-29 12:51:52 -07:00
Tomás Barreiro f1c73fb48b Remove unused imports (#11988) 2026-06-29 12:39:17 -07:00
Tomás Barreiro abaa8383c4 Forcefully enable ClinePass on the CLI (#11986) 2026-06-29 21:31:51 +02:00
Renee Huang 3a5e372d73 docs: document ClinePass API usage (#11980)
* docs: add MiniMax M3, Qwen3.7 Max, Qwen3.7 Plus models to ClinePass page

* more updates

* explicitly direct to personal org

* making the clinepass page more detailed

* updates to cline provider wording

* docs: document ClinePass API usage

* chore: discard McpHub change from PR

* docs: simplify ClinePass model slug table

* updates

* nit
2026-06-29 10:59:02 -07:00
Saoud Rizwan cf3a59f0e2 chore(cli): release v3.0.34 2026-06-29 09:59:28 -07:00
Tomás Barreiro b3aee68ca5 Merge both options and remove credits link (#11973)
* Merge both options and remove credits link

* Remove import

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-29 09:48:57 -07:00
Tomás Barreiro cd8fd29063 Improve the ClinePass step wording (#11974)
* Improve the ClinePass step wording

* Use a blacklist instead

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-29 09:21:27 -07:00
Saoud Rizwan 7777d61311 fix(cli): suppress ClinePass notice after onboarding (#11975) 2026-06-29 09:20:35 -07:00
Renee Huang 64fc3f372e docs: add MiniMax M3, Qwen3.7 Max, Qwen3.7 Plus models to ClinePass page (#11849)
* docs: add MiniMax M3, Qwen3.7 Max, Qwen3.7 Plus models to ClinePass page

* more updates

* explicitly direct to personal org

* making the clinepass page more detailed

* updates to cline provider wording
2026-06-29 07:28:38 -07:00
Saoud Rizwan 4175677e71 chore(cli): release v3.0.33 2026-06-28 23:45:56 -07:00
Saoud Rizwan 4934450947 fix(cli): show ClinePass subscription URL fallback (#11961)
* fix(cli): show ClinePass subscription URL fallback

* fix(cli): move ClinePass URL fallback below options
2026-06-28 23:33:58 -07:00
Saoud Rizwan b0a2d8a223 fix(cli): hide ClinePass promo for ClinePass users (#11963)
* fix(cli): hide ClinePass promo for ClinePass users

* fix(cli): expand ClinePass subscription card

* fix(cli): tune ClinePass subscription card height
2026-06-28 23:33:19 -07:00
Saoud Rizwan d9f1d862a5 fix(cli): use adaptive plan accent for ClinePass prompts (#11962) 2026-06-28 23:12:30 -07:00
Saoud Rizwan f8d73f3811 chore(cli): release v3.0.32 2026-06-28 21:43:18 -07:00
Saoud Rizwan 9aac8340dc chore(sdk): regenerate bun.lock for v0.0.54
Align resolved workspace versions in bun.lock with the v0.0.54 package
bumps. bun pm pack substitutes workspace:* deps using the version
recorded in bun.lock, so a stale lock made packed inter-package deps
resolve to 0.0.53, failing check-publish and the node smoke test (which
then pulled the old published @cline/shared from npm).
2026-06-28 21:24:41 -07:00
Saoud Rizwan 242b5ebff6 chore(sdk): release v0.0.54 2026-06-28 20:54:47 -07:00
Tomás Barreiro 7ca41fdb7d Improve ClinePass onboarding UX (#11959)
* Prevent ClinePass onboarding flicker

* Make the clinepass step scrollable and remove details
2026-06-28 20:49:27 -07:00
Saoud Rizwan 000918989f fix(cli): make ClinePass subscription screen selectable (#11957) 2026-06-28 20:12:04 -07:00
Saoud Rizwan 9560b6d625 fix(cli): use ClinePass as one word consistently (#11956) 2026-06-28 18:41:26 -07:00
Saoud Rizwan c7304097a7 fix(cli): update ClinePass provider UI copy (#11953)
* fix(cli): update ClinePass provider UI copy

* fix(llms): rename Cline provider display name

* fix(cli): separate ClinePass billing links
2026-06-28 17:57:11 -07:00
Tomás Barreiro 674a6022ee Add an intermediate step before going to ClinePass model selection (#11947)
* Add an intermediate step before going to model selection

* fix type issues

* use allSettled

* fix(cli): serialize ClinePass subscription checks

* fix(cli): handle missing ClinePass plan as unsubscribed

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-28 16:13:25 -07:00
Saoud Rizwan dbf0775384 fix(llms): keep error detail extraction for Error instances (#11949)
PR #11928 added an `instanceof Error` branch to extractErrorMessage to
preserve transport-error wrappers (e.g. "fetch failed: SocketError: ...
(UND_ERR_SOCKET)"), but that branch regressed two cases:

- Generic SDK wrappers like "No output generated. Check the stream for
  errors." were prepended to the real cause instead of being dropped.
- Errors carrying structured detail on responseBody/detail/error fields
  surfaced the bland top-level .message ("Bad Request") instead of the
  detail ("Instructions are required").

The Error branch now drops known generic wrappers in favor of the cause
and extracts structured detail from the error's own fields, while keeping
the transport-wrapper concat behavior #11928 intended.
2026-06-28 15:27:46 -07:00
Saoud Rizwan e130b45eb0 feat(cli): promote Cline Pass in startup notice (#11948) 2026-06-28 13:14:27 -07:00
Tomás Barreiro 6f4dbae86f Improve the ClinePass onboarding experience on the CLI [ENG-2236] (#11946)
* Improve the ClinePass onboarding experience on the CLI

* Update apps/cli/src/tui/views/onboarding/screens.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* address comments

* style(cli): format ClinePass onboarding warning

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-28 10:53:27 -07:00
Saoud Rizwan c7de31ae24 ci(vscode): gate legacy publish on test job (#11936) 2026-06-27 19:33:37 -07:00
Saoud Rizwan 45dddb9a4e ci(vscode): add legacy extension publish workflow (#11935) 2026-06-27 19:17:46 -07:00
Bee e32caee96b fix: basic compaction token budgeting (#11894)
* Improve compaction token budgeting

Use MessageWithMetadata.metrics input/output token counts when estimating message size for compaction, falling back to the existing chars/3 heuristic only when metrics are unavailable. This makes trigger decisions and post-compaction accounting use provider-reported token usage instead of relying only on serialized character estimates.

When an explicit compaction maxInputTokens budget is configured and the model exposes maxTokens, reserve half of the model output budget before triggering compaction. This gives the next provider request room for completion tokens and reduces edge cases where the local context estimate passes but the provider rejects the prompt as exceeding its limit.

Keep explicit reserveTokens and thresholdRatio overrides intact, and add regression coverage for metric-based token estimation, fallback estimation, and output-token-aware trigger budgeting.

* new target tokens and trigger tokens value

* fixes

* use imports and add unit test

* resolveMaxInputTokens

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-27 16:04:56 -07:00
Saoud Rizwan 8f6dae0ac0 fix(vscode): sync auto-approve task settings (#11929) 2026-06-27 15:13:14 -07:00
Saoud Rizwan 263b58f8c3 fix(llms): preserve fetch error cause details (#11928) 2026-06-27 14:51:56 -07:00
Saoud Rizwan b193a81ac9 fix: prevent API key field clearing on settings load (#11925)
* fix: prevent API key field clearing on settings load

* fix: cancel API key save on mask hydration
2026-06-27 14:32:14 -07:00
John Choi 92806c60ca fix(agents): derive messageModelInfo in the provider/model runtime path (#11903)
The standalone AgentRuntime({ providerId, modelId }) constructor built the gateway model in resolveRuntimeConfig and returned { ...rest, model } without deriving messageModelInfo. The prebuilt-model path preserves it, and core sessions populate it via buildMessageModelInfo, but standalone SDK callers lost it -- so assistant-message modelInfo and model-tagged telemetry (reasoning tokens, action-follow-through) emitted without provider/model dimensions.

Derive messageModelInfo as { id: modelId, provider: providerId } in that path (family omitted; it is optional and unavailable here). An explicit caller-provided messageModelInfo still wins. Adds provider-form tests covering both the derived and explicit-override cases.
2026-06-26 18:23:54 -07:00
Saoud Rizwan 3a05171e30 fix(sdk): preserve failed run error messages (#11904) 2026-06-26 18:04:53 -07:00
Saoud Rizwan a6e315a4a6 chore(cli): release v3.0.31 2026-06-26 17:37:36 -07:00
Saoud Rizwan 2714f93b45 chore(sdk): drop volatile catalog refresh from v0.0.53
The bun run version catalog regen dropped the xiaomi mimo-v2-omni,
mimo-v2-pro, and mimo-v2-flash models from the live data. mimo-v2-omni
is the xiaomi provider's defaultModelId in builtins.ts, so shipping the
refreshed catalog would point the default at a missing model and broke
the provider-ids test. Revert the catalog to the pre-release state and
ship v0.0.53 as a pure SDK code release; the catalog will refresh in a
later release once upstream data is stable.
2026-06-26 17:17:00 -07:00
Saoud Rizwan 3abeb8a90b chore(sdk): release v0.0.53 2026-06-26 17:03:04 -07:00
Tomás Barreiro 7830472017 Add open subscription page option to the ClinePass options (#11896)
* Add open subscription page option to the ClinePass options

* Address comments
2026-06-27 01:07:11 +02:00
Bee 83339c3c5a refactor: extension to use sdk provider list (#11888)
* refactor(vscode): use string type for api provider in proto

Replace the ApiProvider proto enum with plain string fields across\nmodels.proto and state.proto, and drop the enum<->string conversion\nmappings in api-configuration-conversion.ts. Updates ApiOptions and\nOpenAICompatible settings components accordingly.

* fix custom provider render

* format

* id

* remove extension providers file

* uses includes
2026-06-26 16:05:55 -07:00
Tomás Barreiro 664daf6ded Show cost has been covered by the users subscription (#11889)
* Show cost has been covered by the users subscription

* fix tests

* Update apps/cli/src/tui/components/status-bar.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update wording

* Update tests

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-06-26 23:34:06 +02:00
Tomás Barreiro 5b1f8850af Update coupon code (#11890) 2026-06-26 23:33:11 +02:00
Tomás Barreiro c49d4121a3 Fix SDK tests (#11895) 2026-06-26 14:20:58 -07:00
Tomás Barreiro 7f495a5e99 Open ClinePass subscribe page (#11891) 2026-06-26 22:54:00 +02:00
Max 1e88a708bd upate changelog (#11886)
* upate changelog

* Update CHANGELOG.md

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-26 12:50:52 -07:00
Saoud Rizwan 408be18be9 fix(ci): harden ext-vscode stable release workflow (#11887)
* fix(ci): harden ext-vscode stable release workflow

- Resolve previous tag to the latest vX.Y.Z ancestor instead of the most
  recent reachable tag. The nightly workflow now pushes a nightly-main-*
  annotated tag on every main commit, so git describe was resolving the
  release notes' Full Changelog compare link to a nightly tag rather than
  the prior release tag.
- Extract the changelog section by exact version heading (and fail if it
  is missing) instead of always taking the first ## [ block, so a stale
  top entry can no longer ship as the release notes for a different
  version.
- Add a pre-publish Verify Changelog Entry gate so a release cannot be
  published unless CHANGELOG.md leads with the version being released.
- Add a Verify Marketplace Tokens gate so a missing VSCE_PAT/OVSX_PAT
  fails fast before packaging rather than mid-publish.
- In existing-tag mode, require the tag to point at the tested SHA so the
  published artifact always matches what CI verified.
- Add a concurrency group keyed on the tag to prevent duplicate
  concurrent publishes of the same release.

* fix(ci): validate stable release metadata before publish
2026-06-26 12:50:28 -07:00
Saoud Rizwan 5a9c637e32 fix(core): cap MCP tool names at 64 chars for OpenAI-compatible providers (#11885) 2026-06-26 12:42:57 -07:00
Saoud Rizwan 8152572641 fix(vscode): disable MCP marketplace tab from remote config (#11883) 2026-06-26 11:48:07 -07:00
Saoud Rizwan 0736e12e32 fix(vscode): refresh MCP hub after marketplace install (#11882) 2026-06-26 11:41:34 -07:00
Saoud Rizwan 690f80523f fix(vscode): preserve migrated OpenAI-compatible settings (#11880)
* fix(vscode): preserve migrated OpenAI-compatible settings

* test(vscode): cover OpenAI-compatible plan act selections
2026-06-26 10:56:50 -07:00
Saoud Rizwan 4fa1b8a291 fix(vscode): reject approvals from composer feedback (#11874) 2026-06-26 04:01:10 -07:00
Saoud Rizwan ed685b28e0 feat(vscode): allow cancelling queued prompts (#11875) 2026-06-26 03:58:46 -07:00
Saoud Rizwan 38338310d7 fix(vscode): timeout terminal cwd setup (#11871)
* fix(vscode): timeout terminal cwd setup

* refactor(vscode): simplify terminal cwd timeout
2026-06-26 03:49:36 -07:00
Saoud Rizwan 1c4a7885e6 fix(vscode): keep command output pinned to bottom (#11873) 2026-06-26 03:49:05 -07:00
Saoud Rizwan a0517db2fa feat: add shared marketplace uninstall support (#11870)
* feat: add shared marketplace uninstall support

* fix: avoid regex backtracking in marketplace skill sanitization

* fix: address marketplace uninstall review feedback

* fix: clean up remaining marketplace skill installs

* fix: remove marketplace skills from all agents

* fix: keep customize primitive tabs horizontal
2026-06-26 03:47:53 -07:00
Saoud Rizwan 38134ef967 fix(vscode): surface plugin bundled skills (#11868)
* fix(vscode): surface plugin bundled skills

* fix(core): align plugin skill settings lookup
2026-06-25 21:31:45 -07:00
Saoud Rizwan 8715cafce7 fix(vscode): rename user message reset actions (#11869) 2026-06-25 21:30:46 -07:00
Saoud Rizwan 46ee8ea329 feat(vscode): add customize section tabs (#11867)
* feat(vscode): add customize section tabs

* fix(vscode): reset customize section tab

* fix(vscode): reset customize section on initial type
2026-06-25 21:29:30 -07:00
Tomás Barreiro b7d9ea4500 Add a prompt to change to ClinePass when out of credits (#11866)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-25 21:11:23 -07:00
Saoud Rizwan 5147abc75e fix: hide workflows from customize menu (#11864) 2026-06-25 20:57:10 -07:00
Saoud Rizwan 9abd7ae8c3 fix(vscode): disable command auto-approval by default (#11865) 2026-06-25 20:56:30 -07:00
Saoud Rizwan bf83303bb7 fix(cli): require quoted prompts for one-shot mode (#11861) 2026-06-25 20:54:13 -07:00
Saoud Rizwan bfe5bf841a refactor: share marketplace install logic through core (#11862)
* refactor: share marketplace install logic through core

* fix: harden shared install helpers

* refactor: share mcp marketplace arg parsing
2026-06-25 20:34:42 -07:00
Tomás Barreiro eb362df3ba List ClinePass features in the CLI not-subscribed message (#11846)
* List ClinePass features in the CLI not-subscribed message

* fix tests
2026-06-26 05:22:15 +02:00
Saoud Rizwan f735ddcb7a fix(vscode): handle escape while editing user messages (#11860) 2026-06-25 20:20:33 -07:00
Saoud Rizwan 5b63d3e9c8 fix(vscode): preserve raw structured terminal commands (#11857) 2026-06-25 20:19:40 -07:00
Saoud Rizwan 1ff6a54825 fix: wrap customize tabs on narrow screens (#11855) 2026-06-25 19:17:49 -07:00
Saoud Rizwan b1a3cb6cfc chore(cli): release v3.0.30 2026-06-25 18:03:44 -07:00
Saoud Rizwan 78f1736723 test(cli): widen help terminal so long flag descriptions don't wrap
The --thinking description added in #11656 is long enough that at 120
columns commander wraps it, splitting "omitted leaves provider default"
across two lines. The TUI e2e assertion uses a contiguous getByText, so it
failed on the ubuntu-only TUI test leg, blocking the SDK publish gate.
Widen the help terminal to 200 columns so long descriptions render on a
single line.
2026-06-25 17:51:56 -07:00
Tran Binh Minh 28a014c1c6 docs: fix outdated skills enable path (#11838)
The Skills note pointed users to "Settings → Features → Enable Skills,"
but that toggle no longer exists — the Features settings section has no
Skills entry and skills are loaded by default. Point users to the actual
Skills menu (scale icon → Skills tab), consistent with the access path
already documented later in the same page.

Fixes #11740

Co-authored-by: Minhkunn <minh.12072k6@gmail.com>
2026-06-25 17:32:09 -07:00
Saoud Rizwan fed291e37a chore(sdk): release v0.0.52 2026-06-25 17:27:03 -07:00
Saoud Rizwan bb68351123 fix(vscode): avoid duplicate followup answer bubbles 2026-06-25 17:18:09 -07:00
Saoud Rizwan 84cb15813a fix(vscode): polish queued prompt panel 2026-06-25 17:17:48 -07:00
Saoud Rizwan c3671de7de fix(vscode): disable subagents (#11847) 2026-06-25 17:02:25 -07:00
Saoud Rizwan 26f737913f fix(vscode): show direct user messages immediately (#11845)
* fix(vscode): show direct user messages immediately

* fix(vscode): address pending chat bubble review
2026-06-25 16:53:15 -07:00
Dominic Cooney 50797dd82d vscode(ENG-2203): Migrate legacy MCP files, formats to shared settings file. (#11818)
* Migrate legacy MCP files, formats to shared MCP settings.
2026-06-26 08:23:02 +09:00
Saoud Rizwan 923ee3e137 fix(vscode): enable auto-approval defaults (#11840)
* fix(vscode): enable auto-approval defaults

* test(vscode): update file edit e2e for auto-approval defaults
2026-06-25 15:03:09 -07:00
Saoud Rizwan bbf5bb2302 fix(vscode): hide auto-approve notifications toggle (#11843) 2026-06-25 15:01:46 -07:00
Saoud Rizwan 0220b9a506 fix(vscode): queue chat submits during active turns (#11839)
* fix(core): avoid requeueing terminal failed prompts

* fix(vscode): avoid duplicate queued follow-up messages

* fix(vscode): keep approvals pending for queued messages

* fix(vscode): queue chat submits during active turns

* fix(core): restore pending prompt requeue on send failure

* test(core): remove pending prompt status churn
2026-06-25 15:01:10 -07:00
Ara 1b573275f8 fix(vscode): remove test server (#11842) 2026-06-25 14:39:59 -07:00
Robin Newhouse d7d74e0b89 fix(llms): preserve OpenRouter reasoning disable semantics (#11656)
* fix(llms): preserve OpenRouter reasoning disable semantics

* fix(llms): clarify reasoning token usage

* fix(llms): use OpenRouter reasoning effort none

* refactor(cli): extract reasoning resolution helper

* fix(cli): clarify thinking defaults

* feat(agents): capture unexpected reasoning token telemetry

* fix(cli): preserve reasoning on model change

* refactor(llms): table-drive reasoning token extraction

* fix(sdk): catalog unexpected reasoning telemetry
2026-06-25 14:35:05 -07:00
Saoud Rizwan bebc581652 fix(core): add non-interactive command guidance (#11815)
* fix(core): add non-interactive command guidance

* fix(core): refine non-interactive command guidance
2026-06-25 12:34:32 -07:00
Saoud Rizwan 21ebee3638 fix(sdk): keep SAP model filtering in clients (#11837) 2026-06-25 12:31:59 -07:00
Saoud Rizwan 7d78c5f074 refactor(vscode): attach checkpoints to user edits (#11832)
* refactor(vscode): attach checkpoints to user edits

* fix(vscode): improve checkpoint edit controls

* fix(vscode): align checkpoint edit actions

* fix(vscode): gate checkpoint restore edits
2026-06-25 12:21:17 -07:00
Saoud Rizwan 352a23a6da fix: stabilize SAP AI Core provider setup (#11833)
* fix: Filter SAP AI Core models based on mode-availibility

* chore: fix model picker test

* fix: harden SAP AI Core model filtering

* fix: pin SAP Cloud SDK to 4.6.0

* fix(vscode): simplify SAP AI Core model filtering

---------

Co-authored-by: David Knaack <david.knaack@sap.com>
2026-06-25 12:13:53 -07:00
Saoud Rizwan 176662ee20 fix(vscode): show pending state before queued prompt appears (#11836)
* fix(vscode): show pending state for chat sends

* fix(vscode): keep chat input editable during pending sends
2026-06-25 12:08:05 -07:00
Saoud Rizwan 309ad9da72 fix(vscode): show queued prompts while streaming (#11835)
* fix(vscode): show queued prompts while streaming

* fix(vscode): refine queued prompt updates
2026-06-25 11:54:44 -07:00
Saoud Rizwan 16a6926985 fix: improve ask option selection UI (#11824)
* fix: disable hover for selected ask options

* fix: mark ask options disabled after selection

* fix: hide duplicate ask option echoes
2026-06-25 11:51:43 -07:00
Saoud Rizwan d6db723c9d fix(vscode): keep command output pinned to bottom (#11825)
* fix(vscode): keep command output pinned to bottom

* fix(vscode): address command output scroll review
2026-06-25 11:31:54 -07:00
Tomás Barreiro ce1fc20a9f Limit the ClinePass CLI url to the CLI (#11811)
* Limit the ClinePass CLI url to the CLI

* fix tests

* Remove unused import

* Fix run-agent

* fix run-aent error message
2026-06-25 20:31:31 +02:00
Tomás Barreiro b780a5ec00 Fix ClinePass error mapping on VSCode (#11807)
* Fix ClinePass error mapping on VSCode

* refactor

* fix types

* remove unused constants

* refactor
2026-06-25 19:29:13 +02:00
Tomás Barreiro 285cd6d54c Fix vscode tests (#11831) 2026-06-25 10:16:39 -07:00
Tomás Barreiro ff845539e4 Fix createRequire (#11829) 2026-06-25 18:17:16 +02:00
Saoud Rizwan 05b0aa7dcb fix(vscode): flush state after model selection (#11822)
* fix(vscode): flush state after model selection

* fix(vscode): drain state after teardown cleanup
2026-06-25 04:13:40 -07:00
Saoud Rizwan 6cdd882acb fix(vscode): remove dead settings (#11819) 2026-06-25 03:38:47 -07:00
Saoud Rizwan d055b0941f feat(vscode): add marketplace (#11816)
* feat(vscode): add customize marketplace

* style(vscode): format marketplace imports

* fix(vscode): open marketplace mcp tab from configure

* fix(marketplace): redact authorization headers
2026-06-25 02:57:15 -07:00
Saoud Rizwan 21a0e4c4f1 fix(vscode): remove delay when sending message (#11817)
* fix(vscode): show new chat immediately on send

* fix(vscode): restore chat input after new task failure
2026-06-25 02:56:55 -07:00
Saoud Rizwan 54d022b536 fix(vscode): simplify auto-approve menu (#11814)
* fix(vscode): remove all-commands auto-approve option

* fix(vscode): clear legacy all-commands approval

* fix(vscode): remove external path auto-approve options

* Revert "fix(vscode): clear legacy all-commands approval"

This reverts commit 39af65f894.

* fix(vscode): ignore legacy all-commands approval

* fix(vscode): use all-commands auto-approve flag

* Revert "fix(vscode): use all-commands auto-approve flag"

This reverts commit 8b093ce654.
2026-06-25 02:46:07 -07:00
Dominic Cooney a1709d37e5 fix(vscode): make compact button run real SDK compaction (#11764)
* fix(vscode): make compact button run real SDK compaction

The compact button (and the typed /compact and /smol commands) sent the
literal text "/compact" to the model as a normal chat message. In the SDK
adapter only /workflow and /skill are expanded as runtime commands, so the
model received "/compact" as a prompt and improvised a fake "Conversation
Summary" without actually reducing the context window (CLINE-2503).

Wire the same SDK effect the CLI's /compact (alias /smol) uses:

- sdk-compaction.ts: compactSessionMessages(), the VSCode analog of the CLI's
  compactInteractiveMessages -- a manual-mode createContextCompactionPrepareTurn
  over the current transcript, force-enabling compaction and forwarding
  telemetry/sessionId.
- sdk-compaction-coordinator.ts: reads the active session transcript, runs the
  manual compaction, and restarts the session with the compacted messages via
  replaceActiveSession (same sequencing as a mode rebuild), preserving the
  session id and emitting a CLI-style status line. Guards no-session, mid-turn,
  and empty-transcript cases.
- SdkController.compactTask() exposes it; the condense slash handler now calls
  it instead of the no-op ask response.
- Webview: the compact-confirm button and typed /compact + /smol (with an active
  task) route to the condense RPC instead of sending literal text.

Adds unit tests for the helper, the coordinator, and the webview send routing.

* chore(vscode): drop trailing newline in condense handler (biome)

* test(vscode): cover manual compact flow

* test(vscode): use portable compact matcher

* test(vscode): assert compact calls without vitest matchers

* test(vscode): keep compact assertion type safe

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-25 02:28:07 -07:00
Saoud Rizwan b4157df509 fix(vscode): refresh Cline model info from catalog (#11806) 2026-06-25 02:20:22 -07:00
Saoud Rizwan 60dee73f0f fix(vscode): use SDK for mistake limit (#11808)
* fix(vscode): use SDK mistake-limit recovery

* test(vscode): cover mistake-limit stop response
2026-06-25 02:19:58 -07:00
Saoud Rizwan 2d66bd475e feat: add checkpoints (#11813)
* feat: add SDK-backed VS Code checkpoints

* fix: remove stale checkpoint view changes reset
2026-06-25 02:19:14 -07:00
Renee Huang a1374ae4a5 docs: add ClinePass subscription page and reorganize sidebar nav (#11672)
* docs: add ClinePass subscription page and reorganize sidebar nav

* docs: polish ClinePass copy and add cross-links

* more wording updates

* polishing

* docs: update 5x to 2-5x API rate limits

* add beta label to clinepass

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-24 22:03:39 -07:00
John Choi b8c62ecbae fix(onboarding): restore ClinePass models in onboarding (SDK parser dropped clinePass) (#11805)
* fix(onboarding): restore ClinePass models in onboarding

Root cause: the SDK's fetchClineRecommendedModels (@cline/core) silently
dropped the clinePass list. Its ClineRecommendedModelsData type and
normalizeResponse only handled recommended/free, so the recommended-models
endpoint's clinePass entries were stripped before reaching the extension.
Result: the onboarding ClinePass option appeared but the model list was always
empty ('No ClinePass models are available right now'), regardless of the
ext-cline-pass flag. This also affected any SDK consumer (CLI/JetBrains).

Also reverts the pre-login regression from #11798: that PR gated the first
onboarding screen on the extension-side clinePassEnabled flag, which is only
populated after login (featureFlagsService.poll runs on auth), so the ClinePass
option disappeared on the pre-login 'How will you use Cline?' screen.

Changes:
- @cline/core cline-recommended-models: parse/clone clinePass; include it in
  the type and offline fallback; treat clinePass-only responses as non-empty.
- OnboardingView: gate the ClinePass option on the webview useHasFeatureFlag
  (works pre-login) instead of the extension-side clinePassEnabled.
- Revert the extension-side clinePassEnabled plumbing added in #11798
  (FeatureFlagsService.getClinePassEnabled, state payload, ExtensionMessage,
  ExtensionStateContext default).

* test: add clinePass to recommended-models SDK mocks

ClineRecommendedModelsData now requires clinePass; update the mocked SDK
results in refreshClineRecommendedModels.test.ts so check-types passes.

* fix(onboarding): only offer ClinePass when models are available

Gate the ClinePass option on isClinePassEnabled AND models.clinePass.length > 0.
Previously, when the flag was on but the recommended-models request fell back
(or returned no clinePass entries), the option still appeared and routed users
into the ClinePass model step's empty state, where signup is disabled -- a dead
end instead of staying on Free/Frontier/BYOK.

* chore: trim ClinePass gate comment to one line
2026-06-24 17:41:14 -07:00
Bee 96b29d787e fix: normalize JSON-like tool inputs by schema (#11803) 2026-06-24 17:04:57 -07:00
Tomás Barreiro 5b96beb583 Link the CLI to the promo (#11794) 2026-06-25 01:48:09 +02:00
Dominic Cooney f2af0d700c refactor: simplify sdk terminal execution (#11789) 2026-06-25 08:43:41 +09:00
Tomás Barreiro a9c47f88fd Remove unused retry function (#11804) 2026-06-25 08:38:41 +09:00
John Choi 49884a1194 fix(sdk): batch outdated-read rewrites in MessageBuilder to preserve provider prefix caches (#11471)
* fix(sdk): batch outdated-read rewrites in MessageBuilder to preserve provider prefix caches

MessageBuilder previously rewrote stale read_files results to
'[outdated - see the latest file content]' eagerly on every re-read.
Each rewrite mutates bytes in the middle of the provider-facing
transcript, invalidating provider prefix caches (DeepSeek/Anthropic/
MiniMax-style) from that message to the end of the conversation. Agents
re-read files constantly (read -> edit -> verify), so long sessions paid
full uncached input price on most requests.

Now pending outdated rewrites accumulate and only commit once the total
reclaimable bytes cross a 64KB threshold, then apply as a single batch
(one cache break amortized over a large context saving). Committed
rewrites are sticky so subsequent requests stay byte-stable.

* fix(sdk): count only reclaimable locator bytes when batching outdated-read rewrites

Addresses review feedback (greptile P1, codex P2): pendingBytes was
incremented with the whole tool-result block size once per outdated
locator, so multi-file read_files results were overcounted (N stale
locators = N x block bytes), crossing the batch threshold far earlier
than intended and partially defeating the cache-stability guarantee.

Now estimateOutdatedReclaimBytes attributes bytes per stale entry in
the parsed read result (falling back to full text size only when the
whole block is outdated, matching replaceOutdatedReadContent), counted
once per block. Adds a multi-locator regression test where a 3-file
read result is invalidated file-by-file and must only commit when the
actual reclaimable bytes cross the threshold.

Real-session replay improved from 12.6% to 19.7% net-token reduction
with the accurate counting (commits defer longer, breaks amortize
better).

* fix(sdk): drop committed outdated rewrites when history is rolled back

Addresses review P1: committedOutdatedRewrites survived checkpoint
restore/clearHistory (the orchestrator reuses one MessageBuilder), so a
read that became the latest again after rollback stayed rewritten to
'[outdated...]' forever, hiding live file content from the provider.

Two guards: re-validate committed locators against the current index at
apply time, and clear the committed set in resetIndexes — that path only
fires on non-append-only history changes, where the provider prefix is
already broken, so stickiness loses nothing.

Adds a rollback regression test (commit rewrite, restore to before the
re-read, assert full content returns).

* test(sdk): trim redundant comments in rollback regression test

* fix(sdk): keep outdated-rewrite batching state across fresh message rebuilds

Addresses review feedback: the runtime provider path rebuilds Message
objects every request (agentMessagesToMessages constructs new literals),
so the identity-based reindex check fails each build and resetIndexes
fires. Clearing committedOutdatedRewrites there (added for the rollback
P1) recounted already-committed bytes as pending on every request — once
the first 64KB committed, every newly-stale small read rewrote
immediately, degenerating to eager behavior in steady state.

committedOutdatedRewrites now survives resetIndexes. Rollback
correctness is preserved without it: the apply-time re-validation is
identity-free, and commitOutdatedRewrites now prunes committed locators
that are no longer outdated in the current index plus entries whose
tool_use_id left the transcript. Both prunes are no-ops in append-only
growth since outdatedness is monotonic.

Adds two regression tests that route messages through the real
agent-message codec round-trip (fresh objects per build, as production):
steady-state deferral of a small newly-stale read after a committed
large one, and rollback restoring full content.

* fix(sdk): batch orphaned read results and count stale image bytes

Addresses robinnewhouse review (two pre-approval follow-ups):

1. Tool-name lookups went through toolNameByIdCache only, so a
   tool_result orphaned by compaction/rollback (paired tool_use gone)
   was invisible to the batching scan and pruned from committed state —
   reverting its rewrite mid-transcript in exactly the history-shrinking
   case the batching needs to survive. resolveToolName now falls back to
   tool_result.name at all three lookup sites (transform, reindex,
   commit scan).

2. estimateOutdatedReclaimBytes attributed only text/file entries, but
   replaceOutdatedReadContent also replaces stale image siblings
   (flagged by codex too). Image-heavy sessions accrued ~0 pending bytes
   and never crossed the threshold. The estimator now counts stale image
   payload bytes using the same positional marker counting as the
   rewriter (countOutdatedImageEntries).

Both regression tests fail before this change: orphaned result keeps
its committed rewrite through a codec round-trip, and a 4KB stale image
crosses a 2KB threshold that its ~70-byte text marker alone would not.

* perf(sdk): retune outdated-rewrite threshold to 128KB for executor caps

The 64KB default was calibrated before executor-layer output caps landed
(#11480/#11504: read_files/run_commands/search now cap at 48K chars).
With reads bounded at ~48K, 64KB sat awkwardly — one stale read can't
cross it, two overshoot — making it the worst non-extreme threshold in a
post-cap cost sweep.

Re-measured eager vs batched on 48K-capped transcripts (DeepSeek 10x
cache pricing): batching still beats eager 44-61%, confirming the
mechanism remains valuable after the caps (never-rewrite is now +35%
worse in long sessions). 128KB (~2-3 capped reads) is cheapest in both
short and long shapes, ~5-12 points better than the old 64KB.

Bumps LARGE_CONTENT test fixture to ~140KB so single-large-read commit
tests still exceed the raised threshold.

* fix(sdk): batch structured read tool results

* fix(sdk): resolve orphaned tool names for aggregate truncation

* test(sdk): trim redundant message builder cache tests

* style(sdk): trim message builder comments

* test(sdk): allow schedule history test more time on windows

* fix(sdk): preserve infinity outdated rewrite threshold

* perf(sdk): retune outdated rewrite threshold to 64KB

* Revert "test(sdk): allow schedule history test more time on windows"

This reverts commit ac21ef1702.

* test(sdk): fold message builder cache stability coverage

* fix(sdk): address stale read batching review
2026-06-24 16:17:34 -07:00
Dominic Cooney 49da86f60c chore: update biome vscode settings (#11788) 2026-06-25 08:16:50 +09:00
John Choi 84477dd84f fix(onboarding): gate ClinePass on reliable extension-side flag (#11798)
* fix(onboarding): show ClinePass models reliably + label the group ClinePass

Two issues:

1. Nightly feature-flag race. ClinePass was gated twice by two different flag
   clients: the recommended-models endpoint is server-gated by ext-cline-pass
   (PostHog-node), while the webview independently re-checked ext-cline-pass via
   PostHog-js to decide whether to show the option and keep the models. These
   reads race and disagree (mid auth/identify handshake, or when PostHog
   remote-config scripts are blocked by the webview CSP), so the ClinePass
   option could appear with an empty model list.

   Fix: make the server-gated payload the single source of truth. Onboarding
   shows the ClinePass option iff the payload contains ClinePass models
   (getUserTypeSelections now takes hasClinePassModels), and
   getRecommendedModelsData no longer re-filters response.clinePass on the
   webview flag. Removes the second racy webview PostHog read entirely.

2. Group label. The ClinePass group rendered as the raw provider id (CLINE-PASS).
   Render it with the product's proper casing (ClinePass). Model ids/names are
   intentionally left as-is (e.g. cline-pass/minimax-m3), since that's what the
   model is called.

* fix(onboarding): gate ClinePass on reliable extension-side flag

The ext-cline-pass flag is rolled out to internal cohorts only (QA/Cline
team/ClinePass Beta), not GA. Onboarding read it via the webview posthog-js
client, which is unreliable during onboarding (CSP blocks PostHog remote
config in Nightly, and it evaluates before auth/identify resolves) -- so
eligible team members saw ClinePass with an empty list / not at all.

Read the flag from the extension-side featureFlagsService instead (the same
server-evaluated source Settings/catalog already use), plumbed into webview
state like worktreesEnabled. Onboarding now shows ClinePass iff the flag is
enabled AND the payload contains ClinePass models, so the option and the
list are always in sync.

- FeatureFlagsService.getClinePassEnabled()
- getStateToPostToWebview: clinePassEnabled
- ExtensionState type + webview default
- OnboardingView gates on state.clinePassEnabled
2026-06-24 16:15:53 -07:00
Saoud Rizwan bd662d81f6 fix: bundle SAP AI Core provider auth (#11796)
* fix: bundle SAP AI Core provider auth

* fix: serialize SAP service-key auth calls
2026-06-24 13:45:00 -07:00
Tran Binh Minh d8a3086eaa docs(mcp): set type=streamableHttp in remote server example (#11670) (#11690)
The remote-server JSON example omitted the `type` field. Because the
config schema's z.union lists the SSE branch before streamableHttp
(intentionally, for backward compat), an untyped remote entry silently
resolves to the deprecated legacy SSE transport — the opposite of the
docs' own "Streamable HTTP (recommended)" guidance.

Add `"type": "streamableHttp"` to the example, rename the heading to
match, and add a sentence explaining that omitting `type` defaults to
legacy SSE.

Fixes #11670

Co-authored-by: Minhkunn <minh.12072k6@gmail.com>
2026-06-24 20:59:31 +02:00
Tomás Barreiro 8ecd136e52 Update the clinePass model list live (#11792)
* Generate the model list dynamically

* Do not return known models

* Make both calls in parallel

* Remove modelsDev catch on model generation

* readd error catching
2026-06-24 20:44:36 +02:00
Robin Newhouse 14a28b0559 fix(core): avoid nullable editor old_text schema (#11784) 2026-06-24 04:31:01 -07:00
Dominic Cooney c5378d1847 Merge pull request #11777 from cline/dpc/sdk-migration-simpler-login
SDK migration: move apps/vscode to bun + Cline SDK

### Description

This is the integration branch that moves the VSCode extension onto the Cline SDK and the bun toolchain. Major facts:

- **`apps/vscode` now runs on the Cline SDK.** The extension consumes `@cline/core`, `@cline/llms`, and `@cline/shared` through an adapter layer in `apps/vscode/src/sdk/` (single codepath — no `CLINE_SDK` flag). The webview still talks gRPC; the adapter translates between the gRPC handlers and SDK calls.
- **`apps/vscode` is folded into the root bun workspace.** Package management and task running move from npm/node to **bun**; the extension links the local `@cline/*` packages via `workspace:*` instead of pinned published versions. **Node remains the runtime** (extension host, standalone `cline-core`, esbuild `platform: node`, prebuild ABI targets).
- **npm lockfiles deleted; root `bun.lock` is authoritative** (`apps/vscode`, `webview-ui`, and `testing-platform` per-package lockfiles removed).
- **CI updated** for the new layout: the `ext-vscode-*` workflows install once at the root with bun and build the SDK before the extension build.
- **VSCode extension version bumped to `4.0.0`.**

### Test Procedure

Validated locally before opening:

- `bun run lint` — clean.
- Typechecks across SDK packages, `@cline/cli`, `@cline/cline-hub`, plus `apps/vscode` extension + webview `tsc` — all clean.
- Extension esbuild bundle and both webviews (`apps/vscode/webview-ui`, `apps/cline-hub`) build.
- Unit suites: `apps/vscode` bun-unit (932 pass), webview-ui vitest (247 pass), and SDK package suites (llms 323, agents 41, shared 202) pass.

Watching CI here for the authoritative cross-platform signal.

### Type of Change

-   [x]  New feature (non-breaking change which adds functionality)
-   [x] ♻️ Refactor Changes
-   [x] 🏃 Workflow Changes

### Pre-flight Checklist

-   [ ] Changes are limited to a single feature, bugfix or chore (split larger changes into separate PRs)
-   [x] Tests are passing (`bun test`) and code is formatted and linted (`bun run format && bun run lint`)
-   [x] I have reviewed contributor guidelines
2026-06-24 15:46:39 +09:00
Dominic Cooney b5388e5c8d chore(vscode): bump version to 4.0.0 2026-06-24 14:48:03 +09:00
Saoud Rizwan 65b3977bc5 fix(vscode): forward OCA reasoning effort to SDK sessions (#11739) 2026-06-24 14:43:39 +09:00
Dominic Cooney f4a46cf8e7 fix(deps): drop global vite override so cline-hub webview keeps vite 8
The bun migration relocated apps/vscode/webview-ui overrides to the root
package.json, including vite ^7.1.11. As a workspace-wide override this
forced vite 7 onto apps/cline-hub/src/webview, which targets vite 8 and
uses rolldownOptions in its vite.config.ts. That broke `bun run -F
@cline/cli build` (cline-hub build:webview) with TS2769 on rolldownOptions.

Removing the global override lets each workspace resolve its declared
vite: webview-ui stays on vite 7.3.5, cline-hub resolves vite 8.0.16.
Both webviews build and the webview-ui vitest suite (247 tests) passes.
2026-06-24 14:36:34 +09:00
Dominic Cooney 0b47033a0f fix(core): suppress cross-package import lint in SAP handler-factory test 2026-06-24 14:23:23 +09:00
Saoud Rizwan c94ef4b750 fix(vscode): revert OpenAI-compatible metadata limit plumbing (#11775)
* fix(vscode): stop deriving output limits from model metadata

* fix(vscode): send OpenAI-compatible output token limit (#11776)
2026-06-24 14:12:00 +09:00
Tomás Barreiro 3e855f7d3f Fix other instances of issues with the litellm model list (#11773)
* Fix other instances of issues with the litellm model list

* address comment
2026-06-24 14:12:00 +09:00
Saoud Rizwan 2cd062ce68 fix(vscode): keep model metadata out of provider settings (#11772)
* fix(vscode): keep model metadata out of provider settings

* fix(vscode): prune stale provider model metadata

* docs(vscode): explain provider metadata pruning
2026-06-24 14:12:00 +09:00
Tomás Barreiro be6999209e Prevent injecting other models into the LiteLLM model list (#11771) 2026-06-24 14:12:00 +09:00
Saoud Rizwan 9a1e6121c7 fix(llms): align SAP AI Core provider config (#11759)
* fix(core): align SAP AI Core mode config

* fix(llms): map SAP AI Core credentials to service binding
2026-06-24 14:12:00 +09:00
Tomás Barreiro 4ae0ff4d5a Build the SDK sourcemaps (#11757)
* Build the SDK sourcemaps

* Do not minify

* do not minify packages when building sourcemaps
2026-06-24 14:12:00 +09:00
BarreiroT 18737f1448 Map ClinePass model information 2026-06-24 14:12:00 +09:00
Saoud Rizwan b56ce72fc7 fix(core): forward SAP provider options to gateway (#11756) 2026-06-24 14:11:59 +09:00
Max 425182c7c0 Remove chat scroll action button (#11734)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:59 +09:00
Max 30fe302a71 fix retry after cline login issue (#11646)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:59 +09:00
BarreiroT eadbc09ffd Update generated models 2026-06-24 14:11:59 +09:00
BarreiroT 3178487bdd fix cline-pass options 2026-06-24 14:11:59 +09:00
Saoud Rizwan f58941c500 fix(core): add OCA legacy reasoning effort (#11746) 2026-06-24 14:11:59 +09:00
Saoud Rizwan 8da5ffa874 fix: wire up SAP provider (#11745)
* fix(vscode): wire SAP AI Core session config

* fix(vscode): remove redundant SAP base URL mapping
2026-06-24 14:11:59 +09:00
Dominic Cooney 9ed95d1dc2 fix(llms): restore provider-request capture wiring lost in SDK migration 2026-06-24 14:11:59 +09:00
Dominic Cooney 5fc7341312 chore: regenerate bun.lock after rebase onto main 2026-06-24 14:11:37 +09:00
Dominic Cooney d5b38f38cb fix(vscode): preserve OrgClinePass error UI through SDK rebase 2026-06-24 14:11:37 +09:00
Tomás Barreiro 386401b41f Identfy accounts for feature flag resolution (#11741)
* Identify accounts for Feature Flag resolution

* simply code
2026-06-24 14:11:37 +09:00
Max 6471d2475a if search result is undefined then don't crash the extension (#11733)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:37 +09:00
Max Paulus 🥪 fada079558 remove gap between approve bar and input box 2026-06-24 14:11:37 +09:00
BarreiroT 31d50ff54a Add log 2026-06-24 14:11:37 +09:00
BarreiroT efebc6380b Fix imports 2026-06-24 14:11:36 +09:00
Saoud Rizwan 2acc25cf6e test(vscode): raise vitest testTimeout to 20s to fix import-cost flakes (#11729)
Several vitest suites lazily await import() their subject inside the first
test (so vi.mock factories apply first). That import pulls in heavy workspace
packages (@cline/core, @cline/llms, @cline/shared), and on loaded CI runners
the first test in a file intermittently exceeds the 5s default timeout and
fails the nightly (observed in catalog.test.ts, now resolveModelInfo.test.ts).
Set a global 20s testTimeout so import cost attributed to the first test does
not cause flakes.
2026-06-24 14:11:36 +09:00
Saoud Rizwan 1ebe99c676 test(vscode): add invalidateProviderListings to auth-service mock controllers (#11727)
#11720 (feature flag resolution on startup) added a
controller.invalidateProviderListings() call to AuthService.sendAuthStatusUpdate
but did not update the test's mock controllers, which only stubbed
postStateToWebview. The new call threw on the mocks, so the throw happened
before postStateToWebview ran (failing the 'polls feature flags' test) and
caused subscribeToAuthStatusUpdate to delete the handler in its catch block
(failing the 'removes subscription on cleanup' test). Add the now-required
invalidateProviderListings stub to the mock controllers.
2026-06-24 14:11:36 +09:00
BarreiroT f5bbbd7f08 Log feature flags 2026-06-24 14:11:36 +09:00
Saoud Rizwan 8e4a2b8a19 fix(sdk): repair exposed provider auth routing (#11721)
* fix(sdk): repair exposed provider auth routing

* fix(sdk): use accessible ZAI coding plan default

* fix(sdk): use live Poolside model default

* docs(vscode): explain SDK provider key fallback
2026-06-24 14:11:36 +09:00
Tomás Barreiro dfdeffb558 Fix ModelAutocomplete selection (#11718) 2026-06-24 14:11:36 +09:00
Tomás Barreiro 1d22a51e30 Fix Feature Flag resolution on startup (#11720)
* Fix Feature Flag resolution on startup

* remove irrelevant test
2026-06-24 14:11:36 +09:00
Saoud Rizwan c827537386 test(vscode): use toBe instead of toMatchObject in proto conversion test (#11712)
api-configuration-conversion.test.ts is picked up by both the vitest
runner and the mocha-based vscode-test integration runner (.vscode-test.mjs
globs src/shared/**/*.test.js). vitest's jest-compat matcher toMatchObject
does not exist in the mocha runtime, so the test passed under vitest but
threw "toMatchObject is not a function" in the integration suite, failing
the nightly publish. Assert the two provider fields with toBe, which works
under both runners.
2026-06-24 14:11:36 +09:00
Saoud Rizwan f73121e369 test(vscode): warm catalog import to fix flaky 5s timeout (#11711)
The first test in catalog.test.ts paid the cost of dynamically importing
./catalog (which pulls in @cline/core, @cline/llms and @cline/shared)
inside its own 5s test timeout, intermittently failing CI/nightly runs.
Warm the import once in beforeAll so the cost falls outside any per-test
clock.
2026-06-24 14:11:36 +09:00
Saoud Rizwan 52bbda183d feat(vscode): expose additional SDK providers (#11703)
* feat(vscode): expose additional SDK providers

* fix(vscode): reserve skipped provider enum slots

* fix(vscode): keep Z.AI Coding Plan provider-specific
2026-06-24 14:11:36 +09:00
Saoud Rizwan 80036cef44 fix(sdk): route LiteLLM model fetches through SDK (#11705)
* fix(vscode): improve LiteLLM model fetch errors

* fix(vscode): align LiteLLM fetch return contract

* fix(sdk): improve LiteLLM private model fetch

* chore(vscode): drop duplicate LiteLLM fetch changes

* fix(vscode): route LiteLLM refresh through SDK
2026-06-24 14:11:36 +09:00
Saoud Rizwan b37f872a1f fix(vscode): honor OpenAI-compatible model settings (#11710)
* fix(vscode): honor OpenAI-compatible model settings

* fix(vscode): simplify OpenAI-compatible model bridge

* fix(vscode): respect OpenAI-compatible image support
2026-06-24 14:11:35 +09:00
Max ebe2b6498b fix(vscode): use Codex OAuth credentials (#11691) 2026-06-24 14:11:35 +09:00
BarreiroT 365250c785 Fix tests 2026-06-24 14:11:35 +09:00
BarreiroT 1e208472da fix tests 2026-06-24 14:11:35 +09:00
Tomás Barreiro ee974ea863 Fix ClinePass auth (#11680)
* Return local providers with ClinePass in the new extension

* Fix import

* Fix ClinePass auth
2026-06-24 14:11:35 +09:00
Tomás Barreiro a6416d02c8 Return local providers with ClinePass in the new extension (#11678)
* Return local providers with ClinePass in the new extension

* Fix import
2026-06-24 14:11:35 +09:00
Max Paulus 🥪 55cfeaec65 fix broken CI tests 2026-06-24 14:11:35 +09:00
Max Paulus 🥪 acfe779b20 fix local build not picking up .env file 2026-06-24 14:11:35 +09:00
Max Paulus 🥪 cff8c22f06 update vscode ignore
vsix bundling failed because of some unignored files
2026-06-24 14:11:35 +09:00
Max Paulus 🥪 8f2be20858 fix broken integ tests 2026-06-24 14:11:35 +09:00
Max Paulus 🥪 f1fd189631 remove storage tests from vitest
- these run under node resolution so they can see "bun:test" imports.
- these tests will get run by scripts/run-bun-unit-tests.ts instead
2026-06-24 14:11:34 +09:00
Cline Agent 9b03cb878d fix: repair SDK ClinePass webview rebase
Restore the webview feature-flag hook needed by the ClinePass onboarding/settings UI, but implement it against the existing posthog singleton instead of posthog-js/react so tests do not pull in a second React copy.

Make ClinePass settings follow the SDK provider-catalog pattern: render the Cline account card, resolve models with useProviderModels("cline-pass"), and persist selections with useProviderConfig/useProviderModelSelection for providerId="cline-pass". Remove the stale origin/main props that tried to drive the SDK-era ClineModelPicker, which is intentionally Cline-provider specific.

ClinePass remains hidden by the ext-cline-pass flag in settings/onboarding, and its model info hides token usage costs because billing is subscription-based.
2026-06-24 14:11:34 +09:00
Cline Agent 3dc020185f fix: post-rebase ClinePass plumbing for SDK migration
Resolve type-check and test breakages from rebasing the ClinePass
feature (origin/main) onto the SDK migration branch:

- provider-keys: re-add cline-pass to ProviderKeyMap and
  NON_SDK_PROVIDER_DEFAULTS (removed by the 'remove unused code'
  commit which predated ClinePass), so getProviderModelIdKey and
  getProviderDefaultModelId handle the cline-pass provider.
- provider-id: register 'cline-pass' in KNOWN_API_PROVIDERS so the
  Record<ApiProvider, true> constraint is satisfied.
- refreshClineRecommendedModels: add optional 'clinePass' field to
  ClineRecommendedModelsData so the RPC handler can map it into the
  proto response without a type error.
- refreshClineRecommendedModelsRpc: guard models.clinePass with ?? []
  for the same reason.
- handleClinePassProviderSelection: pass undefined (not null) to
  accountService.switchAccount to match the SDK signature.
- provider-keys.test: remove a duplicate closing brace left by the
  conflict resolution.
- Biome formatting (asNeeded semicolons) applied by check-types.
2026-06-24 14:11:34 +09:00
Dominic Cooney 4829f08b3f fix(vscode): reliable MCP OAuth on the SDK extension (ENG-2108, CLINE-2304) (#11529)
* fix(vscode): store MCP OAuth in shared settings file like the CLI (ENG-2108)

VSCode stored MCP OAuth tokens in a single mcpOAuthSecrets secrets blob
keyed by sha256(name:url), while the CLI/SDK store per-server oauth state in
cline_mcp_settings.json. The two never interoperated (CLI auth was invisible to
VSCode), and VSCode's read-whole-blob/write-whole-blob through StateManager's
non-refreshing cache meant concurrent windows clobbered each other's tokens.

- Store MCP OAuth state in the shared settings file in @cline/core's format.
- Reads are fresh from disk; writes are scoped read-modify-write of one
  server's oauth key via updateMcpServerOAuthState (now atomic temp+rename).
- Replace the vscode:// callback flow with HTTP-based token collection via
  authorizeMcpServerOAuth (same local loopback flow the CLI uses).
- Reconnect an unauthenticated server when its tokens appear (e.g. CLI auth).
- One-time migration of legacy mcpOAuthSecrets tokens into the shared file.
- Remove McpOAuthRedirectResolver, mcpOAuthFlow, completeOAuth, and the
  mcp-auth URI callback route.

* feat(vscode): add --instances/--random-port to MCP OAuth test server

Lets you start several independent test servers, each on its own OS-assigned
random port, so you can add multiple streamableHttp MCP servers to Cline at
once and exercise concurrent OAuth flows. baseUrl now reflects the actually
bound port so discovery metadata and redirect URIs stay correct under random
ports.

* fix(vscode): stop MCP OAuth handshake writes from livelocking the settings watcher (ENG-2108)

Now that codeVerifier/clientInformation live in the shared settings file, the
MCP SDK's per-connect-attempt saveCodeVerifier() writes were tripping the
settings watcher, which re-entered updateServerConnections -> connectToServer
-> another write, looping forever. It was especially bad with two+
unauthenticated servers, where each server's verifier churn re-triggered the
other (visible as a flickering, ever-changing codeVerifier nonce).

The watcher now compares a connection-relevant fingerprint (full per-server
config minus the oauth block, plus a boolean for whether an access token
exists) and skips writes that only churn OAuth-handshake fields. A token
appearing/disappearing still changes the fingerprint, so CLI/other-window
authorization continues to trigger a reconnect via serverGainedOAuthTokens.

* feat(vscode): print paste-ready MCP settings fragment from OAuth test server

On startup the test server now emits an mcpServers JSON fragment (nested
transport shape, matching cline_mcp_settings.json) alongside the banner, so you
can paste it straight into the settings file instead of hand-writing it. With
--instances the entries get distinct names (oauth-test-1, ...), each carrying
its actual bound port.

* fix(vscode): atomic MCP settings writes + fingerprint gate; drop timer guards (CLINE-2097)

Deleting one MCP server could empty the whole list. Root cause: settings
writes were non-atomic (fs.writeFile), so chokidar (and any other process)
could read a transient empty/torn file mid-write and reconcile to zero servers.
The previous fix only masked this with a per-process isUpdatingClineSettings
boolean cleared on a 300ms timer — it did nothing for the CLI or other windows
and was racy.

Replace both timer guards (isUpdatingClineSettings, isUpdatingFromRemoteConfig)
with two deterministic, process-agnostic mechanisms:

- writeSettingsFile(): atomic temp-file + rename for every settings write, so
  any reader always sees a complete file. Holds for any number of concurrent
  writers (CLI, multiple windows, SDK OAuth handshake).
- content fingerprint: the watcher reconciles only when the connection-relevant
  view changed. writeSettingsFile pre-seeds the fingerprint so our own write is
  a no-op, while a genuine change from any other process is still processed.
  Because reconcile is idempotent and reads are never torn, a missed
  suppression is at worst a redundant reconnect, never data loss.

All RPC writers (toggle disabled, autoApprove x2, timeout, add, delete) and the
remote-config sync now go through writeSettingsFile. Removes all setTimeout(.,
300) flag juggling.

* feat(vscode): add a non-guessable 'frozzle' tool to the MCP OAuth test server

The MCP OAuth test server now serves tools/list + tools/call exposing a
'frozzle' tool whose output cannot be derived without calling it (reverse the
string and swap each letter's case, wrapped in guillemets). This gives an eval
a reliable end-to-end signal that the OAuth-authenticated MCP round-trip really
happened: a correct 'frozzle <text>' answer can't be hallucinated. The
transform is easy to verify at a glance and invertible. Adds frozzle.test.ts.

* fix(sdk): drop lingering OAuth callback sockets on close so deny->approve re-auth works (ENG-2108)

The local OAuth callback server's close() called Server.close(), which only
stops accepting new connections and lets existing keep-alive sockets linger.
The browser / global-fetch connection pool keeps such a socket to the fixed
callback port (1456) alive. So after the user denied an MCP OAuth request and
retried, the retry's approve callback could be delivered over the pooled socket
to the FIRST (already-settled) server. That server's settle() was a no-op, so
waitForCallback() never resolved, finishAuth()/token exchange never ran, and no
token was saved — the server stayed unauthenticated (the deny->approve repro).

Call server.closeAllConnections() in close() so no pooled socket outlives the
server. Adds a regression test driving a keep-alive agent across close().

* fix(vscode): actually reconnect MCP server when toggled back on (ENG-2108)

toggleServerDisabledRPC only flipped the in-memory disabled flag and set status
to 'connecting', but never rebuilt the connection. A disabled server's
connection has no live transport/client, so re-enabling left it stuck on the
yellow 'connecting' indicator forever and never re-advertised its tools to the
agent.

Tear down and rebuild the connection through deleteConnection + connectToServer
(which opens a real transport when enabled, or a disconnected stub when
disabled), then notifyWebviewOfServerChanges so the SDK session's tool list is
refreshed. OAuth state is preserved (deleteConnection doesn't clear it). Adds
McpHub.toggleServerDisabledRPC.test.ts.

* fix(vscode): reload MCP tools silently without chat spam (ENG-2108)

Restarting the SDK session to pick up MCP tool changes appended visible chat
messages ('MCP tools changed - reloading...' and 'MCP tools reloaded
successfully...') plus a completion_result banner. Toggling several servers
piled up many of these. Tool reloading should be transparent.

Emit only the session status transitions (running -> idle) via
emitSessionEvents([], ...) instead of appendAndEmit, so no chat messages or
completion banner are shown. Genuine reload failures still surface an error
message. Updates sdk-mcp-coordinator.test.ts accordingly.

* docs(mcp): clean up comments to describe current behavior

Revise comments across the MCP OAuth and settings code to document the code as
it stands, dropping references to prior implementations, task IDs, and
before/after narration. Also reflow the auth-server regression test to the
repository's formatter. No behavior change.

* fix(vscode): atomic fallback write in remote MCP sync; document sync OAuth I/O

Make the no-McpHub branch of syncRemoteMcpServersToSettings write via an
atomic temp-file + rename so a concurrent reader never observes a torn or
empty settings file, matching every other settings write.

Document why the OAuth state read-modify-write in McpOAuthManager is
synchronous: it serializes this process's shared-file updates without a
Promise queue, which we prefer over async I/O for reliability of the
cross-process settings file.

* fix(mcp): serialize settings read-modify-writes

* docs(vscode): clarify MCP settings create race

* fix(vscode): create MCP settings atomically

* fix(cli): keep clearing missing MCP OAuth state a no-op

* fix(vscode): avoid yielding while holding MCP settings lock (#11596)

* fix(mcp): async lock acquisition for VSCode MCP settings/OAuth writes

Add updateMcpSettingsFile/updateMcpServerOAuthStateAsync to @cline/core that
yield the event loop while acquiring the cross-process settings lock instead of
blocking it with Atomics.wait. The critical section stays synchronous and the
mutator stays pure, so the lock is never held across an await and serialization
is preserved without an in-process queue.

Route the VSCode extension host's OAuth state writes (McpOAuthManager) through
the async variant so a connection-time OAuth callback can no longer freeze the
extension host event loop or deadlock against an in-flight updateMcpSettingsFile
whose lock-releasing continuation needs the loop.

Unify the sync and async acquisition paths on a shared reentrancy guard
(activeLocks) so a nested settings update on the same file fails fast instead of
self-deadlocking.

Tests: contended async serialization asserting zero Atomics.wait calls, async
stale-lock reclaim, reentrancy fail-fast, and uncontended run+release.

* fix(mcp): bootstrap missing settings file inside the lock; tidy docs

Creating the MCP settings file now happens in one place: the locked
read-modify-write helpers. A missing file reads as an empty settings object, so
the first write to a fresh path (e.g. a fresh-install `cline mcp add`) creates
it inside the lock instead of throwing ENOENT. The SDK (updateMcpSettingsFile /
updateMcpSettingsFileSync) and the VSCode lock helper share this contract, so
callers no longer need to pre-create the file. Add regression tests for the
SDK, the CLI wizard addServer(), and the VSCode helper on a missing path.

Also flag the synchronous SDK entry points (updateMcpSettingsFileSync,
updateMcpServerOAuthState) as preferring their async siblings, with a TODO to
delete them once all callers migrate, and tighten the lock-helper doc comments
to describe current behavior.

* fix(vscode): finish npm->bun migration in dev tooling, tasks, and docs

The npm->bun migration (#11632) updated package scripts, .vscodeignore and .vscode-test.mjs but left a trail of npm/npx/node invocations in editor configs, dev scripts, and docs. Following the breadcrumbs from 'npm run protos':

- .vscode/launch.json: standalone-core debug uses 'bun <file>.ts' (was npx tsx); Open Storybook uses 'bun run' (was npm run).
- .vscode/tasks.json: all task commands use 'bun run' (was npm run).
- scripts/run-extension-host.sh and .claude/hooks/claude-code-for-web-setup.sh: 'bun run' (was npm run).
- debug-harness/server.ts: shebang 'bun'; build steps use 'bun run protos', 'bun esbuild.mjs', 'bunx vite build' (were npm/node/npx).
- dev script shebangs (test-hostbridge-server, test-standalone-core-api-server, testing-platform-orchestrator, interactive-playwright): '#!/usr/bin/env bun' (was npx tsx).
- WebviewProvider HMR hint, e2e README, copilot-instructions, PR template, mcp-oauth-test-server docs, generate-state-proto message, tsconfig.test comment, state-keys test comment: bun.

Left untouched (correct per .clinerules/bun-and-node): Node-runtime invocations (node build.mjs), prebuild-install --target=<node>, vsce, 'npm install -g cline' (user CLI install), and App.stories.tsx mock chat fixtures.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-06-24 14:11:34 +09:00
Dominic Cooney 82d1846a45 Migrate apps/vscode from npm/node to bun (#11632)
* chore(vscode): migrate package management & build from npm/node to bun

Fold apps/vscode (+ webview-ui, testing-platform) into the root bun
workspace so the extension consumes the local @cline/* SDK packages via
workspace symlinks instead of pinned published versions, eliminating the
SDK vendoring cycle. Node remains the runtime (extension host, standalone
cline-core, esbuild platform:node, prebuild-install ABI target).

- root: drop "!apps/vscode", add nested members, relocate overrides to
  root, add trustedDependencies [better-sqlite3, grpc-tools]
- apps/vscode: @cline/* -> workspace:*, scripts -> bun/bunx,
  npm-run-all -> bun --parallel, drop cross-env; keep esbuild + vite;
  declare previously-hoisted phantom deps (nice-grpc-common, playwright)
- package-standalone.mjs: npm install -> bun install (isolated dist dir)
- CI: setup-bun + single root bun install --frozen-lockfile, build:sdk
  before extension build, better-sqlite3 binary + zero-test guards;
  publish workflows intentionally keep setup-node for vsce/ovsx
- docs/comments: curated pass (keep-list vs rewrite-list), add
  apps/vscode/docs/bun-migration-notes.md guard doc
- delete npm lockfiles (root bun.lock authoritative)

Deferred to follow-up PRs: test-runner migration to bun test (Phase 4)
and devDep cleanup (Phase 6).

* test(vscode): add bun test foundation for the vitest-native unit suites

Phase 4a of the test-runner migration. Adds a bun test runner that
reaches full parity (582 pass / 0 fail / 50 files) with the existing
vitest SDK-adapter + model-catalog suite, without touching the
@vscode/test-cli integration tests or the webview vitest suite.

- bunfig.toml: [test] preload
- src/test/bun-test-preload.ts: mock.module() shadows `vscode` and
  `@cline/core` with their unit-test stubs (bun's onResolve plugin hook
  does not intercept host/symlinked specifiers); seeds real @cline/core
  export names as undefined to satisfy bun's strict ESM named-import
  linking; full vitest->bun:test shim (vi.fn/mocked/spyOn, describe/it/
  expect/before*/after*)
- scripts/run-bun-tests.ts: mirrors vitest.config.ts include[] exactly and
  runs with --parallel for per-file mock isolation (bun test's single-process
  default lets mock.module clobber across files)
- test:bun script

* test(vscode): migrate node-side unit suite from mocha to bun test

Phase 4b of the test-runner migration. The standalone mocha unit runner
(.mocharc spec: __tests__/* + test/services/**) was already broken under
bun (mocha was a phantom dependency — only @types/mocha/ts-node were
declared, npm hoisted mocha transitively). Migrate it to `bun test`.

- codemod 77 files: import { ... } from "mocha" -> "bun:test", renaming
  before->beforeAll / after->afterAll at imports and call-sites; chai,
  should and sinon kept as libraries (they work under bun test)
- convert sinon.stub() on ESM namespace exports to mock.module()/spyOn
  (bun loads real ESM: "ES Modules cannot be stubbed")
- scripts/run-bun-unit-tests.ts: runs the .mocharc spec set with one
  isolated `bun test` process per file (Bun.spawn + concurrency pool),
  restoring vitest-forks module-registry isolation (bun's single-process
  default lets mock.module leak across files)
- scripts/codemod-mocha-{to-bun,this}.ts: one-shot migration tooling
- test:unit now runs the bun unit runner; CI calls bun + a non-zero
  pass-count guard instead of `bunx nyc ... mocha`
- tsconfig: add root node_modules/@types to typeRoots so `bun:test`
  types resolve under tsc; cast loose os.userInfo mocks in shell.test

Result: unit suite 58 files / 880 pass / 0 fail; vitest set still
582/0. @vscode/test-cli integration tests and webview vitest unchanged.

* chore(vscode): remove dead mocha-runner deps and artifacts

Phase 6 cleanup after the bun test migration. The standalone mocha unit
runner is gone (replaced by scripts/run-bun-unit-tests.ts), so its
config and now-unused devDependencies are removed.

- remove dead files: .mocharc.json, tsconfig.unit-test.json,
  src/test/requires.ts, .nycrc.unit.json
- remove unused devDeps: @types/mocha, @types/proxyquire, ts-node,
  tsconfig-paths, cross-env, npm-run-all, nyc, proxyquire, husky
  (root owns the husky hook; chai/should/sinon stay — used as libs)
- install:all -> single root `bun install` (workspace covers webview-ui)
- drop .mocharc.json / .nycrc*.json from CI paths-filters and
  .vscodeignore; add bunfig.toml to the filters

Verified: check-types clean, unit 880/0, vitest 582/0.

* fix(vscode): import bun:test globals in tests that relied on ambient @types/mocha

CI Quality Checks (clean `bun install` without @types/mocha) surfaced
TS2582/TS2304 "Cannot find name 'describe'/'it'/'beforeEach'" in test
files that used the global mocha/jest test functions without importing
them. The Phase 4b codemod only rewrote files that imported from
"mocha"; these used ambient globals, so they were missed (and passed
locally because a stale @types/mocha lingered in node_modules).

Add explicit `bun:test` imports (before->beforeAll, after->afterAll in
TelemetryService.test.ts). chai/sinon stay as libraries.

Verified against a clean tree (no @types/mocha): check-types 0 errors,
unit suite 58 files / 880 pass / 0 fail.

* style(vscode): biome-format migrated test files + codemod scripts

The mocha->bun:test codemod and manual import edits left formatting that
didn't match biome (the CI `format` check, which validates files changed
since main, flagged them). Also narrow setup.ts's bun:test import to the
actually-used beforeEach/afterEach (describe/it only appear in a JSDoc
example), fixing a noUnusedImports lint error.

ci:check-all (check-types + lint + format) now passes locally.

* fix(webview-ui): declare phantom deps + pin React 18 types under bun workspace

Folding webview-ui into the bun workspace changed its install topology
from an isolated npm flat tree to the shared hoisted store, surfacing
two classes of pre-existing latent issues that npm hoisting had masked:

1. Phantom dependencies: src imports `marked`, `unist`, `unist-util-visit`
   and `@heroui/theme` directly but never declared them. Declared them
   (marked ^15, unist-util-visit ^5, @types/unist ^3, @heroui/theme 2.4.26).
2. React types: @testing-library/react's optional peer pulls @types/react@19
   into a resolvable location; tsc mixed it with the toolkit's React 18
   types (React 19 dropped Component.refs), breaking 452 JSX usages. Pin
   react/react-dom type resolution to webview-ui's React 18 copy via
   tsconfig paths.

build:webview (tsc -b && vite build) and ci:check-all now pass.

* fix(vscode): restore @types/mocha for integration build + add bun:test types

The @vscode/test-cli integration runner still uses mocha, and
tsconfig.test.json compiles all src/**/*.test.ts (including bun-migrated
files) to out/. So:
- restore @types/mocha (integration compile needs the mocha ambient types)
- add `bun` to tsconfig.test.json types + root @types to both tsconfig
  typeRoots so `bun:test` resolves under tsc for the migrated tests

* fix(vscode): declare glob — phantom dep used by package-standalone.mjs

scripts/package-standalone.mjs imports `glob` but it was never declared
(resolved transitively under npm's flat hoist). Under the bun workspace
store it's unresolvable, failing postcompile-standalone with
ERR_MODULE_NOT_FOUND. Declare glob ^11 (modern named-export API).

compile-standalone now produces dist-standalone/standalone.zip.

* fix(ci): strip ANSI before vitest zero-test guard grep

The vitest summary line colorizes the count ("Tests  <ansi>582 passed"),
so the count isn't adjacent to the "Tests" label in raw bytes and the
guard regex failed even though 582 tests passed. Strip ANSI escapes
before matching.

* fix(vscode): declare minimist — phantom dep in testing-platform-orchestrator

scripts/testing-platform-orchestrator.ts imports `minimist` (undeclared,
resolved transitively under npm hoist). Declare it so the testing-platform
integration job runs under the bun workspace store.

* fix(vscode): restore tsconfig-paths for integration runner; tp-orchestrator uses bun

Phase 6 over-removed tsconfig-paths: test-setup.js (loaded by the
@vscode/test-cli mocha integration runner) requires it to resolve @/
aliases in the compiled out/ tree — the extension host test runner failed
with "Cannot find module 'tsconfig-paths'". Restore it. Also switch the
testing-platform spawn from `npx ts-node index.ts` to `bun index.ts`
(bun runs TS natively; avoids the removed ts-node).

* fix(vscode): route tests by bun:test import marker; integration runner stays mocha

The mocha->bun codemod swept up tests that the Node-based @vscode/test-cli
integration runner compiles/runs, which cannot load the `bun:test` builtin
(and some need the real VSCode host). Establish a single source of truth:
a *.test.ts is bun-runner-owned IFF it imports "bun:test".

- run-bun-unit-tests.ts: discover files by the bun:test import marker
  (not fixed globs), so every migrated file runs under bun.
- build-tests.js: generate a tsconfig that excludes all bun:test files
  from the integration compile (json5-parsed), so out/ never contains
  bun:test; gitignore the generated config.
- .vscode-test.mjs: exclude the bun unit dirs from the runner globs.
- revert host-dependent tests (hostbridge/*, extension, terminal,
  FileContextTracker host bits) and 3 files with sinon-on-ESM/behavioral
  issues (ClineIgnoreController, mentions, TelemetryService) back to
  mocha; they run on @vscode/test-cli as before.

Verified: check-types 0 errors; compile-tests 0 bun:test in out/;
bun unit 65 files/962 pass/0 fail; vitest 582/0.

* fix(vscode): declare mocha — phantom dep for @vscode/test-cli integration runner

The @vscode/test-cli extension host loads `mocha` at runtime to run the
integration suite, but only @types/mocha was declared (npm hoisted the
mocha package transitively; bun's store does not expose it). The host
failed with "Cannot find module 'mocha'". Declare mocha ^11.7.4 (matches
@vscode/test-cli's own range).

* fix(vscode): robust Windows protoc-gen-ts_proto plugin resolution under bun

build-proto.mjs hardcoded node_modules/.bin/protoc-gen-ts_proto.cmd for
Windows, but bun's workspace store places/extensions the bin shim
differently (hoist + .cmd/.bunx), so Windows protos failed with
"protoc-gen-ts_proto: The system cannot find the file specified". Probe
the local + root .bin with known shim extensions instead. Also update
the testing-platform usage string (ts-node -> bun).

* fix(vscode): generate node .cmd wrapper for ts-proto plugin on Windows

The previous probe found bun's `.bunx` shim, but protoc cannot exec it
("%1 is not a valid Win32 application"). Instead, on Windows generate a
small .cmd wrapper that runs the resolved protoc-gen-ts_proto JS via
`node`, which protoc can execute regardless of package manager. POSIX
path (direct JS bin) is unchanged.

* fix(vscode): package VSIX with --no-dependencies (bundled) to stop monorepo traversal

Under the bun workspace, @cline/* are workspace:* symlinks pointing to
../../../../sdk/packages/*. vsce, walking the dependency tree, followed
them out of apps/vscode and packaged the whole monorepo (../, ~84MB incl.
root node_modules and .env), which crashed vsce's secret scanner and
failed all e2e jobs.

The extension is fully esbuild-bundled into dist/extension.js, so vsce
should not walk node_modules at all. Add --no-dependencies to every
vsce/ovsx package/publish path (e2e build, marketplace, nightly), and
tighten .vscodeignore to drop nested node_modules and dev-only inputs
(scripts, proto, testing-platform, bunfig, esbuild.mjs, etc.).

Result: VSIX is 39 files / ~7 MB and the secret scan passes.

* docs(vscode): tighten bun/node comments and consolidate into a clinerule

- add .clinerules/bun-and-node.md (eternal-now: bun=tooling, node=runtime,
  keep-list, and the bun:test-vs-mocha test routing rule); remove the
  apps/vscode/docs/bun-migration-notes.md migration doc and point
  .clinerules/general.md at the rule (single-line bullet matching the file).
- fix the hotfix-release note: there is no infra step that regenerates the
  lockfile; a CHANGELOG+version bump leaves bun.lock consistent (workspace
  versions aren't pinned) and publish runs --frozen-lockfile.
- reframe runner/preload comments to describe the code as-is (drop
  "migrated off mocha"/codemod history); add a TODO on the bun-test preload
  to migrate suites off the vitest `vi` shim to native bun:test and delete it.
- remove the one-shot mocha->bun codemod scripts.

* fix(debug-harness): pin debugee VSCode version so bundled Playwright can drive it

The harness downloaded "stable" VSCode (currently 1.125 / Electron 42),
which the bundled Playwright cannot drive — `_electron.launch()` hangs
until its 60s timeout (Electron started and a window appeared, but the
launch handshake never completed). Default to a known-good version
(1.103.0, matching the e2e CI matrix) and allow override via
VSCODE_TEST_VERSION.

* fix(webview): render under bun workspace — dedupe React, drop stale codicons link

The webview mounted but crashed before rendering (blank sidebar; e2e
"Login to Cline" never visible) with "Cannot read properties of null
(reading 'useRef')" — the classic two-React-copies / null hook dispatcher.
Under the bun workspace, sibling packages pull react@19 into the shared
store and a transitive webview dep resolved a second React instance into
the vite bundle. Add resolve.dedupe + pin react/react-dom to webview-ui's
own React 18 copy.

Also drop the separate `<link>` to node_modules/@vscode/codicons in the
webview HTML: the webview's index.css already @imports codicons, so the
font is bundled into the build assets. Under bun that node_modules path
is a symlink to the root store (outside the webview localResourceRoots)
and isn't packaged with --no-dependencies, so the link 404'd; the bundle
covers it. Re-scope the .vscodeignore nested-node_modules exclude so it
no longer shadows the codicons re-include.

* fix(debug-harness): disable GPU so the debugee renders in headless/VM envs

On headless/VM GPU stacks the debugee Electron's GPU process crash-loops
("Exiting GPU process during initialization" / CreateCommandBuffer
kTransientFailure), killing the window before Playwright finishes
attaching and tripping the 60s launch timeout. Force software rendering
(--disable-gpu and friends) for a stable harness launch.

* fix(debug-harness): survive launch failures; configurable, longer launch timeout

The harness crashed (whole bun process exited) whenever VSCode launch
failed/timed out: Playwright emits a late unhandled rejection on the dead
CDP transport after we've already handled the launch error, and the
default behavior takes the HTTP server down with it — forcing a full
restart just to retry.

- Add process-level unhandledRejection/uncaughtException guards so stray
  async errors are logged and the server keeps serving (retry via `launch`).
- On launch failure, close the orphaned Electron so a retry isn't blocked.
- Make the _electron.launch timeout configurable (--launch-timeout) and
  raise the default to 120s for cold launches; document VSCODE_TEST_VERSION.

* fix(ci): address review feedback — vsix --no-dependencies, drop stale coverage path, Windows shell

- ext-vscode-publish-stable.yml: add --no-dependencies to the release-artifact
  `vsce package` (Max's catch). Without it, vsce follows the @cline/* workspace
  symlinks out of the package and bloats the .vsix with the whole monorepo.
- ext-vscode-test.yml: drop the stale apps/vscode/coverage-unit/lcov.info upload
  path (Max's catch). That file was produced by the removed nyc unit-coverage
  step (.nycrc.unit.json); nothing generates it now.
- ext-vscode-test-e2e.yml: the better-sqlite3 assert step ran under the Windows
  runner's default pwsh and failed to parse the POSIX test. Pin it to `shell: bash`
  (Git Bash ships on windows-latest); the non-e2e job already defaults to bash.

---------

Co-authored-by: Cline Agent <cline-agent@users.noreply.github.com>
2026-06-24 14:11:34 +09:00
Max 8636272eb5 Improve onboarding funnel metrics (#11650)
* improve onboarding metrics

* fix onboarding page view dedupe

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:34 +09:00
Max Paulus 🥪 815b0346f6 fix package lock issues post rebase 2026-06-24 14:11:34 +09:00
Max 84fe95de4d fix(vscode): persist Vertex provider settings (#11565)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:34 +09:00
Max 36be3333d4 fix(vscode): preserve legacy task metadata on resume (#11570)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-24 14:11:34 +09:00
Dominic Cooney 96c0bd70a4 chore(vscode): remove stale HuggingFace provider test 2026-06-24 14:11:33 +09:00
Max Paulus 🥪 6f621e4bf2 fix standalone e2e test 2026-06-24 14:11:33 +09:00
Max Paulus 🥪 d7ea201c25 bump sdk version 2026-06-24 14:11:33 +09:00
Dominic Cooney e3b3e6b0ad fix(vscode): simpler login UX (ENG-2174) — remove inline provider gate, restore debug harness (#11534)
* remove inline no-usable-provider sign-in banner; rely on inference-time errors

The "Sign in to Cline or set up a provider" banner gated chat input on a
parallel provider-usability heuristic that mis-detected BYOK setups
(Bedrock profile/IAM, Vertex ADC) and its sign-in button discarded the
device code. Remove the component and the hasUsableProvider plumbing.

Auth/config problems now surface at inference time, where handling
already exists:
- cline provider without a token -> emitClineAuthError -> ErrorRow
  renders the Sign in button with the device-code display
- any other misconfigured provider -> say:"error" row

Also deletes the now-dead sdk/provider-usability module and adds a test
that failed session start emits a plain chat error.

* restore debug-harness server deleted in 0bfbfb944

Commit 0bfbfb944 ("delete unused files") removed src/dev/debug-harness/server.ts
as dead code, but it is a dev tool launched directly via
`npx tsx src/dev/debug-harness/server.ts` (see its README and
.clinerules/debug-harness.md) — no static import graph reaches it, which
is why the unused-file analysis flagged it. The README, the .clinerules
docs, and the CLINE_CAPTURE_BROWSER / __clineHandleUri hooks in
extension.ts and utils/env.ts that exist solely for this harness all
survived the deletion, leaving them dangling.

Restored verbatim from 0bfbfb944~1; verified it boots and listens on
:19229.
2026-06-24 14:11:33 +09:00
Saoud Rizwan 53d0f50494 fix(vscode): restart session when user switches provider (#11507)
* fix: format Cline OAuth tokens in provider config

* fix(vscode): restart SDK session on provider switch

* fix(vscode): serialize SDK provider restarts

* chore(vscode): keep provider switch PR scoped

* fix(vscode): simplify deferred provider restarts
2026-06-24 14:11:33 +09:00
Mikołaj Kondratek 141a61fd45 fix: thread proxy/CA-aware fetch into the SDK inference path (#11462)
* fix: thread proxy/CA-aware fetch into the SDK inference path

The main agent loop did not receive the host's proxy/CA-aware fetch, so
on JetBrains and the CLI inference over a corporate proxy or to a
self-signed/private-CA endpoint failed with "unable to get local issuer
certificate". This regressed at the SDK cutover: the pre-SDK CLI
(2.18.0) constructed provider clients with a proxy-aware fetch directly,
while the SDK agent loop fell back to bare global fetch (CLINE-2353).

Two layers:
- App (cline-session-factory.ts): always build CoreSessionConfig.
  providerConfig and carry the proxy-aware fetch from @/shared/net, not
  just for Bedrock. In VSCode this fetch is global fetch, so behavior is
  unchanged there; in the standalone (JetBrains) build it is undici with
  EnvHttpProxyAgent.
- SDK (handler-factory.ts): forward providerConfig.fetch into
  createGateway both as the top-level fallback fetch and per provider, so
  the gateway's provider clients use it. Passing undefined is a no-op
  (registry resolves config?.fetch ?? defaults?.fetch ?? fallbackFetch),
  so other SDK consumers are unaffected.

The SDK change covers every host that supplies a fetch; the app change
covers VSCode and JetBrains. The CLI builds its session config through a
separate path (apps/cli) that does not yet wire a proxy-aware fetch, so
CLINE-2353 on the CLI surface is addressed in a follow-up.

Adds a handler-factory unit test asserting the host fetch is forwarded
to createGateway at both the top level and per provider.

* fix: deterministically install proxy dispatcher in standalone core

The proxy/CA-aware undici dispatcher is installed as a side effect of
loading @/shared/net (it calls setGlobalDispatcher with EnvHttpProxyAgent
in the standalone build). The standalone entry cline-core.ts did not
import that module, so the dispatcher was only installed incidentally
when some other transitively-imported module happened to pull it in. A
future change to the import graph could silently drop proxy/CA support on
JetBrains.

Import @/shared/net for its side effect, first, so the install is
deterministic and runs before any network use (CLINE-2353).

Standalone-only hardening; VSCode uses global fetch and is unaffected.
2026-06-24 14:11:33 +09:00
Saoud Rizwan 7578a1de36 fix(vscode): fix duplicate tool row when changing plan/act mode during pending tool approval (#11437)
* fix(vscode): suppress duplicate tool row when a mode change clears a pending approval

Switching plan/act while a tool approval was pending duplicated the
approval row in chat. clearPending resolved the pending approval as
denied, which unblocks the core; the core then emits the denied tool
call's content_start/content_end events before the mode coordinator's
abort lands. The interactive deny paths record the denial in the
message translator state so those events are suppressed, but
clearPending skipped that step, so the translator rendered the events
as a fresh say:tool row next to the still-visible approval ask.

clearPending now records the denial through recordDeniedToolApproval
before resolving, mirroring resolvePendingToolApproval. This covers all
clearPending callers: mode changes, task cancel, and task clear.

* refactor(vscode): trim the clearPending denial fix to its minimal shape

Keep clearPending's original structure, only inserting the denial
recording before the resolve. Drop the end-to-end suppression test:
translator suppression for recorded denials is already covered by
message-translator-approval-denial.test.ts, and the clearPending
recording is covered by the extended unit assertion.
2026-06-24 14:11:33 +09:00
Saoud Rizwan 5aa60cf116 fix(vscode): restore aggressive pin-to-bottom auto scroll in chat view (#11436)
* fix(webview): restore aggressive pin-to-bottom auto scroll in chat view

The auto-scroll effect only fired on groupedMessages.length changes, but in
the SDK-migrated extension new content can appear in the chat without the
message list length changing:

- The Thinking placeholder row is driven by turnState alone (e.g. the plan
  to act switch auto-continues the task with no new message), and it was
  appended to the rendered list inside MessagesArea where the scroll hook
  never saw it.
- New tool messages merge into the trailing tool group, and the thinking
  placeholder gets swapped for a real reasoning row at constant length.

Fixes:
- Lift the thinking placeholder computation out of MessagesArea into a new
  useDisplayedGroupedMessages hook so ChatView feeds the same list to both
  Virtuoso and useScrollBehavior; the placeholder appearing now pins to
  bottom like a real message.
- Key the pin effect on the tail message ts (skipping the placeholder) in
  addition to list length, covering in-place tail changes.
- Re-engage auto scroll when turnState.phase transitions into streaming. In
  the old extension every turn start was accompanied by a user send/button
  click that reset disableAutoScrollRef; turnState-driven turn starts like
  plan to act auto-continue have no webview-side action, so handle it in
  the scroll hook.

* refactor(webview): replace scroll fix with minimal single-file version

Same three behaviors as the previous commit (pin when the thinking
placeholder appears, pin on in-place tail changes, re-engage auto scroll
when a turn starts streaming) but implemented as two small effects in
MessagesArea, which already has both the rendered list and scrollBehavior
in scope. Reverts the useDisplayedGroupedMessages hook extraction and the
ChatView/useScrollBehavior changes; net diff vs the base branch is now
one file.
2026-06-24 14:11:33 +09:00
Dominic Cooney b62e572ea9 test(vscode): exercise full SDK structured edit flow in file-edit e2e (#11442)
* test(vscode): exercise full SDK structured edit flow in diff.test.ts e2e (ENG-2042)

The SDK runtime executes structured (OpenAI-format) tool calls instead of parsing XML-style tool syntax out of assistant text. Teach the e2e mock server to stream an editor tool call for edit_request (arguments split across deltas to exercise fragment reassembly), answer the SDK's follow-up tool-result request (role:'tool' message) with turn-ending completion text, and remove the classic XML-era EDIT_REQUEST/REPLACE_REQUEST responses.

diff.test.ts now covers the full approval flow: approval ask row -> Save -> editor tool writes the file -> completion text, verifying the edit on disk and restoring the git-tracked fixture afterwards. The old 'test.ts: Original <-> Cline's Changes' diff-tab assertions are unreachable under the SDK executor architecture (the editor executor writes via Node fs and does not route through DiffViewProvider); this behavioral difference is documented in the test file.

* test(vscode): address review feedback on diff.test.ts e2e

- Scope the mock server's tool-result follow-up detection to edit_request conversations so tool results from other (future) scenarios don't mis-route to EDIT_REQUEST_COMPLETE.

- Move the fixture readFileSync inside the try block and guard the finally restore, so a failed read doesn't bypass cleanup attribution or write undefined back to the fixture.

* docs(vscode): rephrase diff e2e comments to describe current behavior

Comments described historical behavior (XML-style tool-call parsing that predates the SDK runtime), which is confusing to readers of the current code. Rephrase them to describe the code as it exists now.

* test(vscode): rename diff.test.ts to file-edit.test.ts and drop duplicated preamble

The test no longer touches a diff editor (the SDK editor executor writes files directly after approval), so the 'Diff Editor' name was misleading. Rename the file and describe block to match what it asserts: the file-edit approval flow.

Drop the first half of the test (send hello, wait, New Task, check history), which duplicated chat.test.ts, and the mock server's 500ms delay that existed only to support an 'API Request...' visibility assertion that no longer exists.
2026-06-24 14:11:33 +09:00
Robin Newhouse 268462ddcf fix(vscode): stabilize SDK e2e login flow (#11441) 2026-06-24 14:11:33 +09:00
Dominic Cooney 072e237d01 fix(vscode): persist skill disable to SKILL.md frontmatter so the model honors it (ENG-1995) (#11294)
* fix(vscode): persist skill disable to SKILL.md frontmatter so the model honors it (ENG-1995)

The VS Code skill toggle only updated extension state (globalSkillsToggles /
localSkillsToggles), but the SDK builds the model's skill list and the `skills`
tool from each SKILL.md's frontmatter `disabled` flag. As a result, disabling a
skill in the sidebar left it fully available to the model, including in new
tasks.

toggleSkill now also writes the `disabled` flag to the skill's SKILL.md
frontmatter (no-op for remote skills, which have no backing file), via new
helpers updateSkillMarkdownDisabledState / setSkillDisabledInFrontmatter in
skills.ts. Adds unit tests for both helpers.

* fix(vscode): don't rewrite skills with malformed frontmatter (ENG-1995)

parseYamlFrontmatter fails open on invalid YAML, returning the full original
document as the body. updateSkillMarkdownDisabledState would then prepend a
second `---` block on a disable, corrupting the file. Bail out and leave the
file untouched when frontmatter fails to parse. Adds tests for the malformed
disable/enable cases.

Addresses Greptile review feedback on #11294.

* test(vscode): assert malformed-skill fixture is actually invalid YAML (ENG-1995)

Add a guard test that parseYamlFrontmatter reports hadFrontmatter and a
parseError for the shared malformed fixture, so the two "leave file untouched"
tests can't silently pass via a different code path if the fixture ever became
valid YAML.

Addresses Greptile review feedback on #11294.

* fix(vscode): resolve @cline/shared/storage subpath in mocha unit-test compile

The CommonJS mocha unit-test runner uses classic "node" moduleResolution,
which does not read the `exports` subpath maps in @cline/* package
manifests, so `@cline/shared/storage` (imported by
src/sdk/telemetry-settings-sync.ts) failed with TS2307 when test files
transitively reach the SDK adapter. Mirror the explicit paths mapping
already added to tsconfig.test.json for the integration-test compile.

* fix(vscode): restore E2E mock auth in SDK auth service so e2e tests can sign in

The SDK migration replaced classic AuthService (which swapped in
AuthServiceMock under E2E_TEST) with sdk/auth-service.ts, losing the
mock path. "Login to Cline" then invoked the real SDK OAuth flow and
opened a native browser dialog the Playwright tests cannot interact
with, so helper.signin() never authenticated and chat.test.ts +
diff.test.ts failed on every platform (the failures also reproduce on
the base branch).

- auth-service.ts: under E2E_TEST=true (and CLINE_ENVIRONMENT=local),
  exchange the well-known test code with the local mock API server and
  persist credentials to providers.json — no browser. Replaces classic
  AuthServiceMock (see origin/main src/services/auth/AuthServiceMock.ts).
- chat.test.ts/diff.test.ts: wait for the mock turn to complete before
  clicking New Task; SDK history is persisted at turn end, so navigating
  mid-turn races the write and Recent never shows.
- diff.test.ts: the footer Start New Task button only appears for
  attempt_completion turns under SDK TurnState; use the header New Task
  button like chat.test.ts.
2026-06-24 14:11:32 +09:00
Saoud Rizwan 31cff1f75e fix(vscode): auto-continue the task when switching from plan to act (#11401)
* fix(vscode): enforce stop-before-start ordering for same-id session restarts

The app reuses the taskId as the sessionId whenever it replaces or
resumes a session (mode/MCP rebuilds, follow-up resume, history
restore), but the old session's stop ran fire-and-forget, and core
cleanup is keyed by sessionId across multiple awaits. A stop still in
flight when the same-id replacement started could tear down the live
successor: late sessions-map deletes, a late 'ended' emission, or a
stalled status write landing on the replacement.

Adopt the sequencing invariant the CLI has always used: never start a
same-id session while its stop is in flight. SdkSessionLifecycle tracks
in-flight stops in a pendingStops map keyed by sessionId, and
startNewSession awaits the pending stop for a reused id before starting
(with a log line so a wedged stop is diagnosable). Fresh-id starts
never wait. fireAndForgetSend additionally captures the ActiveSession
by object identity at send time so a send settling after a same-id
replacement cannot flip the successor's run state.

* fix(vscode): auto-continue the task when switching from plan to act

In plan mode, the model's switch_to_act_mode tool call flipped the toggle
but ended the run as aborted: the beforeModel stop hook fired after
turn-started, leaving a dangling api_req_started spinner rendered as
'API Request Cancelled', and nothing continued the task after the
act-mode rebuild. Manually toggling after a presented plan had the same
dead end.

The tool now declares lifecycle.completesRun so the run ends cleanly
after the tool result, and the queued mode change rebuilds the session
and auto-continues with a hidden continuation prompt. A manual plan to
act toggle auto-continues only when the agent is idle after presenting
its plan (not running and awaiting_followup; a pending ask_question
blocks mid-run so it cannot false-positive). Composer content rides
along: typed text becomes the continuation, attachments are forwarded
and echoed, attachment-only toggles count as consumed. The RPC reports
consumption only after the send was actually handed to the session, and
the webview then clears only the exact submitted content, so failures
and racing input never lose composer state. Failures before the send
undo the optimistic running flip, report an error phase, and roll the
mode back when the session was never replaced.

Hidden prompts (the act continuation and the pre-existing task
resumption prompt) shifted editMessageAndRegenerate's visible-to-SDK
user message ordinal mapping; the new sdk-user-message-mapping module
skips them in their persisted user_input-wrapped shape, counts
attachment-only messages (which have visible bubbles), ignores
tool-result rows, and attachment-only resumes now echo a bubble to keep
both transcripts aligned. Follow-ups sent during a rebuild wait on
waitForPendingRebuild instead of resuming a parallel session that the
rebuild would kill.

The plan-mode system prompt and tool description require explicit user
approval in a message sent after the plan was presented, preventing the
model from self-escalating to act mode.

* fix(vscode): move the turn phase to error when a task resume fails

askResponse optimistically sets the turn phase to streaming before
delegating to the followup coordinator, but the coordinator's resume
catch only posted an error row, leaving the footer stuck on
Thinking/Cancel. Resume failures (auth errors, session start errors)
now report back via onResumeFailed so the controller can set the phase
to error.
2026-06-24 14:11:32 +09:00
Saoud Rizwan 9a981f81d7 fix(webview): use consistent reasoning selector component in extension provider settings (#11399)
* fix(webview): use themed components and reasoning selector in generic provider settings

The catalog-backed GenericProviderSettings path (deepseek, gemini, mistral,
and other migrated providers) rendered its model picker with raw unstyled
HTML select/input/button elements, unlike every other provider which uses
the VS Code webview-ui-toolkit components. Swap ModelPickerWithManualEntry
to VSCodeDropdown/VSCodeOption/VSCodeTextField/VSCodeButton, reusing the
DropdownContainer and re-init key workaround from common/ModelSelector.

Also render ReasoningEffortSelector in GenericProviderSettings when the
selected model's catalog info has supportsReasoning, persisting the effort
through the provider config reasoning patch, matching ClineModelPicker.
This is driven by the catalog capability flag rather than provider id.

* fix(webview): re-sync custom model id field after async config hydration

The controlled customModelId state was initialized once at mount, but the
provider config and model catalog both hydrate asynchronously, so the lazy
initializer could capture a placeholder value and leave the custom model
text field stale once the committed selection loaded. Sync the field via an
effect keyed on the committed model id and its in-list status, depending on
derived values rather than the models object whose identity can change
every render while the catalog loads.
2026-06-24 14:11:32 +09:00
Robin Newhouse d8b1ce54d4 fix(vscode): expand remote workflow/skill slash commands before send ENG-2036 (#11388)
* fix(vscode): expand remote workflow/skill slash commands before send

The SDK-backed extension sent `/workflow` text to the model verbatim, so
remote-config workflows never ran. Expansion is host-driven (the agent loop
never auto-expands), and the controller's pre-send path did none — matching
the CLI's `buildUserInputMessage`, resolve slash commands via a
controller-owned UserInstructionConfigService that watches the workspace
(including `.cline/remote-config/`), refreshed after each remote-config sync.

Fixes ENG-2036.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(vscode): guard instruction watcher against post-dispose race

Reject in ensureUserInstructionService when the controller is already
disposed so a slash-command resolution that yielded across dispose() can't
resurrect a file watcher that nothing will stop. Also log the post-expansion
length handed to parseMentions. Addresses Greptile review.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 14:11:32 +09:00
Max Paulus 🥪 6bc771ba9d include optional deps so that CI passes 2026-06-24 14:11:32 +09:00
Max Paulus 🥪 87338cacd1 fix broken tests 2026-06-24 14:11:32 +09:00
Max Paulus 🥪 827a4a2616 bump sdk version 2026-06-24 14:11:32 +09:00
Max Paulus 🥪 13d6fe3876 add vertex support to extension 2026-06-24 14:11:32 +09:00
Mikołaj Kondratek 82c3d8d771 fix(sdk): make model-not-found API errors actionable in the webview (#11378)
When a provider returns a model-not-found error (e.g. Anthropic's HTTP 404
for a retired model such as claude-3-haiku-20240307), the SDK strips the
status and delivers only the terse body, which collapses to the bare label
"model: <id>". reshapeErrorForWebview fell through to returning that raw
string, so ErrorRow rendered a label-like fragment in red with no hint that
the model is gone or how to recover.

Detect these in the plain-text branch of reshapeErrorForWebview and rewrite
them into a sentence that names the model and tells the user to switch models
in API Configuration settings, then retry. The model switch is framed as a
precondition rather than a parallel option so users don't loop on Retry.

Detection is text-based because the HTTP status is unavailable at this point.
The keyword match is anchored to the word "model" with a not-found signal in
the same sentence, so unrelated errors that merely mention a model (plan
gating, deprecated features) are left untouched. Adds tests for the bare
label form, a generic "does not exist" form, and two negative cases (plan
gating and an auth error mentioning a model) that must pass through unchanged.
2026-06-24 14:11:32 +09:00
Max Paulus 🥪 ee0c63a216 fix telemtry opt flag migration 2026-06-24 14:11:31 +09:00
Dominic Cooney 11e668ca23 fix(vscode): resolve @cline/shared/storage subpath in test compile + vitest
The CommonJS integration-test tsconfig (moduleResolution: node) and the
vitest config did not resolve the @cline/shared/storage exports subpath
imported by src/sdk/SdkController.ts, breaking 'compile-tests' (TS2307)
and 3 vitest SDK suites. Add explicit path/alias mappings to the built
dist so both resolve without changing module emit. Compile-time/test-only;
emitted JS still uses the real package specifier.
2026-06-24 14:11:31 +09:00
Max Paulus 🥪 50a4bfb435 migrate telemetry value in extension 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 2c53d36e6d bump sdk version 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 e4ab360c3c fix claude-code setting loading/persistence 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 5363b2a6d2 fix task history delete 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 51d3865ec5 fix model selector not showing most up to date model in providers.json 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 2a070cddf5 fix ui test 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 401154a42e fix ci checks 2026-06-24 14:11:31 +09:00
Robin Newhouse 3b02d5162e refactor(vscode): remove MCP marketplace ENG-1591 (#11217)
* refactor(vscode): remove MCP marketplace

* test(vscode): clarify MCP marketplace removal test

* docs: update MCP server controls docs
2026-06-24 14:11:31 +09:00
Max Paulus 🥪 710cceab90 fix anthropic provider settings persistence 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 a469377d57 remove baseUrl from providers.json when unchecking box in ui 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 1689abf479 fix ollama and lmtudio settings persistence 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 abd84b80cb fix openrouter apikey persist to providers.json 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 9d4aa03d4d fix vscodelm provider settings persist 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 91077fd49e persist bedrock settings to providers.json 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 6aa4c33918 don't block user input when hasNoUsableProvider == true 2026-06-24 14:11:30 +09:00
Mikołaj Kondratek aa8e11ad47 fix(bedrock): treat profile/IAM/credential-chain auth as a usable provider (#11313)
The inline "Sign in to Cline or add an API key" gate appeared and disabled
chat for Amazon Bedrock users who configured AWS Credentials (access key +
secret), an AWS profile, or relied on the default AWS credential chain, even
though the provider was fully usable (issue #11270).

hasUsableProvider() decided Bedrock usability solely via resolveApiKey(),
which maps bedrock -> awsBedrockApiKey. Bedrock's three non-API-key auth
modes leave that field empty, so buildBedrockProviderConfig() would build a
working session while the gate reported the provider unusable. The Cline
login state is irrelevant here: the gate is computed for the active-mode
provider, and the "Sign in to Cline" button is just one of two generic
remedies, which is what made the symptom look like a logged-out state.

Add a Bedrock branch that classifies usability per auth mode, reusing
resolveBedrockAuthentication() so the gate and the session builder agree on
what each mode means:
- api-key: usable only when awsBedrockApiKey is non-blank (unchanged, now
  also rejects whitespace-only keys)
- profile / iam / default credential chain: usable, deferring credential
  resolution to request time (mirrors buildBedrockProviderConfig and the
  existing keyless-provider philosophy)

Manually verified on a real setup across all four auth modes: pre-fix the
gate blocked chat for access-key and profile auth; post-fix the gate clears
and chat works. API-key mode was never gated incorrectly.

Tests: add Bedrock coverage for every auth mode, including api-key with a
blank and with an unset key (both not usable), the SigV4 repro, profile
(explicit/inferred/awsUseProfile), the bare credential-chain config, and
plan-mode resolution plus plan/act isolation.
2026-06-24 14:11:30 +09:00
Ara ca128cfd9f Fix SDK task size in delete tooltip (#11277)
* fix: show SDK task size in delete tooltip

* fix: address SDK task size review feedback

* fix: simplify SDK task size caching
2026-06-24 14:11:30 +09:00
Max Paulus 🥪 78b1f30133 remove unused code/files
fix broken webview build

remove unused code/files

fix broken webview build
2026-06-24 14:11:30 +09:00
Max Paulus 🥪 1f31738b32 delete unused files 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 6eeee7c4e6 Add edit and regenerate for VS Code chat messages
Allow user feedback messages in the VS Code extension to be edited inline and regenerated from that point. Adds a TaskService RPC, truncates persisted SDK history before the selected visible user prompt, and starts a new session with the edited prompt. Also ensures the regenerated active task appears in extension history while SDK history catches up.
2026-06-24 14:11:29 +09:00
Max Paulus 🥪 e88e0c4994 fix webview-ui tests 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 4258b338df show model list if possible for openai compatible 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 7b24026291 fix onboarding model selection not persisting 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 9a6c972b02 remove provider-specific views and just use genericprovidersettings.tsx 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 fae824bff0 dry up duplicate code and create useProviderModelSelection 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 9011051f76 dry up provider api key logic 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 c4068823a4 dry up some duplicate code 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 323492108d fix onboarding models 2026-06-24 14:11:28 +09:00
Max Paulus 🥪 58b41fbaf5 fix failing biome/lint 2026-06-24 14:11:28 +09:00
Mikołaj Kondratek 3df2d6c780 Remove unused import 2026-06-24 14:11:28 +09:00
Mikołaj Kondratek 60b1ab4d57 fix(sdk): drop dead autoContinue branch in mode rebuild
cf25cd66a ("make extension plan mode more similar to CLI") removed the
file-level ACT_MODE_CONTINUATION_PROMPT constant and stopped passing the
autoContinue / continuationPrompt options when rebuilding a session for
a mode change, but left the corresponding block inside
rebuildSessionForMode in place. The block still references the deleted
constant, so tsc fails on the SDK migration branch with TS2304: Cannot
find name "ACT_MODE_CONTINUATION_PROMPT".

No caller passes options to rebuildSessionForMode anymore, so the block
is dead. Drop the block and narrow the signature to take only newMode.
Existing tests already invoke rebuildSessionForMode(<mode>) with no
second argument and assert that fireAndForgetSend is not called on a
mode rebuild, so they keep passing.
2026-06-24 14:11:28 +09:00
Mikołaj Kondratek 0755b58da1 fix(terminal): capture standalone terminal output on Windows and harden PowerShell command handling (#11133)
* fix(terminal): surface standalone terminal spawn diagnostics

Add Logger calls at every chokepoint of the standalone terminal pipeline
so the (currently silent) failure modes around JetBrains-hosted
cline-core become debuggable from cline-core-service.log.

Lines added, all using the existing Logger facility (no new
dependencies, no behavioral changes):

* StandaloneTerminalProcess.run() now logs:
  - `[StandaloneTerminalProcess] run() entered: shell=… cwd=… args=…`
    on entry, before the try block;
  - `[StandaloneTerminalProcess] spawned pid=… for shell=…` right
    after child_process.spawn returns;
  - `[StandaloneTerminalProcess] close: code=… signal=… fullOutputLen=…`
    inside the `close` handler (the `fullOutputLen` reveals when the
    child exits 0 with empty pipes — the symptom in issue #10948);
  - `[StandaloneTerminalProcess] child error: …` in the `error`
    handler;
  - `[StandaloneTerminalProcess] spawn threw synchronously: …` in
    the outer catch.

* StandaloneTerminalManager.runCommand() now logs entry
  (`[StandaloneTerminalManager] runCommand terminalId=…: <cmd>`) and
  attaches a `.catch` to the previously fire-and-forget
  `process.run(…)` Promise so an unhandled rejection surfaces as
  `[StandaloneTerminalManager] process.run rejected for terminal …`
  instead of disappearing.

* CommandExecutor.execute() extends the existing "Executing command
  in … terminal" line with `mode=<terminalExecutionMode>` and
  `managerCtor=<manager.constructor.name>`, so it's possible to
  confirm whether the `vscodeTerminal` path is in fact backed by a
  `StandaloneTerminalManager` on JetBrains (it is — see
  notes/issue-10948-…md).

* CommandOrchestrator.orchestrateCommandExecution() logs the
  `process.once("completed")` event with `exitCode`/`signal`/
  `terminalType`, the "resolved completed" return branch with the
  line/byte totals, and emits a `WARN` on the silent "still running"
  fall-through. The last one matters because the original repro
  reported "Command executed successfully (exit code 0)" with empty
  output — the WARN makes that branch loud the next time it fires.

These logs are what made the two distinct bugs in #10948 visible
(see the 2026-05-28 update in
notes/issue-10948-terminal-output-investigation-2026-05-27.md). They
stay in to keep the next regression debuggable.

Refs: cline/cline#10948

* fix(terminal): keep Windows child stdio attached to parent pipes

The non-cmd Windows branch in StandaloneTerminalProcess.run() spawned
the shell (powershell.exe in practice) with `detached: true` and no
`windowsHide`. When cline-core is launched by the JetBrains plugin it
has no console of its own, so Windows CreateProcess allocates a NEW
console for the detached child and the child's stdio routes to that
new console instead of the pipe handles the parent created. From the
parent's point of view the pipes immediately EOF, `close` fires with
`code=0`, and `fullOutput` is 0 bytes — exactly the symptom reported
in cline/cline#10948 ("Command executed successfully (exit code 0)"
with no output and no filesystem effect).

This bug applies to every command the agent runs through the
standalone terminal path on Windows, not just the
double-wrapped-PowerShell cases (verified by re-running a clean
`dir <file>` after the diagnostics from the previous commit landed:
`run() entered` and `spawned pid=<num>` both fired, then `close: code=0
fullOutputLen=0`).

Fix:

* `detached: process.platform !== "win32"` — keep the existing
  POSIX behavior (a separate process group helps `tree-kill`), but
  drop it on Windows where `tree-kill` walks the PID tree with
  `taskkill /T` and doesn't need a process group.
* `windowsHide: true` — matches every other `child_process.spawn`
  call site in cline-core (git, MCP, hooks, browser) and flips on
  `CREATE_NO_WINDOW`, keeping the child attached to our pipes
  without popping a console window.

Verified on Windows 11 + IntelliJ IDEA 2026.1 + Cline plugin
1.1.59-Internal: `dir <path>`-style probes now produce a non-zero
`fullOutputLen` in the close log, and the captured output bytes
match what would have been visible interactively. PowerShell
double-wrapping (the other half of #10948) is handled in a
follow-up commit.

Refs: cline/cline#10948

* fix(terminal): harden PowerShell command wrapping for standalone shell

`StandaloneTerminalProcess.getShellArgs()` blindly wrapped every
PowerShell command as `["-Command", command]`. That has two
end-user-visible failure modes on Windows, both observed in
cline/cline#10948:

1. The agent's `run_commands` tool call sometimes arrives already
   prefixed with `powershell -Command "…"`. We then spawned
   `powershell.exe -Command 'powershell -Command "…"'`, and the
   outer shell shredded the inner single/double-quote pairs while
   re-parsing its `-Command` argument. The inner pwsh saw
   quote-empty `Test-Path` calls, fell through to the `else` branch
   and reported "File not found" — to ITS stdout, which the outer
   inherited but the file deletion the LLM intended never ran.
2. The user's `$PROFILE` script ran on every spawn, leaking
   non-deterministic noise (e.g.
   `%windir%\System32\REG.exe : The module '%windir%' could not be
   loaded`) into the captured output and confusing the agent.
3. Bonus: the POSIX branch used `["-l", "-c", command]`. The `-l`
   re-sources login files on every command, which is slow and lets
   greeter scripts leak into output.
4. Bonus: the cmd branch used `["/c", command]`. `/d` skips
   AutoRun, `/s` makes the embedded-quote handling deterministic.

Fix:

* PowerShell branch returns
  `["-NoProfile", "-NonInteractive", "-Command", unwrap(command)]`.
  `-NoProfile` suppresses (1) the spurious profile noise that
  contaminated the captured output, and `-NonInteractive` ensures
  the child doesn't deadlock waiting on a prompt no one will answer.
* `unwrapPowerShell(command)` strips a leading
  `powershell|pwsh [.exe] -Command|-c "…"` (or single-quoted)
  wrapper that the LLM sometimes emits, fixing the double-pass
  argument-quoting destruction. If the command does not match the
  exact wrapper shape it is returned verbatim — worst case is "no
  change", preserving pre-fix behavior.
* cmd branch returns `["/d", "/s", "/c", command]`, matching the
  canonical helper in cline/sdk/packages/shared/src/parse/shell.ts.
* POSIX branch returns `["-c", command]`, dropping the unhelpful
  `-l`. Also matches the SDK helper.

Verified on Windows 11 + IntelliJ IDEA 2026.1 + Cline plugin
1.1.59-Internal in combination with the previous "keep Windows
child stdio attached" commit: `Remove-Item CHANGELOG.md` now
deletes the file, the agent's verification `Get-ChildItem CHANGELOG*`
returns nothing, and the profile-load REG.exe error no longer leaks
into captured output.

Refs: cline/cline#10948

* refactor(terminal): tone down standalone terminal diagnostics

The diagnostics added while chasing #10948 were intentionally loud so the
two bugs were visible. Now that the fixes are in, reduce them to a normal
operating posture:

* Demote fine-grained traces to `debug`: the per-spawn `spawning …` and
  `spawned pid=…` lines, `StandaloneTerminalManager.runCommand`, and the
  orchestrator's `resolved completed` summary.
* Drop the orchestrator's `completed event` line entirely — the
  `resolved completed` debug line already carries exit code, signal, and
  line/byte totals.
* Stop echoing the full command in the manager line and stop echoing the
  args vector in the spawn line. The command is still logged once at
  `info` by CommandExecutor (unchanged, pre-existing), so we go back from
  three command echoes to one. Commands routinely embed secrets
  (Authorization headers, tokens), so fewer copies on disk is better.

Kept loud on purpose:

* `info` on `close: code=… fullOutputLen=…` — the single line that proves
  the Windows stdio-capture fix and the most useful per-command signal.
* `warn` on `resolved without completion event` — the silent-success
  canary for the #10948 failure mode.
* `error` on child error / synchronous spawn failure / unhandled
  process.run rejection.

Refs: cline/cline#10948

* fix(terminal): tighten PowerShell unwrap regex and extract to a pure module

Two review follow-ups for the #10948 shell-arg handling:

1. The wrapper-strip regex used a greedy `([\s\S]*)` body, so a command
   like `powershell -Command "foo" "bar"` would match with the body
   captured as `foo" "bar`, silently rewriting a command into something
   different. Replace the body with a tempered match `((?:(?!\1).)*)`
   that cannot contain the captured delimiter, so anything other than
   exactly one quoted token is returned verbatim. Worst case is now
   "no change" rather than an incorrect rewrite. The legitimate
   double-wrapped case from #10948 (outer ", inner ') still unwraps.

2. `getShellArgs` and `unwrapPowerShell` were private methods on
   StandaloneTerminalProcess, untestable without spawning a process.
   Move them to a pure `shellArgs.ts` module. `getShellArgs` now takes
   an injectable `platform` (defaulting to `process.platform`) purely so
   the win32-vs-posix branch is testable; behavior is unchanged. This
   also gives us a single local seam to later consolidate onto the
   canonical `@cline/shared` helper (tracked as a follow-up).

No behavioral change beyond the regex correctness fix.

Refs: cline/cline#10948

* test(terminal): cover shell-arg construction and PowerShell unwrap

Add mocha unit tests (matching the repo's node:assert/strict + __tests__/
convention so the existing mocharc spec globs pick them up) for the newly
extracted shellArgs module:

* unwrapPowerShell: double-quote and single-quote wrappers, powershell.exe
  -c form, the #10948 nested-quote repro (inner quotes preserved),
  non-wrapped passthrough, and the two regressions the tightened regex
  must reject (`… "foo" "bar"` and a command that merely mentions
  powershell mid-string).
* getShellArgs: PowerShell -> -NoProfile -NonInteractive -Command (with
  unwrap), cmd -> /d /s /c, POSIX -> -c. The injectable platform arg lets
  these run on any CI host.

This closes the M1 review finding (the regex was the riskiest line in the
change and had zero coverage) and exercises the cmd/POSIX flag changes
called out in M2.

Refs: cline/cline#10948

* docs(terminal): drop issue references and clarify windowsHide comment

Remove inline issue-number references from source comments and a test
name; that context belongs in the commit history, not the code. Also add
a one-line note that windowsHide is a no-op on non-Windows platforms,
since it is set unconditionally while the surrounding comment is
Windows-specific.

No behavior change.

* refactor(terminal): drop warn on the non-completion return path

The orchestrator's final fall-through return is a normal, expected path:
the process resolved via `continue` without a `completed` event (e.g. a
terminal mode without shell integration, or proceed-while-running flows).
Logging it at `warn` cries wolf on healthy runs, so remove it. The
genuine failure mode this was meant to catch surfaces through the
`close`/error logs and the result string itself.

* fix(terminal): address review feedback on standalone spawn paths

Three follow-ups from code review:

* StandaloneTerminalManager.runCommand: the unawaited process.run()
  .catch only logged. run() emits "error" for failures it catches, but a
  rejection escaping without an "error" event would leave the outer
  promise (resolved via the "continue"/"error" events) pending forever,
  stalling the caller. Re-emit "error" from the catch so both paths stay
  consistent. Cannot trigger today (no await outside run()'s try/catch)
  but the guard exists precisely for future rejections.

* shellArgs POSIX branch: document that dropping the login flag (`-l`)
  is intentional and relies on the child inheriting the parent's PATH via
  process.env, with a note that a GUI-launched IDE without a login PATH
  is the edge case to watch.

* StandaloneTerminalProcess cmd.exe branch: add windowsHide:true. The
  console-allocation/window-pop problem is not exclusive to the non-cmd
  branch; a console-less parent could pop a window for cmd.exe too.
  No-op on non-Windows.
2026-06-24 14:11:28 +09:00
Ara 9c030a93b6 Remove Explain Changes feature (#11278)
* chore(vscode): remove explain changes entry points

* chore(vscode): remove explain changes feature
2026-06-24 14:11:28 +09:00
Max Paulus 🥪 f9fcf7f5ce make extension plan mode more similar to CLI
- basically, don't auto continue when agent switches to act mode
2026-06-24 14:11:28 +09:00
Max Paulus 🥪 c4cf2743d6 fix zai insufficient credits issue 2026-06-24 14:11:28 +09:00
Max Paulus 🥪 1dbfb50038 fix tool use name sanitization 2026-06-24 14:11:28 +09:00
Max Paulus 🥪 9b548347ef fix broken tsc 2026-06-24 14:11:28 +09:00
Dominic Cooney 2dc5791d46 fix(vscode): exclude vitest src/sdk suites from CommonJS test compile
compile-tests runs 'tsc -p tsconfig.test.json' (module: commonjs) over all
src/**/*.test.ts for the VS Code integration runner. The new src/sdk vitest
suites use top-level 'await import(...)' (after vi.mock), which is invalid
under CommonJS and fails with TS1378. The integration runner never runs
src/sdk anyway (.vscode-test.mjs only globs core/test/utils/shared/
integrations/hosts/services); these run via 'npm run test:vitest'. Exclude
src/sdk/**/*.test.ts from the integration compile.
2026-06-24 14:11:27 +09:00
Dominic Cooney 35e3bb4787 fix(vscode): restore biome --config-path so lint resolves apps/vscode/biome.jsonc
The rebase dropped '--config-path ./biome.jsonc' from the lint/format/
postprotos scripts and removed the '!!**/.vscode-test' ignore from
biome.jsonc. Without the explicit config path, biome auto-discovered the
root biome.json instead of apps/vscode/biome.jsonc, applying the wrong
rule severities (449 errors at error level for rules that are off/info in
the nested config). Restore both to match origin/main and apply the
pending buf format fix to models.proto.
2026-06-24 14:11:27 +09:00
Dominic Cooney 205a539491 fix(vscode): show running state for in-progress commands
The command row reflects an executing state while a command runs. The
message translator includes the command-output marker on the running
command row so the webview renders it as executing; the row is finalized
with output and a completed flag when the command ends.

Also remove the unused onChange parameter from the foreground run_commands
path: the SDK runtime does not pass it, so it had no effect. Foreground
command output is surfaced to the chat at completion, not incrementally.

Fixes CLINE-2298 and CLINE-2162
2026-06-24 14:11:27 +09:00
Dominic Cooney 0175548dd8 fix(vscode): re-enable approval buttons for consecutive asks
The footer Approve/Reject buttons stayed disabled when a second consecutive
approval ask arrived. The button configs are shared singletons (e.g.
BUTTON_CONFIGS.tool_approve), so two identical asks return the same object
reference and the effect that reset the processing latch never re-ran.

Key the processing latch on the ask identity (anchored turn timestamp plus the
button labels) rather than the config object reference, using a ref-based latch
so each new ask re-enables the buttons. Adds a regression test.

Test plan:

1. Ask the agent to generate two requests to ls /tmp at once

2. Approve (or reject) the first request

3. Check that the buttons for the second request are enabled
2026-06-24 14:11:27 +09:00
Dominic Cooney 6acc231da5 feat(vscode): add the VS Code Language Model (vscode-lm) provider
Run Cline inference through the VS Code Language Model API (vscode.lm), enabling
models contributed by any extension that registers a language model chat
provider with VS Code. GitHub Copilot is the most common such vendor, but the
implementation is vendor-agnostic — it selects models via
vscode.lm.selectChatModels and has no Copilot-specific logic.

- VsCodeLmHandler implements the Cline SDK ApiHandler and is registered with the
  SDK handler registry; the model selector travels as a vendor/family[/version/id]
  string in modelId and is parsed back here. Selector segments are
  percent-encoded so values containing slashes round-trip intact.
- Native tool calling: tool definitions are passed to sendRequest and tool calls
  are surfaced as tool-call chunks; tool results round-trip as
  LanguageModelToolResultPart, with structured tool output serialized to text and
  a trailing user message appended when a turn ends on tool results so models can
  read the output.
- Gated to VS Code: registration is conditioned on the vscode.lm API being
  present, and the provider is hidden in the UI on hosts without it (JetBrains).

Depends on @cline/{shared,llms,agents,core} 0.0.42-nightly.1780514867, the first
published SDK build with the custom-registered-handler routing this provider
needs.
2026-06-24 14:11:27 +09:00
Ara d2ac39455b Fix approval chat replies rendering as tool errors (#11246)
* fix(vscode): route approval chat replies as user feedback

* fix(vscode): suppress approval reply denial errors

* fix(vscode): hide rejected approval tool failures

* chore(vscode): clarify denied approval suppression helper
2026-06-24 14:11:27 +09:00
Max Paulus 🥪 87543e6f47 Persist OpenRouter provider config via catalog hook 2026-06-24 14:11:27 +09:00
Max Paulus 🥪 3757a8fd0d persist openai-codex provider model settings
- also don't show a sign in button if openai-codex is the only provider
2026-06-24 14:11:27 +09:00
Max Paulus 🥪 0e9a5c0fc1 Persist Cline model selections to provider config 2026-06-24 14:11:27 +09:00
Dominic Cooney 8aba515d6b fix(vscode): declare missing direct deps @grpc/proto-loader and @opentelemetry/api-logs
Both packages are imported directly from source but were never declared in
apps/vscode/package.json, so they only resolved transitively. On a clean
install this broke:

- @grpc/proto-loader — imported by scripts/proto-utils.mjs,
  src/standalone/utils.ts and src/standalone/hostbridge-client.ts; its absence
  made `npm run protos` (and therefore the whole build) fail on a fresh checkout.
- @opentelemetry/api-logs — imported by the OpenTelemetry telemetry providers;
  its absence produced TS2307 "Cannot find module" errors under tsc.

Versions are pinned to align with the existing dependency families already
declared in this package (@grpc/grpc-js ^1.9.x → proto-loader ^0.7.13;
the @opentelemetry/* 0.56.x line → api-logs ^0.56.0). The npm and bun
lockfiles are updated accordingly (the api-logs change also dedupes several
previously-nested copies to a single hoisted entry).
2026-06-24 14:11:27 +09:00
Max Paulus 🥪 a2194f4908 show legacy task history that is not saved in the ~/.cline folder 2026-06-24 14:11:26 +09:00
Max Paulus 🥪 f5d0b4fd49 add migration telemetry 2026-06-24 14:11:26 +09:00
Ara 61ea8688c6 fix(vscode): reuse approved tool rows (#11213)
* fix(vscode): reuse approved tool rows

* fix(vscode): clear stale approved tool rows
2026-06-24 14:11:26 +09:00
Dominic Cooney 94f5a47a59 sdk migration: squashed pre-2026-06-02 work
Omnibus squash of the 10 oldest SDK-migration commits (authored 2026-05-27
through 2026-06-02), collapsed during the 2026-06-09 rebase onto origin/main.

Squashed commits:
- sdk migration: squashed pre-2026-05-27 work
- sdk migration: squashed 06-05-2026 -- instead of listHistory, use host.get(sessionId) instead
- updat gitignore
- fix xai provider
- fix(vscode): forward Bedrock region + AWS auth to the SDK gateway
- fix(vscode): keep in-progress MCP OAuth flow across reconnects
- fix(vscode): wire auto compact into SDK sessions (#11197)
- fix(vscode): compact Codex OAuth before input cap (#11194)
- fix unauthed user flow
- fix(llms): strip Cerebras reasoning history (#11214)
2026-06-24 14:11:26 +09:00
John Choi 864419d8b0 Fix ClinePass onboarding model grouping (#11768) 2026-06-23 17:38:30 -07:00
Tomás Barreiro 218db38544 Add to the status bar [ENG-2216] (#11766)
* Add  to the status bar

* Update tests
2026-06-24 01:24:46 +02:00
Bee a3b3295461 fix(llms): includes session id for OpenRouter caching (#11744)
* Improve OpenRouter sticky session routing

OpenRouter prompt caching was getting weaker cache hit rates because requests did not include a stable session_id. Without that identifier, OpenRouter can route consecutive turns away from the same upstream cache context even when cache_control is present, causing more fresh input billing.

Propagate explicit runtime sessionId metadata from agents into model requests without synthesizing fallback session or conversation ids. Add provider metadata for sticky sessions so OpenRouter maps metadata.sessionId to the JSON body session_id field, while keeping the mechanism extensible for header-based providers.

Apply sticky-session metadata in the AI SDK provider fetch wrapper, preserving explicit wire values when callers already set them. Move trimNonEmpty and omitUndefinedValues into @cline/shared for reuse, and cover the JSON-body, header, and no-fallback paths with tests.

* Tighten sticky session fetch body handling

Treat init.body null as an explicit no-body override so the sticky-session wrapper does not inspect or parse a Request body that the caller has intentionally overridden.

Model fetch body text by source, which removes the unreachable JSON-body injection branch and makes the Request rewrite path explicit. Also type the provider fetch mocks so sticky-session assertions compile cleanly in editors.

* merges model-handle default metadata with per-request metadata
2026-06-23 12:07:17 -07:00
Max d09270940f fix(hub): validate dashboard websocket host and origin (#11724)
* fix(hub): validate browser websocket origins

* test(hub): cover websocket host and origin gates

* fix(hub): allow explicit bind host for dashboard socket

* fix(hub): default protect browser routes

* fix(hub): normalize default port origins

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-23 10:14:25 -07:00
Saoud Rizwan 23c9bf1c05 fix: prevent 'ERROR: EMPTY CONTENT' message when error occurs (#11726)
* fix: prevent empty assistant history replay

* refactor: move tool call filtering after empty guard
2026-06-23 09:33:10 -07:00
Bee 52531d935a refactor: add connector configure path and share catalog to shared pkg (#11730)
* feat(hub): add connector configure path and share catalog via @cline/shared

Add connector.channels/configure/delete_config Hub commands that persist
connector settings to disk without starting connector processes or calling
provider auth APIs. This means settings (including tokens) are saved as-is
without verification; callers are responsible for supplying valid values.

Shared catalog and platform definitions:
- Move ConnectorCatalogEntry, CONNECTOR_CATALOG, listConnectorCatalog, and
  all ConnectorPlatformDef/FieldDef/SecurityDef types + CONNECTOR_PLATFORMS
  into sdk/packages/shared/src/connectors/platforms.ts
- Export everything from @cline/shared index so CLI and Hub use the same
  definitions without duplication
- Reduce apps/cli/src/connectors/catalog.ts and
  apps/cli/src/wizards/connect/platforms.ts to thin re-export shims that
  preserve existing CLI import paths

New Hub connector handlers (sdk/packages/core):
- connector-handlers.ts: handles connector.channels (list available/active/
  configured), connector.configure (validate fields and write settings.json
  under ~/.cline/data/connectors/), connector.delete_config (remove entry
  and clean up empty file)
- Settings are stored as ConnectorSettingsFile (version 1) at
  ~/.cline/data/connectors/settings.json; reads are lenient/defensive
- Wire handlers into hub-server-transport.ts dispatch switch
- connector-handlers.test.ts: unit tests for configure, channels, and
  delete_config covering field validation, conditional fields, security
  constraints, and settings round-trips

Hub WebSocket auth helpers (hub-websocket-server.ts):
- Extract isLocalHubHostName / isLocalHubOrigin as named, tested exports
- Allow unauthenticated WebSocket upgrades from local origins (localhost,
  127.0.0.1, ::1) so the Hub UI can connect without an auth token
- hub-websocket-server.test.ts: extend tests to cover new local-origin logic

Add connector.channels, connector.configure, connector.delete_config to
HubCommandName union in sdk/packages/shared/src/hub.ts

* apply feedback

* export connector settings json path

* apply feedback from robin
2026-06-22 22:25:49 -07:00
Tomás Barreiro 19d4248381 Add Organization error messages to the Cline CLI (#11737)
* Add Organization error messages to the Cline CLI

* Fix tests

* Fix tests

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-22 17:18:29 -07:00
Tomás Barreiro b2b29678a2 Add a button to unselect an org on ClinePass error (#11738)
* Add a button to unselect an org on ClinePass error

* Fix comments
2026-06-23 01:47:15 +02:00
Bee ad30714608 fix(hub-ui): Use development mode conditionally for connector (#11728)
* fix(hub-ui): Use development mode conditionally for connector

Refactor connector CLI launch logic to conditionally apply Bun-specific
flags (`--conditions=development`) only when running under Bun or Node
runtimes with access to the source entrypoint. When running from a
compiled binary (e.g., packaged Cline app), use the execPath directly
without Bun flags.

- Extract `buildCliConnectCommand` function to encapsulate launcher
  and args resolution logic based on runtime detection
- Use `withResolvedClineBuildEnv` for environment variable setup
- Export `__test__` object to enable unit testing of internal logic
- Add tests covering Bun source entrypoint and compiled binary cases

* address feedback
2026-06-22 16:41:47 -07:00
Bee 9eebc44f24 chore: Optimize Cline Hub webview bundle (#11731)
* Optimize Cline Hub webview bundle

Issue:

The Cline Hub webview build emitted Vite's large chunk warning. The main entry bundle was over 2 MB minified, and the build output included many Shiki language/theme chunks plus eagerly bundled Streamdown math/Mermaid support that the hub did not need on startup.

Changes:

- Split heavyweight Cline Hub views behind React.lazy/Suspense so settings, customization, and chat code do not all land in the initial app shell.

- Replace root Shiki highlighter usage with shiki/core and lazily loaded, explicitly supported languages/themes.

- Route Streamdown through a local HubStreamdown wrapper that uses the local CodeBlock renderer and keeps Mermaid as a lazy diagram plugin.

- Remove unused Streamdown math/code/Mermaid packages, add direct lazy Mermaid support, and add targeted Rolldown chunk groups for Mermaid parser/layout/markup assets.

Before/after bundle measurements:

| Metric | Before | After |

| --- | ---: | ---: |

| Total generated JS | 24,356 KiB | 5,800 KiB |

| Total generated JS gzip | 4,805.5 KiB | 1,434.6 KiB |

| Main entry chunk | 2,044.02 kB | 373.88 kB |

| Main entry gzip | 617.25 kB | 117.83 kB |

Verification:

- bun -F @cline/cline-hub build:webview

- bun biome check apps/cline-hub/src/webview/vite.config.ts apps/cline-hub/src/webview/src/App.tsx apps/cline-hub/src/webview/src/components/ai-elements/code-block.tsx apps/cline-hub/src/webview/src/components/ai-elements/message.tsx apps/cline-hub/src/webview/src/components/ai-elements/reasoning.tsx apps/cline-hub/src/webview/src/components/ai-elements/streamdown.tsx apps/cline-hub/src/webview/package.json

* address feedback
2026-06-22 16:40:01 -07:00
Tomás Barreiro 6ddf48d227 Forcefully send users to their personal dashboard when going to subscribe (#11725)
* Forcefully send users to their personal dashboard when going to subscribe

* fix trailing slash
2026-06-22 22:27:31 +02:00
Saoud Rizwan ee59f81706 chore(cli): release v3.0.29 2026-06-20 00:30:51 -07:00
Saoud Rizwan 92f6e28f13 fix(cli): hide costs for Cline free models (#11686)
* fix(cli): hide Cline free model costs

* fix(cli): harden free model cost lookup
2026-06-20 00:20:07 -07:00
Saoud Rizwan 27a78f0248 chore(sdk): release v0.0.51 2026-06-19 19:15:00 -07:00
Saoud Rizwan 2c4980f42a fix(sdk): resolve Cline Z.ai model metadata aliases (#11685)
* fix(sdk): resolve Cline Z.ai model metadata aliases

* fix(sdk): preserve Cline model alias overrides

* test(sdk): update Cline provider model list expectation
2026-06-19 19:05:47 -07:00
Saoud Rizwan 64c5e48edb Revert "fix(cli): resolve Cline model display names by just model name (#11668)" (#11684)
This reverts commit c497698beb.
2026-06-19 18:27:29 -07:00
Saoud Rizwan 3c23f80a94 fix(sdk): deflake run_commands timeout telemetry tests 2026-06-19 14:59:46 -07:00
Saoud Rizwan 4be362892f chore(cli): release v3.0.28 2026-06-19 14:52:50 -07:00
Saoud Rizwan cdff084652 chore(sdk): release v0.0.50 2026-06-19 14:11:25 -07:00
Robin Newhouse 299a4a9520 fix(sdk): harden parallel tool guidance (#11598)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-19 12:25:20 -07:00
Saoud Rizwan c497698beb fix(cli): resolve Cline model display names by just model name (#11668)
* fix(cli): resolve Cline model display names by slug

* fix(cli): type model display metadata

* test(cli): cover model display fallback paths
2026-06-19 12:21:57 -07:00
Ara 292133989b fix(sdk): cap assistant text in provider messages (#11478) 2026-06-19 11:41:04 -07:00
Ara f5aa035a6a fix(sdk): default-on tool result truncation, tightened limits + budget accounting in MessageBuilder (#11475)
* fix(sdk): default-on tool result truncation, name fallback, tool_use budget accounting

- Truncate every tool result (MCP/custom tools included), not just an allowlist
- Resolve tool names from tool_result.name when the paired tool_use is gone
- Count tool_use.input strings toward the aggregate provider request budget
- Protect any binary carrier block ({type, data}) from truncation, not just images
- Don't let failSession cleanup errors mask the original turn error

* fix(sdk): tighten MessageBuilder limits with named options and env overrides

Folds #11474 into the default-on truncation branch and addresses review
feedback from both PRs:

- per-result tool cap drops to 8k chars; MessageBuilderOptions constructor
  with CLINE_MESSAGE_BUILDER_* env overrides for A/B testing
- env parsing is positive-only so '=0' cannot silently disable a limit
  (greptile on #11474)
- aggregate budget stays at 6MB: budget truncation rewrites mid-transcript
  bytes and breaks provider prefix caching, so it must stay a rare overflow
  valve rather than the steady state (johnwschoi on #11474)
- user file attachments get a dedicated 50k cap instead of inheriting the
  aggressive tool-result cap (codex on #11474)
- isBinaryContentLike restricted to known binary block types so textual
  {type, data} payloads can no longer dodge every cap (codex/greptile)
- tool_use.input strings become last-resort budget truncation candidates,
  making the aggregate budget reclaimable when oversized model-generated
  arguments carry the overflow (robinnewhouse/greptile)

* fix(sdk): constrain binary tool result truncation

* style(sdk): remove disallowed comment formatting

* Revert "style(sdk): remove disallowed comment formatting"

This reverts commit 9d8b66726d.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-19 11:15:42 -07:00
Saoud Rizwan bbcae25542 fix(cli): refine marketplace primitive rows (#11659) 2026-06-19 10:57:37 -07:00
Tomás Barreiro 9b914912ac Rename Cline Pass to ClinePass (#11657) 2026-06-19 18:36:07 +02:00
Saoud Rizwan 8c369eeed9 docs(vscode): remove marketplace readme language links (#11658) 2026-06-18 18:09:02 -07:00
Saoud Rizwan 85223a07cf fix(cli): apply auto-approve toggles immediately (#11653) 2026-06-18 15:58:31 -07:00
John Choi eb2687677c feat(onboarding): add Cline Pass to the signup flow (#11609)
* Add Cline Pass provider to VS Code extension

* Fix the model picker

* ClinePass specific options

* Add feature flag to the api options

* Extend flag usage

* fix type error

* Use the right model info

* Fix tests

* Hide price info for clinePass models

* feat(onboarding): add Cline Pass as optional user-type in signup flow

Surface Cline Pass as a recommended-but-optional onboarding choice gated
behind the ext-cline-pass feature flag, alongside Free / Frontier / BYOK.
When selected, signup provisions the cline-pass provider and ClinePass
model fields; price info is hidden since cost is covered by the
subscription. Falls back cleanly to the existing flow when the flag is
off. Adds unit tests for the new helpers.

* feat(onboarding): nudge + Cline Pass subscribe link in signup flow

Strengthen the Cline Pass onboarding option with a 'Recommended' copy
nudge (text + ordering, no badge/paid-default), and add an additive
subscribe affordance on the post-signup 'Almost there!' step that links
to {appBaseUrl}/dashboard/plan. Client-only: reuses existing appBaseUrl
from useClineAuth and the existing dashboard subscribe page; no backend
change. Shown only when the user selects Cline Pass; existing flow and
other user-types are unchanged.

* refactor(onboarding): redirect Cline Pass signup to subscription page

Replace the manual 'Get Cline Pass' callout with an automatic redirect:
after a Cline Pass user completes account creation, open the dashboard
subscription signup page (/onboarding/individual-plan) in the browser.
Keep the free option first and default-selected, with Cline Pass shown
second and labeled '(Recommended)'.

* chore(onboarding): tighten inline comments

* fix(onboarding): constrain Cline Pass selection and clear stale subscribe redirect

Addresses review findings:
- Never save a non-Cline-Pass model id under the cline-pass provider:
  Cline Pass selection no longer falls back to a free model when the
  Cline Pass list is empty, the generic model search is hidden for Cline
  Pass, finishOnboarding guards on a cline-pass/ id, and the CTA is
  disabled (with an empty state) when no Cline Pass model is available.
- Clear the pending subscription redirect when the user backs out, signs
  in, or navigates away from Cline Pass, and re-check userType in the
  redirect effect so a late auth update can't force the paid flow.

* fix(onboarding): add userType to handleFooterAction deps

The signup handler reads userType (to set the pending Cline Pass
subscribe flag); make that dependency explicit rather than relying on
the transitive finishOnboarding dependency.

* fix(onboarding): gate Cline Pass model data by feature flag

* fix(onboarding): address Greptile review on Cline Pass signup

- Log when the Cline Pass provider write is skipped due to an unexpected
  (non cline-pass/) model id, so the otherwise-silent no-op is observable.
- Open the subscription page for already-authenticated users by invoking
  the redirect helper directly after accountLoginClicked resolves, not
  only via the auth effect (which never re-runs when clineUser is
  unchanged).

* fix(onboarding): preserve ClinePass selection through login + one-word branding

- handleAuthCallback no longer forces the provider back to 'cline' on
  login when the user picked Cline Pass during onboarding; it preserves a
  'cline-pass' selection per mode. Only 'cline-pass' configs are affected
  (which require the ext-cline-pass flag), so all other logins are
  unchanged.
- Open the subscription page solely from the clineUser auth effect; the
  signup action no longer also calls it directly (prevents the login
  browser and subscribe page opening at once / dead-ends for already-
  authenticated users). Signup flow is otherwise untouched.
- Rename user-facing 'Cline Pass' to one-word 'ClinePass' (card titles,
  step title, empty state) and the model group label to 'clinepass'.

* chore(onboarding): normalize ClinePass branding in comments, trim verbose comments

* fix(merge): resolve ApiOptions redeclare + apply biome semicolon formatting

- Remove duplicate CLINE_PASS_FEATURE_FLAG const in ApiOptions.tsx (the
  shared import from constants/featureFlags supersedes main's local const).
- Apply biome format (semicolons) to onboarding/controller files so they
  match main's current style and pass Quality Checks.

* fix: restore clean no-semicolon formatting, keep only real ClinePass changes

The previous merge-recovery commit ran a local biome that incorrectly added
semicolons across entire files, bloating the diff by ~2000 LOC. Restore the
files to their clean pre-format state (matching main's asNeeded style) so the
diff reflects only the actual ClinePass onboarding changes (~420 LOC). Keeps
the handleAuthCallback provider-preservation fix and the ApiOptions duplicate
const removal.

* chore(onboarding): trim redundant inline comments

* chore: match main in refreshClineRecommendedModels (drop snake_case cline_pass handling)

* chore: trim featureFlags.ts comment

* fix(onboarding): open ClinePass subscribe page from App, not OnboardingView

handleAuthCallback marks the welcome view completed (unmounting OnboardingView)
before it pushes the auth-status update that sets clineUser. The redirect effect
lived in OnboardingView, so the pending-subscribe intent was lost on unmount and
the subscription page never opened for new ClinePass users.

Move the pending intent to a module-level store (clinePassSubscribe.ts) and run
the redirect from an effect in App, which outlives the onboarding unmount.

---------

Co-authored-by: BarreiroT <tomasmbarreiroi@gmail.com>
2026-06-18 14:45:34 -07:00
Tomás Barreiro 6fe5acb2a4 Resolve feature flags using the user id on startup (#11652) 2026-06-18 23:24:42 +02:00
Tomás Barreiro 99fb5101e5 Improve ClinePass error handling (#11637)
* Make sections expandable and add ClinePass

* fix label

* Map clinePass models to clinePass and the rest to cline

* gate the models behind the feature flag

* Do not allow custom model on cline pass

* Do not show the count on the mode list

* fix clinepass model list

* fix focus when expanding a section

* update the model data when the provider doesn't match

* Add ClinePass to the onboarding screen

* Fix the auth flow not starting

* Hide option behind a feature flag

* Update icon

* Remove unrelated changes

* Hide the custom model id

* fix model names

* remove custom model id option

* fix tests

* Fix names

* Display the clinepass models in the onboarding

* Throw a specific error when the user isn't subscribed

* properly render the error message

* fix error detection

* refactor

* Update the ResponseErrorHandler type

* re-add trailing slash
2026-06-18 22:18:12 +02:00
Tomás Barreiro 58db9887ee Clear organization on ClinePass selection on the VSCode extension (#11648)
* Clear organization on ClinePass selection on the VSCode extension

* Disable remote config if ClinePass is selected

* Disable remote config if ClinePass is selected

* Revert formatting changes

* fix issue
2026-06-18 22:04:49 +02:00
Ahmad Shahzad b16a28cd6f feat(vscode): add Fireworks GLM 5.2 and Kimi K2.6 Fast, fix DeepSeek V4 Flash cache-read price (#11642)
Update the Fireworks model registry in apps/vscode/src/shared/api.ts:

- Add accounts/fireworks/models/glm-5p2: new general-purpose model with
  a 1,048,576-token context window (131,072 output).
- Add accounts/fireworks/routers/kimi-k2p6-fast: standardizes the
  Kimi K2.6 router on the `-fast` naming convention used for /routers.
  The existing kimi-k2p6-turbo entry is retained for now to avoid
  breaking user workflows; the turbo variant is expected to be removed
  in a future release once the fast variant is the only one served.
- Correct the cache-read price for accounts/fireworks/models/deepseek-v4-flash
  from 0.03 to 0.028 to match the published rate.

Pricing sourced from https://docs.fireworks.ai/serverless/pricing.
2026-06-18 21:18:01 +02:00
John Choi 114aa3f161 Add friendly Cline Pass entitlement error UI (#11606)
* Add Cline Pass provider to VS Code extension

* Fix the model picker

* ClinePass specific options

* Add feature flag to the api options

* Extend flag usage

* fix type error

* Use the right model info

* Fix tests

* Hide price info for clinePass models

* Add friendly Cline Pass entitlement error UI

When a Cline Pass model returns a 403 ENTITLEMENT_ERROR (user not subscribed to the required model plan), the chat dumped the raw serialized error JSON and pointlessly auto-retried it.

- Add ClineErrorType.Entitlement, classified before the generic 403/auth path.
- Skip auto-retry (and the retries-exhausted message) for entitlement errors.
- Render a dedicated EntitlementError card: friendly headline, env-aware 'Get Cline Pass' subscribe link (clineUser.appBaseUrl, falling back to production), and a Retry button; backend detail is shown as muted support text.
- Add unit/component tests and a Storybook story.

* Scope entitlement to individual case; fix path-prefixed subscribe URL

- ClineError: only classify the individual 'not subscribed' ENTITLEMENT_ERROR as Entitlement (checks message and details.message). Org-account variant falls through to generic handling rather than showing a misleading 'Get Cline Pass' card.

- EntitlementError: build the subscribe URL with a relative path against a trailing-slash-normalized base so path-prefixed self-hosted/proxy app URLs (e.g. https://proxy.example.com/cline/app) are preserved instead of resetting to origin.

- Tests: add org-exclusion case, path-prefixed URL case, and assert the retry askResponse payload (yesButtonClicked).

* Trim redundant inline comments

* Add provider test: pre-stream 403 entitlement error classifies correctly

Documents and locks in that a 403 ENTITLEMENT_ERROR (which rejects completions.create before streaming, as an OpenAI SDK APIError) propagates through ClineHandler with the code intact so ClineError classifies it as Entitlement. Confirms the error does not rely on the mid-stream chunk.error path.

* Guard subscribe URL build against malformed appBaseUrl

Wrap new URL() in try/catch so an invalid appBaseUrl from the auth context omits the subscribe link instead of throwing TypeError and crashing the EntitlementError card mid-render. Addresses Greptile review feedback; adds a malformed-URL test.

* test: trim entitlement error UI coverage

* Use one-word 'ClinePass' in user-facing copy

Renames the display text in the entitlement card (headline, helper, button) and related tests/story/comments from 'Cline Pass' to 'ClinePass'. Also normalizes EntitlementError.test.tsx formatting to the webview Biome style.

* fix: skip subagent retries for ClinePass entitlement errors

---------

Co-authored-by: BarreiroT <tomasmbarreiroi@gmail.com>
Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>
2026-06-18 12:06:40 -07:00
Tomás Barreiro 64743d2911 Improve the ClinePass UX (#11644) 2026-06-18 20:39:52 +02:00
Tomás Barreiro d7292993e9 Add Cline Pass to the extension (#11556)
* Add Cline Pass provider to VS Code extension

* Fix the model picker

* ClinePass specific options

* Add feature flag to the api options

* Extend flag usage

* fix type error

* Use the right model info

* Fix tests

* Hide price info for clinePass models

* refactor

* remove redundancy

* fix syncModeConfigurations
2026-06-18 20:19:46 +02:00
Tomás Barreiro 8110cc46d8 Add the ClinePass onboarding flow (#11590)
* Make sections expandable and add ClinePass

* fix label

* Map clinePass models to clinePass and the rest to cline

* gate the models behind the feature flag

* Do not allow custom model on cline pass

* Do not show the count on the mode list

* fix clinepass model list

* fix focus when expanding a section

* update the model data when the provider doesn't match

* Add ClinePass to the onboarding screen

* Fix the auth flow not starting

* Hide option behind a feature flag

* Update icon

* Remove unrelated changes

* Hide the custom model id

* fix model names

* remove custom model id option

* fix tests

* Fix names

* Display the clinepass models in the onboarding
2026-06-18 19:00:18 +02:00
Saoud Rizwan a3cd39da14 feat(cli): update hub dashboard design with customizations break out (#11631)
* feat(cli): refresh hub dashboard navigation

* fix(cli): address hub dashboard review feedback

* feat(cli): polish hub dashboard mockup styling

* fix(cli): polish hub dashboard pages

* fix(cli): brand hub browser metadata

* fix(cli): simplify marketplace entry details

* fix(cli): consolidate customization catalog lists

* fix(cli): dedupe marketplace installed rows

* fix(cli): preserve marketplace controls in consolidated rows

* fix(cli): align marketplace plugin controls

* fix(cli): flatten marketplace plugin tool layout

* fix(cli): place marketplace path below tags

* fix(cli): show primitive icons in marketplace rows

* fix(cli): sort sessions by displayed created time

* fix(cli): restore hub account navigation

* fix(cli): refine hub account navigation

* fix(cli): reuse hub account credentials

* fix(cli): support uninstalling local primitives
2026-06-17 21:55:31 -07:00
Saoud Rizwan a51b156383 feat(cli): add hub primitive catalogs (#11624)
* feat(cli): add hub marketplace

* chore(cli): clean up marketplace install code

* fix(cli): harden marketplace review issues

* feat(cli): add marketplace uninstall actions

* fix(cli): keep marketplace dialog actions in sync

* fix(cli): uninstall marketplace cards directly

* fix(cli): hide stale marketplace installed notice

* feat(cli): split hub marketplace pages

* fix(cli): show full marketplace descriptions

* fix(cli): avoid duplicate marketplace descriptions

* fix(cli): support marketplace deep refreshes

* fix(cli): hide marketplace placeholder icons

* fix(cli): remove marketplace card icons

* fix(cli): ignore featured marketplace entries

* chore(cli): clean marketplace follow-up code

* fix(cli): address marketplace review findings
2026-06-17 17:17:38 -07:00
Saoud Rizwan 7966bb9f21 chore(cli): release v3.0.27 2026-06-16 23:56:02 -07:00
Saoud Rizwan d92e4797ed fix(cli): reject accidental prompt fallback for bad args (#11615) 2026-06-16 23:52:43 -07:00
Saoud Rizwan 0de0c94555 fix(cli): clarify plugin MCP OAuth failure handling (#11614) 2026-06-16 23:38:28 -07:00
Saoud Rizwan 94b82fca3c feat(cli): add prefilled MCP install wizard command (#11610)
* feat(cli): add prefilled MCP install wizard command

* fix(cli): address MCP install review feedback
2026-06-16 23:07:16 -07:00
Saoud Rizwan ba2f2284e8 feat(cli): add cline skill command aliasing the open skills CLI (#11611)
* feat(cli): add cline skill command aliasing the open skills CLI

Adds a 'cline skill' command that forwards to Vercel's open skills CLI
via 'npx -y skills@latest <args>', giving parity with 'cline plugin
install' and 'cline mcp' without reimplementing skill installation.

install/add/i default to '--agent cline' (unless the user passes their
own -a/--agent) so installs land in a directory Cline already scans;
use/list/remove pass through verbatim.

* fix(cli): scope skill update to cline
2026-06-16 22:59:35 -07:00
Saoud Rizwan b7f955c78b chore(cli): release v3.0.26 2026-06-16 21:07:22 -07:00
Saoud Rizwan 8433549327 chore(sdk): release v0.0.49 2026-06-16 20:43:23 -07:00
Saoud Rizwan 860f544ab8 Revert "Make sections expandable and add ClinePass models (#11582)" (#11608)
This reverts commit 2fd944cb84.
2026-06-16 20:33:44 -07:00
Saoud Rizwan 0761de984b chore(cli): release v3.0.25 2026-06-16 19:35:02 -07:00
Saoud Rizwan 940b43f72f chore(sdk): release v0.0.48 2026-06-16 19:20:04 -07:00
Saoud Rizwan a466060f79 fix(llms): avoid disabled reasoning for StepFun flash (#11597)
* fix(llms): avoid disabled reasoning for StepFun flash

* fix(llms): cover StepFun flash reasoning variants
2026-06-16 19:07:50 -07:00
Tomás Barreiro 2fd944cb84 Make sections expandable and add ClinePass models (#11582)
* Make sections expandable and add ClinePass

* fix label

* Map clinePass models to clinePass and the rest to cline

* gate the models behind the feature flag

* Do not allow custom model on cline pass

* Do not show the count on the mode list

* fix clinepass model list

* fix focus when expanding a section

* update the model data when the provider doesn't match

* Update sdk/packages/core/src/services/llms/cline-recommended-models.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* style: format Cline recommended models fallback

* Build models

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-17 04:02:27 +02:00
Tomás Barreiro 7d1abb25a7 Update fixed model list (#11591) 2026-06-17 03:28:35 +02:00
Saoud Rizwan 628aaa0675 feat(cli): authorize plugin MCP OAuth during install (#11575) 2026-06-16 17:46:19 -07:00
Robin Newhouse af7fda87c0 ENG-2184 Add generic provider-request capture for SDK CLI (#11481)
* Add generic provider request capture

* Use per-request provider capture files
2026-06-16 16:49:46 -07:00
Tomás Barreiro 9af6ced896 Rename Cline Pass to ClinePass everywhere (#11584)
* Rename Cline Pass to ClinePass everywhere

* Update apps/vscode/src/utils/path.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-06-16 21:50:40 +02:00
Ahmad Shahzad a3258dd79a feat: update Fireworks AI model registry with latest platform offerings (#11554)
The VS Code extension's Fireworks model list is out of date compared to the current active models available on the Fireworks platform. This commit updates the registry to match the current model lineup, ensuring users can select from the latest available models.

Changes:
- Add Kimi K2.7 Code (accounts/fireworks/models/kimi-k2p7-code)
- Add Kimi K2.7 Code Fast (accounts/fireworks/routers/kimi-k2p7-code-fast)
- Add Qwen 3.7 Plus (accounts/fireworks/models/qwen3p7-plus)
- Add MiniMax M3 (accounts/fireworks/models/minimax-m3)
- Remove deprecated Kimi K2.5 (accounts/fireworks/models/kimi-k2p5)
- Remove deprecated MiniMax M2.5 (accounts/fireworks/models/minimax-m2p5)
- Remove deprecated Qwen 3.6 Plus (accounts/fireworks/models/qwen3p6-plus)

The default model remains accounts/fireworks/models/kimi-k2p6.

Files:
- apps/vscode/src/shared/api.ts
2026-06-16 14:23:55 +02:00
Saoud Rizwan ebb05ed963 feat: add MCP support to plugins (#11516)
* feat: add MCP support to plugins

* fix: address plugin MCP review comments

* feat: sync plugin MCP servers to settings

* fix: tighten plugin MCP settings behavior

* fix: isolate plugin MCP sync failures

* fix: prune plugin MCP cleanup paths

* fix: surface plugin MCP re-enable failures

* fix: surface plugin MCP install sync failures

* fix: order plugin MCP state transitions
2026-06-15 19:44:46 -07:00
Tomás Barreiro 8d03d176f2 Fix WebView env replacing (#11574)
* Fix WebView env replacing

* fix node env resolution

* fix node env resolution

* fix node env resolution

* Fix platform reference

* Fix platform reference
2026-06-16 04:09:32 +02:00
Max 32b3cfc081 fix hugging face url (#11567)
hugging face inference url was incorrect

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-15 14:26:47 -07:00
Tomás Barreiro a6500a07b4 Enable feature flags on vscode (#11566)
* Enable feature flags on vscode

* Remove comment

* refactor

* fix
2026-06-15 22:52:21 +02:00
Max 81384089c4 allow dynamic models ids in huggingface provider (#11563)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-15 10:22:31 -07:00
Robin Newhouse 6364792c47 fix(cli): isolate history resume renderer ENG-2190 (#11502)
* fix(cli): isolate history resume renderer

* fix(cli): avoid duplicate history resume signals
2026-06-15 10:04:23 -07:00
Tomás Barreiro 40fc8879f1 Fix Postprotos modifying unrelated files (#11557) 2026-06-15 08:59:29 -07:00
Ara e91cba4045 fix(sdk): search output cap + bash executor fixes (follow-up to #11480) (#11504)
* fix(sdk): search output cap + bash executor fixes (follow-up to #11480)

Slimmed from the original revision: the aggregate per-call output budget
is deferred to its own follow-up PR. What remains:

- cap search_codebase output at 48k chars per query with a middle-cut
  notice teaching the model to narrow the pattern (robinnewhouse's
  finding on #11480 — search was the last uncapped tool)
- rename bash executor maxOutputBytes -> maxOutputChars; the limit was
  always enforced in characters. Deprecated alias retained; stale
  @default annotation fixed
- flush the rolling collector's StringDecoder at end-of-stream so
  trailing incomplete multibyte sequences are not silently dropped
  (greptile's finding on #11480)
- decouple output-limits comments from MessageBuilder's specific
  backstop value; the durable invariant is that truncation notices live
  in the preserved head/tail of an entry

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/ygz2qho62ub6o8v1zhjvdktt

* test(sdk): cover search output cap

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-14 19:22:39 -07:00
Robin Newhouse e5e1aa3455 Add bounded provider request media budget ENG-2191 (#11520)
* fix(sdk): bound provider request media payloads

* fix(sdk): address media review feedback

* fix(sdk): scrub media from error tool results

* chore(sdk): remove media budget docs noise

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-14 18:21:12 -07:00
Robin Newhouse d2893d2e93 fix(sdk): coalesce split heredoc run_commands (#11518)
* fix(sdk): coalesce split heredoc run_commands

* fix(sdk): address heredoc coalescing review

* test(sdk): cover split heredoc edge cases

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-14 16:47:16 -07:00
Saoud Rizwan 4fc366df5f fix(sdk): allow ranged reads on large files (#11511)
* fix(sdk): allow ranged reads on large files

* fix(sdk): bound ranged file reads

* fix(sdk): bound streamed file reads

* fix(sdk): simplify file read streaming bounds
2026-06-12 17:23:25 -07:00
Saoud Rizwan d8eb06318b fix(sdk): fail apply_patch when a hunk is skipped (#11509) 2026-06-12 16:46:48 -07:00
Tomás Barreiro fe4eb44c6b Unselect the org when selecting Cline Pass (#11501)
* Centralize OAuth management to the SDK

* Update mock

* Cleanup TUI cline-account logic

* clean save credentails

* Remove unused code

* Reduce mocks

* use normalizeStoredAccessToken

* Add Cline Pass

* Properly read storageProviderId

* Use the name for the model generation

* Use the model for the capabilities lookup

* Fix capability discovery

* Fix getLastUsedProviderSettings

* remove the provider id from the resolveWithSingleFlight return

* Fix tests

* Remove the entry.name check

* Execute model API calls separetely

* Hide Cline Pass pricing

* update model list

* Unselect the org when selecting Cline Pass

* deduplicate onProviderChange calls

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-13 01:44:54 +02:00
Saoud Rizwan 6c52bdc177 fix(sdk): return captured stdout on failing run_commands (#11508)
* fix(sdk): return captured stdout on failing run_commands

* fix(sdk): respect combineOutput on command failure
2026-06-12 16:42:08 -07:00
Saoud Rizwan 5260595472 fix(sdk): treat zero search results as success (#11510) 2026-06-12 16:35:00 -07:00
Tomás Barreiro a279388451 Add feature flag for cline pass (#11500)
* Centralize OAuth management to the SDK

* Update mock

* Cleanup TUI cline-account logic

* clean save credentails

* Remove unused code

* Reduce mocks

* use normalizeStoredAccessToken

* Add Cline Pass

* Properly read storageProviderId

* Use the name for the model generation

* Use the model for the capabilities lookup

* Fix capability discovery

* Fix getLastUsedProviderSettings

* remove the provider id from the resolveWithSingleFlight return

* Fix tests

* Remove the entry.name check

* Execute model API calls separetely

* Hide Cline Pass pricing

* Display cline-pass only if the feature flag is enabled

* unselect cline pass when the feature flag is off

* Store and read feature flag cache

* Add comment

* Do not return userId

* fix tests

* Revert unrelated changes
2026-06-13 01:29:29 +02:00
Saoud Rizwan 7810a81efe feat(sdk): encourage parallel tool calls (#11514)
* feat(sdk): encourage parallel tool calls

* fix(sdk): tighten tool execution return type

* test(sdk): remove prompt assertion test

* chore(sdk): restore existing prompt formatting

* chore(sdk): soften command batching wording

* chore(sdk): preserve tool execution default

* test(sdk): remove tool description assertions
2026-06-12 16:29:07 -07:00
Tomás Barreiro 2a54e2a76e Add cline pass (#11355)
* Centralize OAuth management to the SDK

* Update mock

* Cleanup TUI cline-account logic

* clean save credentails

* Remove unused code

* Reduce mocks

* use normalizeStoredAccessToken

* Add Cline Pass

* Properly read storageProviderId

* Use the name for the model generation

* Use the model for the capabilities lookup

* Fix capability discovery

* Fix getLastUsedProviderSettings

* remove the provider id from the resolveWithSingleFlight return

* Fix tests

* Remove the entry.name check

* Execute model API calls separetely

* Hide Cline Pass pricing

* update model list

* Address PR feedback

* revert unrelated changes

* Update comment

* Update check
2026-06-13 00:49:52 +02:00
Tomás Barreiro b7c38f76c9 Add buildtime variables for posthog (#11503)
* Add buildtime variables for posthog

* Apply changes
2026-06-13 00:25:22 +02:00
Saoud Rizwan d20e517831 feat(sdk): cap tool output ingestion for bash and file reads (#11480)
Tool outputs previously entered conversation history nearly unbounded
(1MB command output, whole-file reads up to 10MB) and were re-sent on
every subsequent request. Evals showed single observations of 350KB-3.2MB
dominating token spend versus opencode's 50KB-bounded observations.

- run_commands: combined stdout/stderr capped at 48,000 chars with
  head+tail sampling (middle elided with a notice reporting total size),
  since failures usually live at the end of build/test output. Failing
  commands carry the notice in stderr errors too. Streams decode through
  StringDecoder so multibyte chars split across chunks stay intact.
- read_files: whole-file and oversized-range reads windowed to 2,000
  lines / 48,000 chars with a notice reporting total line count and how
  to paginate via start_line/end_line. Per-line cap of 2,000 chars
  defangs minified files. In-window ranged reads are byte-for-byte
  unchanged; the 10MB stat guard stays.
- Shared constants live in executors/output-limits.ts, sized below
  MessageBuilder's 50,000 per-string backstop so source notices survive
  provider-request truncation intact. Tool descriptions document the
  windowing so the model pages or filters instead of retrying.

Companion to #11463/#11465: those bound provider requests at build time;
this bounds what enters history at the source and gives the model a
recovery path.
2026-06-12 11:51:48 -07:00
Tomás Barreiro fa3630da47 Add posthog for feature flags on the cli (#11491)
* Introduce PostHog as a Feature Flag provider

* Set-up auth after login

* Update the context when something changes in the CLI

* Make the distinctId not be optional

* Dispose of the feature flag service

* Remove the distinctId from the options

* get rid of isSharedClient

* Remove timeoutMs from the posthog options

* Rename functions to not refer cli

* Change the PostHogFeatureFlagsProvider API
2026-06-12 18:02:50 +02:00
Saoud Rizwan 8229d0c9be fix: format Cline OAuth tokens in provider config (#11489) 2026-06-11 20:03:46 -07:00
Saoud Rizwan efa14b6cab chore(cli): release v3.0.24 2026-06-11 14:27:27 -07:00
Saoud Rizwan c10b417b78 chore(sdk): release v0.0.47 2026-06-11 14:02:45 -07:00
Saoud Rizwan 9958e3f354 feat(cli): allow plugin commands to submit prompts (#11479)
* feat(cli): allow plugin commands to submit prompts

* fix(cli): preserve plugin command output on abort

* revert(cli): drop ineffective clear view tweak
2026-06-11 13:49:50 -07:00
Tomás Barreiro ec75291d5b Allow overriding the API base url (#11440)
* Allow overriding the base url

* Override the mcpbaseurl

* update the api base url

* Fix tests
2026-06-11 22:29:17 +02:00
Tomás Barreiro a3a31da37d Add the FeatureFlagService to the SDK [NOOP] (#11444)
* Add the FeatureFlagService to the SDK

* Fix comments

* Dispose of the telemetry service

* Dispose of the feature flag service

* Address PR feedback

* Stop the polling early if a new one is triggered with another user id

* Address PR feedback

* Dispose of the feature flag service

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-11 22:11:35 +02:00
Tomás Barreiro a69d650838 Open URLs when starting device auth (#11393)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-11 21:27:44 +02:00
Ara 7934d367a9 fix(sdk): truncate structured ToolOperationResult strings in MessageBuilder (#11465)
* test(sdk): add regression tests for structured ToolOperationResult truncation

MessageBuilder tests only covered string and {type:

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/mrbt2b39jfn370scr4g0ivzo"text"} tool-result
content, not the structured ToolOperationResult[] shape the default tools
(run_commands, read_files, search_codebase) actually emit. Those entries
are plain {query, result, success} objects with no type discriminator, so
the token-bloat path they create was unprotected by tests.

Adds regression tests using the real structured shape: huge result, huge
query, huge read_files payload, aggregate budget across multiple results,
mutation safety, and provider-formatted AI SDK payload size. Assertions
are on actual serialized payload sizes, not transcript shape.

The new tests fail at this commit by design; the following commit makes
them pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(sdk): truncate structured ToolOperationResult strings in MessageBuilder

The runtime stores structured tool outputs (ToolOperationResult[] from
run_commands/read_files/search_codebase) directly as the tool_result
content array (agentPartToContentBlock casts the array straight through).
Those entries have no type discriminator, so MessageBuilder's per-result
truncation, aggregate byte counting, and budget truncation all skipped
them — multi-megabyte command outputs and file reads were JSON-serialized
in full into every subsequent provider request.

MessageBuilder now deep-truncates nested strings inside structured
entries (middle truncation, preserving head and tail), counts them
against the aggregate text budget, collects them as budget-truncation
candidates, and deep-clones them before mutation so the original
conversation history stays untouched. Image blocks are skipped so base64
payloads survive intact.

Real-inference A/B on openrouter:minimax/minimax-m2.7 with realistic
structured payloads: 58.7% overall input-token reduction (82.6% on a
single huge command output) with identical answer correctness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/mrbt2b39jfn370scr4g0ivzo

* fix(sdk): include fetch_web_content in MessageBuilder truncation targets

Review feedback: fetch_web_content also returns ToolOperationResult[] and
its executor allows responses up to 5MB, but the tool was missing from
TARGET_TOOL_NAMES, so a single web fetch could still bloat every
subsequent provider request. Adds the tool to the truncation target set
with a regression test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/mrbt2b39jfn370scr4g0ivzo

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 11:34:43 -07:00
Ara 7f9d5461f1 fix(sdk): stop echoing full command text in run_commands tool results (#11463)
The run_commands tool result's query field repeated the entire executed
command, which already exists verbatim in the assistant tool-call input.
For large generated-file commands (e.g. cat <<EOF heredocs) this
duplicated thousands of chars of source text into every subsequent
provider request.

Bound the provider-facing echo to a 200-char preview plus a truncation
note pointing at the tool call input. Short commands pass through
unchanged. Applies to both createBashTool and createWindowsShellTool,
on success and error paths.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/og840mbjqaog4zita8m0262m
2026-06-11 11:22:20 -07:00
Saoud Rizwan e1bdeeff68 docs(changelog): note Vertex SDK companion bump in 3.89.2 (#11459) 2026-06-11 04:10:30 -07:00
Saoud Rizwan 49897830bb fix(vscode): align Anthropic Vertex SDK with runtime SDK (#11458) 2026-06-11 04:07:51 -07:00
Saoud Rizwan 1f316a2734 fix(vscode): remove unused ClineStorageMessage import in openai-format (#11457)
The SDK 0.50.1 upgrade widened convertToOpenAiMessages to take
Anthropic.Messages.MessageParam[], which left the ClineStorageMessage
import referenced only in comments. tsc does not flag unused imports in
this config, but biome lint does, and it blocked the 3.89.2 publish.
2026-06-11 03:52:55 -07:00
Saoud Rizwan 9c1f9133c7 v3.89.2 Release Notes (#11455) 2026-06-11 03:47:24 -07:00
Saoud Rizwan 2faef2b40d fix(vscode): upgrade @anthropic-ai/sdk to 0.50.1 for Node 24 compatibility (#11454)
VS Code 1.123.0 bumped its bundled runtime from Node 22 to Node 24
(Electron 39 to 42). The Anthropic provider broke on the updated editor
because the old SDK (<=0.41.x) shipped a legacy runtime built on
node-fetch and an internal _shims layer that does not work under Node 24.

0.50.1 is the first SDK release rewritten on top of the platform's native
fetch: it has zero runtime dependencies (no node-fetch, no _shims), which
removes the incompatibility. This is the actual fix; the earlier 0.40.1
bump did not change the runtime architecture.

The 0.50.1 type changes are minimal:
- Usage gained a required server_tool_use field, so fabricated Usage
  objects in the gemini/o1/openai/vscode-lm transforms set it to null.
- ContentBlockParam widened, so Anthropic.MessageParam is no longer
  structurally assignable to ClineStorageMessage. Handled by narrowing
  the two transform helpers that only ever receive Cline history
  (sanitizeAnthropicMessages, convertAnthropicMessageToGemini), typing
  getSavedApiConversationHistory as the Cline history it reads, and
  narrowing ContextManager's loosely-typed truncated output back to
  ClineStorageMessage at the two provider/hook boundaries.
2026-06-11 03:43:33 -07:00
Saoud Rizwan 64829bca8c chore(vscode): release v3.89.1 (#11451) 2026-06-11 02:49:39 -07:00
Saoud Rizwan 4c9ba6b091 fix(vscode): restore Anthropic provider on Node 24 by bumping SDK (#11449)
VS Code 1.123.0 bumped its bundled runtime from Node 22 to Node 24
(Electron 39 to 42). The extension passes VS Code's globalThis.fetch to
every provider SDK, but @anthropic-ai/sdk was pinned at 0.37.0, which
predates the SDK's native-fetch rewrite and relies on legacy _shims
runtime detection that breaks under Node 24. The modern OpenAI and Gemini
SDKs are unaffected, which is why only the Anthropic provider broke after
users updated VS Code.

Bump to ^0.40.1, the first release with the native-fetch rewrite that
restores Node 24 compatibility, while staying short of the latest line's
larger breaking surface.

The only code change the bump requires is narrowing the image source type:
ImageBlockParam.source widened from a base64-only type to
Base64ImageSource | URLImageSource. Add a getBase64ImageSource/
getImageDataUrl helper in shared/messages/content.ts and route the
provider transforms through it. Cline only ever produces base64 image
sources, so behavior is unchanged; the helper emits the same data URL the
inline code did.
2026-06-11 02:44:25 -07:00
Bee 6138bdfe40 feat: Enforce a production singleton Cline Hub (#11372)
* feat: Enforce a production singleton Cline Hub

This PR changes local Hub startup/discovery so production uses one stable daemon per user machine instead of silently creating additional hubs on random ports.

Replace resolveSharedHubOwnerContext with resolveProductionHubOwnerContext
across doctor and hub server lifecycle management to scope hub discovery
to the production owner.

Additionally:
- Preserve and propagate auth tokens when retiring incompatible hubs
- Throw a clear error when a compatible hub is already running but its
  discovery record is missing, guiding users to run 'cline doctor fix'
- Gate port fallback behind an explicit allowPortFallback override
- Update tests to mock the new production hub owner context

* patches

* fix

* hasExplicitPort

* Restored daemon cron startup, made discovery auth tokens required again, and fixed graceful hub stop/restart paths to use the selected production/shared owner context.

* clean up

* patches

* fix Polynomial regular expression

* test

* fix: require explicit hub port fallback in production

* fix(cli): stop pgrep from parsing the hub daemon marker as an option

pgrep treats the "--cline-hub-daemon" pattern as an unknown long option
and exits 2, so doctor never found stale daemons from compiled-binary
installs, which are exactly the processes 'cline doctor fix' is told to
clean up. Pass "--" before the pattern to end option parsing.

* fix(hub): retire legacy shared-owner hubs on production startup

Pre-singleton production builds tracked the local hub under the shared
owner discovery path and spawned daemons on random fallback ports. The
production owner context never reads that path, so upgrades would leave
those daemons running indefinitely with no way to reuse or stop them.
Retire the recorded legacy hub (its record carries the auth token and
pid needed for a graceful stop) and clear the legacy record before
resolving the production hub.

* refactor(hub): simplify stale discovery clearing, share capability list

shouldClearStaleHubDiscovery was only ever called with
discoveredVerified=false (the true assignment sits on a return path),
so the expected-hub probe and compatibility check had no effect and the
condition reduced to "a discovery record exists and was not reused".
Replace it with a plain conditional and drop the tests that exercised
unreachable states.

Also move the hub capability list into a typed HUB_CAPABILITIES
constant in @cline/shared next to HubCapabilityName so the server
cannot drift from the type.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-10 16:59:13 -07:00
Dominic Cooney 7d119351b1 fix(cli): suppress flickering console windows on Windows (#11408)
* fix(cli): suppress flickering console windows on Windows by setting windowsHide on child processes

On Windows, child_process.spawn/execFile default to windowsHide: false,
so console-subsystem children (powershell, rg, git, node, npm) can
allocate a new visible console window - guaranteed when detached: true
is used. In the CLI this caused constant short-lived window flashes
from run_commands, the git status bar polling, ripgrep searches and
indexing, clipboard helpers, and hook/plugin node subprocesses.

Set windowsHide: true (CREATE_NO_WINDOW; a no-op on non-Windows) on all
remaining spawn/spawnSync/execFile call sites in the SDK core, CLI,
Cline Hub, and example plugins, matching the pattern already used by
the MCP client, checkpoint-hooks, and StandaloneTerminalProcess.

* Update apps/cli/src/commands/kanban.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-06-10 22:51:59 +09:00
Saoud Rizwan de987a5246 chore(cli): release v3.0.23 2026-06-09 17:43:24 -07:00
Saoud Rizwan 90050426df chore(sdk): release v0.0.46 2026-06-09 17:30:20 -07:00
Saoud Rizwan 205c5676ff fix(llms): fix disabled reasoning for Fable 5 error (#11397)
* fix(llms): avoid disabled reasoning for fable 5

* fix(llms): route fable reasoning by family

* Revert "fix(llms): route fable reasoning by family"

This reverts commit 6dd4e5dcf5.

* fix(llms): match claude fable reasoning workaround broadly
2026-06-09 17:24:34 -07:00
Bee 1c13edd395 fix(core): configured agent support as subagent tools (#11368)
* fix(core):  configured agent support as subagent tools

Introduce configured agent config parsing and tool creation for
subagents. Agent configs are defined via YAML frontmatter files
specifying name, description, tools, skills, model, and system prompt.

- Add `configured-agent-config` for loading and parsing agent
  definitions from search paths
- Add configured agent tool factory that wraps delegated agents as
  named subagent tools with policy and approval support

* patch

* patches

* fixes

* Infinite loop when YAML block is a non-object fix

* apply feedback

Forwarded host requestToolApproval into configured subagents.
Used the resolved workspace config root for configured-agent skills discovery.
Split configured-agent skill loading from root-session skills enablement.
Added host lifecycle/event plumbing for configured subagents via shared subagent callbacks.
Made UserInstructionConfigService.createSkillsExecutor optional and guarded its use.

* threaded

* test(core): cover configured subagent skill isolation (#11396)

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-09 17:23:00 -07:00
Ara a2a1936709 Fix Azure Foundry API version for CLI (#11359)
* Fix Azure Foundry API version for CLI

* Fix Azure API version setup
2026-06-09 16:28:37 -07:00
Max 35ce6a3f26 fix(cli): configure Vertex GCP settings (#11390)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-09 16:05:09 -07:00
Saoud Rizwan 2c4aeae4f3 fix(vscode): handle DeepSeek V4 reasoning format (#11392) 2026-06-09 15:10:38 -07:00
Tomás Barreiro 0c027d2731 Centralize OAuth management to the SDK (#11260)
* Centralize OAuth management to the SDK

* Update mock

* Cleanup TUI cline-account logic

* clean save credentails

* Remove unused code

* Reduce mocks

* use normalizeStoredAccessToken
2026-06-09 23:56:26 +02:00
Tomás Barreiro 6cc93c124e Format vscode using biome higher order rules (#11389) 2026-06-09 22:14:19 +02:00
Saoud Rizwan 7e5b8be28c chore(cli): release v3.0.22 2026-06-09 12:08:17 -07:00
Saoud Rizwan 764e901693 test(core): update legacy migration default to claude-fable-5
The Fable 5 PR (#11385) made claude-fable-5 the newest anthropic model,
which sorts first in the generated catalog. Legacy provider migration
defaults to the first catalog model, so the migrated default changed from
claude-opus-4-8 to claude-fable-5. Update the test expectation to match.
2026-06-09 11:55:08 -07:00
Saoud Rizwan 2cabb2ddf6 chore(sdk): release v0.0.45 2026-06-09 11:43:56 -07:00
Saoud Rizwan c32789f697 chore: bump version and update changelog (v3.89.0) (#11386) 2026-06-09 11:36:08 -07:00
Saoud Rizwan 349a8da750 feat(sdk): add Claude Fable 5 model support (#11385) 2026-06-09 11:31:50 -07:00
Saoud Rizwan f09dab7a0b feat(vscode): add Claude Fable 5 support to VS Code extension (#11384) 2026-06-09 11:19:29 -07:00
Robin Newhouse 3a3ea6ee96 Fix MiniMax M3 thinking controls across gateways [ENG-2163] (#11371)
* fix(llms): route MiniMax M3 thinking controls

* test(llms): tighten MiniMax M3 routing scope

* fix(llms): preserve fetch preconnect in MiniMax shim
2026-06-09 10:53:59 -07:00
dependabot[bot] 1c1ea0bd53 chore(deps): bump shell-quote from 1.8.3 to 1.8.4 in /apps/vscode (#11383)
Bumps [shell-quote](https://github.com/ljharb/shell-quote) from 1.8.3 to 1.8.4.
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/shell-quote/compare/v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-version: 1.8.4
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-09 19:11:24 +02:00
Mikołaj Kondratek 70303d8541 Improve bug report form: rename surface dropdown, add IDE/CLI diagnostics (#11381)
* Improve bug report form: rename surface dropdown, add IDE/CLI diagnostics

Rename the 'Plugin Type' dropdown to 'Cline Surface' since CLI is not a plugin; the option values keep each choice unambiguous.

Add an 'IDE / CLI Diagnostics' field with per-surface copy-paste steps for About info (VSCode Help/About, JetBrains Help/About Copy button) and a CLI exception using 'cline --version'. System Information is left as-is; minor overlap is acceptable.

* Update repo-label-issues workflow for renamed Cline Surface field

The auto-labeler matches the rendered '### Plugin Type' heading. Since the form label was renamed to 'Cline Surface', update the three regexes so JetBrains/VS Code/CLI labels keep applying.
2026-06-09 18:38:53 +02:00
Saoud Rizwan 8ba15dfca6 chore(cli): release v3.0.21 2026-06-08 21:44:14 -07:00
Saoud Rizwan 6ab6a1eabc chore(sdk): release v0.0.44 2026-06-08 21:27:22 -07:00
Bee 2ad4146de1 doc(sdk): add host logger support in plugin examples (#11363)
* doc(sdk): add host logger support in plugin examples

Add examples to use the exposed `ctx.logger` to plugins via the `setup` second argument for
diagnostics. Wire logging into the agents-squad example to record setup,
subagent starts, follow-ups, and async failures, with a `logPluginError`
helper that falls back to severity-tagged logs. Update README with
logger usage guidance and examples.

* patches
2026-06-08 16:29:38 -07:00
Ara cfc2250717 fix(sdk): support Vertex ADC tool-use inference (#10773)
* fix(sdk): replay Vertex thought signatures

* fix(sdk): route Gemini 2.5 thinking config

* fix(sdk): tighten Vertex thinking replay routing

* chore(sdk): keep Vertex PR scoped to signatures

* chore(sdk): remove defensive thought signature fallback

* test(sdk): cover legacy Google thought signatures

* refactor(sdk): move Gemini model facts
2026-06-08 15:38:54 -07:00
Bee 730bac7f59 fix: empty SDK message content replay for Bedrock CLINE-2373 (#11320)
* fix: empty SDK message content replay for Bedrock CLINE-2373

This fixes SDK message formatting when persisted conversation history contains an empty user or assistant message, such as after an interrupted task is resumed.

Instead of dropping the message turn, the SDK now preserves it and inserts a text content block:

ERROR: EMPTY CONTENT

This prevents providers like Amazon Bedrock from rejecting replayed history with empty content arrays while avoiding message removal that could affect provider turn ordering.

* Exported EMPTY_CONTENT_TEXT from @cline/shared so core/shared use one constant
2026-06-08 14:47:53 -07:00
Bee 797ea1f607 feat: global auto-update setting for CLI startup updates (#11326)
* feat: global auto-update setting for CLI startup updates

* patches

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-08 14:22:41 -07:00
Saoud Rizwan 9d59de4a4c test(llms): align ChatGPT subscription model expectations (#11348) 2026-06-07 23:44:51 -07:00
Saoud Rizwan ae67ca7a13 fix(cli): show Cline credits refill link (#11345)
* fix(cli): show Cline credits refill link

* fix(cli): simplify Cline credits error matcher

* fix(cli): keep credits handling in TUI

* fix(cli): rename credits error matcher

* fix(cli): render credits dashboard as link

* fix(cli): remove credits redirect param

* fix(cli): document temporary credits matcher
2026-06-07 19:58:34 -07:00
Tomás Barreiro 7e2583f40c Fix broken tests (#11344) 2026-06-07 18:22:43 -07:00
Tomás Barreiro ecca88bb98 Clean-up the Codex model list (#11342) 2026-06-07 17:16:12 -07:00
Saoud Rizwan 4bb93ee5b9 chore: bump version and update changelog (v3.88.1) (#11334) 2026-06-06 18:26:28 -07:00
Saoud Rizwan 4f2d7398ed fix(vscode): include walkthrough files in extension package (#11333) 2026-06-06 17:56:55 -07:00
Saoud Rizwan bc184f346d fix(cli): scroll inline ask question responses (#11293)
* fix(cli): scroll inline ask question responses

* fix(cli): address ask question review feedback
2026-06-06 17:31:16 -07:00
Bee 96aea0d34b fix(cli): connector thread session routing & stale hub session (#11325)
* fix(cli): connector thread session routing & stale hub session

Fix connector thread session routing and stale hub session recovery

**PR Description**

This fixes connector messages from separate chat threads being routed into the wrong active runtime session.

**Issue**

In Slack, if a user sent a message in a different thread while another thread was still processing, the new message could be treated as a steer message for the active task. Users could also see errors like:

```text
Slack bridge error: session not found: 1780596180501_ms45m
```

when a connector thread had a persisted session id that no longer existed in the hub, such as after a hub restart.

**Cause**

Connector conversation bindings and active turn queues were using participant identity as the primary key in several paths. That allowed messages from the same user in different chat threads to resolve to the same connector session/active turn.

Separately, persisted connector `sessionId` values were trusted without checking whether the hub still had that runtime session. After a hub restart, the connector could try to send input to a stale session id.

**Fix**

- Store connector conversation bindings by thread id instead of participant key.
- Key connector active turn queues by thread id across Slack, Discord, Telegram, Google Chat, Linear, and WhatsApp adapters.
- Only treat a follow-up as a steer message when the active turn belongs to the same thread.
- Keep participant key/label as metadata instead of using it as the conversation binding key.
- Validate a persisted session id with the hub before reusing it.
- If the persisted session is missing, clear it from thread state and start a fresh runtime session.
- Update schedule delivery metadata to target thread ids while preserving participant metadata.
- Add regression coverage for cross-thread active sessions and stale persisted session ids.

**Verification**

```bash
bun -F @cline/cli typecheck
bunx vitest run apps/cli/src/connectors/connector-host.test.ts apps/cli/src/connectors/thread-bindings.test.ts apps/cli/src/connectors/adapters/slack.test.ts apps/cli/src/connectors/adapters/telegram.test.ts apps/cli/src/connectors/adapters/discord.test.ts apps/cli/src/connectors/adapters/gchat.test.ts apps/cli/src/connectors/adapters/linear.test.ts apps/cli/src/connectors/adapters/whatsapp.test.ts
```

* patches
2026-06-06 10:17:01 -07:00
Tomás Barreiro 676b446d47 Add debug section for Cline testers (#11318) 2026-06-05 12:09:01 -07:00
Ara a8835425bf chore: bump version and update changelog (v3.88.0) (#11316) 2026-06-05 09:58:50 -07:00
Tomás Barreiro e152741e1d Remove the recommended models feature flag (#11315)
* Remove the recommended models feature flag

* Fix recommended model flag CI

---------

Co-authored-by: Arafatkatze <arafat.da.khan@gmail.com>
2026-06-05 08:36:18 -07:00
Mikołaj Kondratek 6fec41ea70 fix(mcp): guard settings writes in delete/add so the watcher can't empty the list (#11240)
deleteServerRPC and addRemoteServer wrote cline_mcp_settings.json without
setting isUpdatingClineSettings, so the chokidar settings watcher was not
suppressed during the plugin's own write. The watcher is configured with
atomic: true and awaitWriteFinish (stabilityThreshold 100ms); on Windows it
races the non-atomic writeFile, reads a transient/empty file, and
readAndValidateMcpSettingsFile() returns { mcpServers: {} }. updateServerConnections({})
then tears down every in-memory connection, so deleting one MCP server emptied
the whole list in the UI after navigating away and back (CLINE-2097).

Wrap both methods in the same guard the sibling RPCs already use
(toggleServerDisabledRPC, toggleToolAutoApproveRPC, updateServerTimeoutRPC):
set isUpdatingClineSettings = true before the write and clear it on a 300ms
timer in finally, so the delayed watcher "change" event is skipped. addRemoteServer
had the same latent omission and is fixed in the same change.

Known tradeoff (pre-existing, unchanged by this fix): the guard is a single
shared boolean cleared by uncoordinated 300ms timers, so two settings writes
within 300ms can clear the flag early. Because awaitWriteFinish only emits once
the file is stable, the worst case there is a redundant reconnect, not the
empty-list data loss this fixes. A deterministic guard (per-op token or
content-compare-and-skip in the watcher) is out of scope for this targeted fix.

Adds McpHub.deleteServerRPC.test.ts covering the user-visible symptom (delete
one of two servers -> remaining server still returned/persisted, list not empty)
and the guard contract (flag set during write, cleared after 300ms, cleared on
the not-found error path).
2026-06-05 08:36:04 -07:00
Saoud Rizwan 5de1a45d1d chore(cli): release v3.0.20 2026-06-04 17:19:31 -07:00
Saoud Rizwan 717a2e643a fix(cli): name installed plugin wrappers from source (#11291) 2026-06-04 16:54:47 -07:00
Saoud Rizwan e50184d316 chore(cli): release v3.0.19 2026-06-04 14:48:01 -07:00
Saoud Rizwan 08c6f7ecbe fix(cli): use npm update for auto updates (#11285)
* fix(cli): use npm update for auto updates

* fix(cli): preserve npm update dist tag
2026-06-04 14:45:24 -07:00
Saoud Rizwan 15e6a685d6 chore(cli): release v3.0.18 2026-06-04 13:49:47 -07:00
Saoud Rizwan bc0eed950b test(core): skip chmod-based plugin uninstall failure test on Windows (#11284) 2026-06-04 13:34:56 -07:00
Saoud Rizwan 48316027a7 chore(sdk): release v0.0.43 2026-06-04 13:19:13 -07:00
Saoud Rizwan 4d15d16109 docs(cli): release the SDK before the CLI in the publish-cli skill (#11280)
* docs(cli): release the SDK before the CLI in the publish-cli skill

Add a Step 0 to the publish-cli skill that gates a CLI release on an SDK
release when the SDK changed since its last release, and relocate the
publish-cli skill to the repo root.

Why release the SDK alongside the CLI: the CLI bundles the SDK source via
workspace:*, so the CLI always ships the latest SDK code, but the hub
daemon stamps a buildId that defaults to the @cline/core version and a
running hub is only respawned when that buildId changes. Bumping the SDK
version forces a stale hub to be retired and respawned with the new code.
It also keeps SDK releases on a regular cadence in step with the CLI.

Step 0 covers detecting unreleased sdk/packages changes, bumping the
shared SDK version + llms CHANGELOG, committing to main, kicking off
sdk-publish.yml on the latest channel, and waiting for it before cutting
the CLI release. Also fixes the now-stale working-directory note (commands
run from the repo root, not sdk/) and updates the DEVELOPMENT.md path.

* chore: move opentui skill to repo root

Relocate the opentui TUI skill from apps/cli to the repo root, matching
the real-dir + symlink convention used by the other root skills (real dir
in .agents/skills, symlink from .claude/skills).

* docs(sdk): reformat the SDK changelog and move it to sdk/CHANGELOG.md

The changelog covers all SDK packages (they share one version and release
together), so move it from sdk/packages/llms/ to the SDK root, parallel to
apps/cli/CHANGELOG.md. Reformat to match the CLI changelog: a titled
header with flat, version-only sections, newest on top, no dates, and no
Next Release bucket. The unreleased entry that bucket held is captured
from commits when the next SDK release is drafted. Update the publish-cli
skill to draft SDK notes from commits and prepend a ## <version> section
at sdk/CHANGELOG.md.
2026-06-04 12:48:17 -07:00
Bee d2339f57f1 fix(slack): normalize channel mentions to original post thread (#11273)
* fix(slack): normalize channel mentions to original post thread

Route top-level Slack channel mentions to the originating post thread so
replies land in the correct conversation. Add `resolveSlackChannelMentionThread`
to rewrite non-DM mention threads using the message's `thread_ts`/`ts` and
channel, while preserving DM threads and already-correct threads.

Includes unit tests covering normalization, no-op, and DM cases.

* thread id
2026-06-04 11:49:56 -07:00
Ahmad Shahzad a209825116 Sync Fireworks AI model registry with current platform offerings (#11173)
* feat: update Fireworks model registry to improve Cline UX for Fireworks API users

The VS Code extension's Fireworks model list was significantly out of
date compared to the current active models available on the Fireworks
platform. This commit updates the registry to match the current model
lineup, ensuring users can select from the latest available models.

Changes:
- Default model: accounts/fireworks/models/kimi-k2p6 (was kimi-k2p5)
- SDK default: accounts/fireworks/models/kimi-k2p6 (was minimax-m2p5)

Removed 6 stale/phantom models no longer available:
  - qwen3-vl-30b-a3b-thinking
  - qwen3-vl-30b-a3b-instruct
  - deepseek-v3p2
  - glm-4p7
  - glm-5
  - minimax-m2p1

Added 9 missing models:
  - accounts/fireworks/models/kimi-k2p6
  - accounts/fireworks/routers/kimi-k2p6-turbo
  - accounts/fireworks/models/deepseek-v4-flash
  - accounts/fireworks/models/deepseek-v4-pro
  - accounts/fireworks/models/glm-5p1
  - accounts/fireworks/routers/glm-5p1-fast
  - accounts/fireworks/models/minimax-m2p7
  - accounts/fireworks/models/qwen3p6-plus
  - accounts/fireworks/models/gpt-oss-20b

Fixed metadata for 3 overlapping models:
  - kimi-k2p5: contextWindow 262144 → 256000, maxTokens 16384 → 256000
  - minimax-m2p5: maxTokens 16384 → 196608
  - gpt-oss-120b: maxTokens 16384 → 32768, cacheReadsPrice 0.01 → 0.015

All models now have cacheWritesPrice: 0 because Fireworks does not
charge a separate rate for prompt cache writes (cache writes are
billed at the standard input rate, matching the SDK catalog).

Three models remain in the UI but are scheduled for deprecation on
June 17 and will be removed then:
  - accounts/fireworks/models/kimi-k2p5
  - accounts/fireworks/models/minimax-m2p5
  - accounts/fireworks/models/qwen3p6-plus

Files:
- apps/vscode/src/shared/api.ts
- apps/vscode/webview-ui/src/components/settings/__tests__/APIOptions.spec.tsx
- sdk/packages/llms/src/providers/builtins.ts

* fix: update qwen3p6-plus context window and maxTokens
2026-06-04 19:19:26 +02:00
Mahesh Narayan acc1a25e51 Update agent storage guidance (#11025) 2026-06-04 09:53:18 -07:00
Saoud Rizwan 5f17a6963f fix(cli): clear abort indicator immediately (#11265) 2026-06-04 00:02:16 -07:00
Saoud Rizwan c7ddb96ddb chore(cli): release v3.0.17 2026-06-03 21:07:30 -07:00
Bee 107bce75b2 fix(cli): recover stale interactive sessions and suppress shutdown races (#11259)
* fix(cli): recover stale interactive sessions and suppress shutdown hook races

This fixes the CLI/TUI regression introduced between `3.0.14` and `3.0.15` where the interactive CLI could enter a broken state after stopping and restarting Cline Hub, then attempting to cancel a request with Escape.

The affected release window was:

- `49e8c1b32` / `v3.0.14`: known-good baseline
- `c33c3176e` / `v3.0.15`: release containing the regression
- `fad8271f4 feat: Cline Hub web app (#10969)`: relevant behavior change in the window

The Hub web app change introduced new Hub-backed runtime/session lifecycle behavior. After Ctrl+C or Hub shutdown, the CLI could still retain an `activeSessionId` that no longer existed in the Hub/runtime process. On the next interactive send, the CLI attempted to reuse that stale session and received `session not found`. Because cancellation also targeted the stale session, Escape stopped working and OpenTUI ended up receiving failures during input handling, which made the TUI look corrupted.

The same lifecycle issue also explains the Ctrl+C errors:

```text
error: hook dispatch failed: Hub connection closed (code=1006, reason=Connection ended)
error: WebSocket connection to 'ws://127.0.0.1:50168/hub' failed: Failed to connect
```

Those were caused by late hook dispatches racing against Hub shutdown. The CLI was still trying to send hook events over a Hub WebSocket that had already closed.

**What changed**

- Added missing-session recovery in the interactive runtime.
  - Detects `session not found` / stale session errors.
  - Reads any recoverable messages from the missing session.
  - Clears the stale active session state.
  - Starts a new interactive runtime session.
  - Retries the current turn once against the fresh session.

- Made hook dispatch shutdown-aware.
  - Runtime hooks now mark themselves as shutting down before session disposal.
  - Hook dispatches are skipped once shutdown begins.
  - Dispatch failures during shutdown are suppressed, since the Hub transport closing is expected at that point.

- Reordered CLI cleanup.
  - Hooks are shut down before stopping/disposing runtime sessions.
  - This prevents abort/stop lifecycle events from trying to dispatch over a closing Hub connection.

**Regression coverage**

Added tests for:

- Recovering from a disappeared active interactive session and retrying against a new session.
- Ensuring hook events are not dispatched after shutdown begins.

**Verification**

Passed:

```text
bunx vitest run apps/cli/src/utils/hooks.test.ts apps/cli/src/runtime/interactive/session-runtime.test.ts
bun -F @cline/cli typecheck
bun -F @cline/cli test:unit
bun -F @cline/cli test:e2e:cli:tui
git diff --check
```

* SessionNotFoundError

* fix(core): preserve stale session errors in hub runs

* clean up

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-03 21:00:19 -07:00
Saoud Rizwan 81792d20c6 fix(sdk): keep hub daemon alive on runtime abort (#11258) 2026-06-03 20:22:55 -07:00
Saoud Rizwan e8e2af705d feat(cli): improve Telegram connector with --allowed-user-id flag (#11256)
* feat(cli): add Telegram allowed user id flag

* fix(cli): tighten connector authorization hooks
2026-06-03 17:50:07 -07:00
Bee 8f00fcf3ed chore: bun run fix (#11252) 2026-06-03 13:45:06 -07:00
Saoud Rizwan a64d17734d chore(cli): release v3.0.16 2026-06-03 13:29:33 -07:00
Saoud Rizwan 8e621817c5 feat: add plugin uninstall ability (#11247)
* feat: add plugin uninstall flow

* fix: preserve disabled plugin settings on uninstall failure
2026-06-03 13:23:35 -07:00
Bee 423fde4828 feat(cli): add Slack socket mode support (#11245)
* feat(cli): add Slack socket mode support

Add socket mode as an alternative to webhook mode for Slack
connector, allowing connections without a public URL.

- Introduce `--connection` flag to select webhook or socket mode
- Add `--app-token` option for socket mode authentication
- Make signing secret and base URL conditional on webhook mode
- Add `parseSlackConnectionMode` with validation and tests
- Update CLI platform definition to support hybrid connection type
- Update README docs with socket mode usage examples

* use base-url and remove connection flag

* isSocketMode
2026-06-03 12:00:57 -07:00
Saoud Rizwan 76af785fa6 fix: allow skills plugin capability (#11244) 2026-06-03 10:42:12 -07:00
Bee dd7042fc7b fix(core): use union schema for read files tool input validation (#11225)
* fix(core): use union schema for read files tool input validation

Move the normalizeReadFileRequests helper logic directly into the
read_files tool executor, replacing the legacy helper with inline
validation against ReadFilesInputUnionSchema. This ensures invalid
union inputs are rejected before reaching the executor.

Update tests to reflect validation behavior and add coverage for
rejecting invalid union inputs.

* add new schema support
2026-06-03 10:20:54 -07:00
Tomás Barreiro 38e5b7c26b Fix VSCode CI (#11236)
* Fix VSCode CI

* fix nightly publish

* Fix install

* Fix biome

* fix tsc

* Fix windows set-up

* Ignore .vscode-test

* Add a LICENSE to the vscode extension

* Add type roots

* Remove workspaces

* remove vscode as a bun workspace
2026-06-03 18:58:27 +02:00
Dominic Cooney 8ae99cd69f feat(llms,core): route custom registered handlers through the agent runtime (#11235)
* feat(llms,core): route custom registered handlers through the agent runtime

Expose the handler-registry helpers (hasRegisteredHandler, getRegisteredHandler,
getRegisteredHandlerAsync, isRegisteredHandlerAsync) from @cline/llms, and have
core's createAgentModelFromConfig consult the registry: when a handler is
registered for a provider, build it via createHandler and adapt its ApiHandler
surface onto the AgentModel contract (the inverse of the gateway's
toApiStreamChunk).

This lets hosts register provider handlers that need host-only dependencies
(e.g. a vscode.lm-backed handler) and have them used by the main agent loop,
not just standalone createHandler callers.

* fix(core): resolve registered handlers lazily and avoid double finish

Address review feedback:
- createAgentModelFromConfig built the handler eagerly with the sync
  createHandler, which throws for providers registered via registerAsyncHandler.
  The adapter now accepts a handler factory and resolves it on the first stream
  via createHandlerAsync, supporting both sync- and async-registered handlers.
- Guard the adapter's catch-block finish with sawFinish so a handler that emits
  an explicit done chunk and then throws does not produce two finish events.

* fix(core): preserve thought signatures and finish-reason semantics in adapter

Further review feedback on the ApiHandler -> AgentModel adapter:
- Reasoning and tool-call thought signatures are now surfaced under
  metadata.thoughtSignature (the key downstream adapters read), instead of being
  stored as metadata.signature / dropped.
- A done chunk whose incompleteReason indicates max output tokens now maps to
  finish{reason:"max-tokens"} rather than "stop".
- A turn that ends with tool calls (no explicit done) now terminates as
  finish{reason:"tool-calls"}, matching the gateway/AI-SDK adapters.

* Apply remaining changes

* fix(core): report lazy handler-factory rejection as a finish(error) event

The lazy handler resolution (await source()) ran outside the adapter's
try/catch, so a rejecting factory (e.g. when the host API is unavailable at
stream time) escaped as a raw generator exception instead of a terminal
finish{reason:"error"} event. Move the resolution inside the try block so all
failure paths converge on the same terminal finish.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-04 01:40:59 +09:00
Max 845970ba7d improve cline provider migration (#11242)
- user's who are signed in with oauth in old extension were not properly
migrating their token. this commit handles that

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-03 09:38:18 -07:00
Max 444a9be6ec allow baseUrl field for anthropic vendor-type providers (#11227)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-02 20:01:49 -07:00
Saoud Rizwan ade7775337 feat(cli): install official plugins by slug (#11230)
* feat(cli): install official plugins by keyword

* fix(cli): harden official plugin clone

* fix(cli): remove official plugin repo env override
2026-06-02 18:24:37 -07:00
Tomás Barreiro e147682945 Set-up global greptile rules, kanban and other SDK files (#11233)
* Set-up global greptile rules, kanban and other SDK files

* Fix path

* Fix stale path

* Update vitest workspace config
2026-06-03 03:24:07 +02:00
Saoud Rizwan ae78fb422c docs(sdk): add custom model provider plugin example (#11234)
* docs(sdk): add custom model provider plugin example

Add an OpenRouter-backed example plugin demonstrating the providers
capability and registerProvider. It registers an OpenAI-compatible
provider plus its model catalog with the gateway so the agent can run
inference against an endpoint Cline does not bundle.

Registers under a distinct id (openrouter-plugin) to avoid colliding
with the built-in openrouter provider.

* docs(sdk): drop redundant provider section from plugin examples readme

* docs(sdk): drop provider demo line from plugin examples readme

* fix: support plugin model providers

* docs: remove provider plugin demo

* docs: address provider example review
2026-06-02 18:22:06 -07:00
Tomás Barreiro 220a21bdcf Move sdk/apps/ to apps/ (#11200)
* Move the apps to the root dir

* Update all references from sdk/apps/ to apps/

* Update dependencies

* Install bun types

* Fix types

* Fix types

* Fix linter

* Ingore apps from vscode

* Fix security warning

* Fix windows install

* Enable windows dev mode

* Revert "Enable windows dev mode"

This reverts commit a46c99282e.

* Revert "Ingore apps from vscode"

This reverts commit 47f7b265d2.

* Revert "Fix windows install"

This reverts commit 1dabba1556.

* update the repo root

* fix root dir

* fix path

* fix other path

* Fix unrelated changes

* fix: address apps move follow-up blockers (#11228)

* fix: update root app command paths

* fix: include moved apps in root checks

* fix: clean up moved app path references

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-03 01:49:57 +02:00
Saoud Rizwan 6aef7f5280 docs(cli): refine supply-chain scan alerts sample (#11224)
- Fold provider/model setup into a single `cline` run; drop the auth command
- Remove the /yolo on step from the Telegram setup
- Present scheduling as two clear options (Telegram chat vs terminal with
  delivery flags)
- Clarify how to find the schedule id before triggering a test run
2026-06-02 14:57:48 -07:00
Saoud Rizwan 4e56ed6922 docs(cli): add supply-chain scan alerts sample (#11222)
* docs(cli): add supply-chain scan alerts sample

Walkthrough for scheduling the Cline CLI to run Perplexity's Bumblebee
scanner and deliver compromise alerts to Telegram. Covers installing the
CLI, cloning/building Bumblebee and how it stays read-only, the Telegram
connector, and creating a scheduled scan that texts a clean/alert verdict.

* docs(cli): drop unsupported --delivery-thread from supply-chain sample
2026-06-02 14:04:33 -07:00
Saoud Rizwan 3a0f182408 fix(cli): show skills in slash autocomplete (#11220) 2026-06-02 13:18:30 -07:00
Saoud Rizwan 1f7adbd87e feat(cli): group plugin skills in settings (#11219) 2026-06-02 13:17:05 -07:00
Saoud Rizwan b0590554da feat: add skills bundled with plugins (#11161)
* feat: discover skills bundled with plugins

* fix: scope plugin bundled skills to active plugins

* fix: prevent ancestor skill discovery for plugins
2026-06-02 12:50:15 -07:00
Ara af2454f8d9 chore: bump version and update changelog (v3.87.0) (#11211) 2026-06-02 10:19:08 -07:00
Shantanu Gontia 1a4bf98e31 Update Sambanova Models (#11008)
* Update Sambanova Models

* moved to vscode/

* fix context windows

* Update Sambanova Models

* fix context windows

* Update api.ts

* Update sambanova prices
2026-06-02 18:52:00 +02:00
Ara 4139db4127 feat: add MiniMax M3 model (#11210) 2026-06-02 09:16:38 -07:00
Saoud Rizwan d55916e3ab fix(cli): show MCP OAuth errors in TUI (#11196)
* fix(cli): surface MCP OAuth errors in TUI

* chore(cli): reuse MCP status label helper
2026-06-01 20:08:23 -07:00
Bee 386ded5126 feat(cli): bundle and serve Cline Hub dashboard with cline dashboard (#11195)
* feat(cli): bundle and serve Cline Hub dashboard with cline dashboard

Add the @cline/cline-hub workspace dependency to the CLI and build the
Hub webview as part of CLI packaging. Copy the generated dashboard assets
into platform-specific CLI distributions so the dashboard is available in
built artifacts.

Refactor the Cline Hub server startup into an exported function so the CLI
can start and stop the dashboard server programmatically.

* fix(cli): resolve dashboard webview in wrapper installs

Detect the platform-specific CLI package from the published wrapper layout
and use its bundled cline-hub webview assets when no explicit dist path is
set. Add test coverage for resolving assets via CLINE_WRAPPER_PATH.

* patches

* patch

* fix server detachHub on stop

Imported detachHub.
Changed ClineHubDashboardServer.stop to () => Promise<void>.
Made stop() idempotent with a stopped guard.
Clears the health interval.
Calls server.stop(true).
Always calls await detachHub(ctx) in a finally, so hub client teardown still happens if the HTTP server stop throws.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-01 17:35:24 -07:00
WaylandYang 44e15319e4 fix(plugin-sandbox): expose CLINE_PLUGIN_IMPORT_TIMEOUT_MS env override (#11065) (#11084)
* fix(plugin-sandbox): expose CLINE_PLUGIN_IMPORT_TIMEOUT_MS env override

The hardcoded 4000 ms importTimeoutMs default is too tight on Windows
cold-start; the plugin-sandbox tests already use 30_000 ms for the same
reason. This patch lets hosts raise the ceiling via env var without
touching code or adding a CLI flag, with explicit options.importTimeoutMs
still taking precedence.

Precedence: options.importTimeoutMs > env var > 4000.

Refs: #11065

* fix(plugin-sandbox): tighten env parsing + use vi.stubEnv (PR feedback)

- Number.parseInt accepts trailing garbage ("4000ms" -> 4000); switch
  to Number() + Number.isInteger() so malformed env values fall back
  to the default instead of silently consuming the numeric prefix.
- Replace manual process.env save/restore in the regression test with
  the idiomatic vi.stubEnv() / vi.unstubAllEnvs() pattern.

Per Greptile review on #11084.
2026-06-01 17:34:13 -07:00
Saoud Rizwan e424b28702 feat(cli): add plugins slash command (#11193)
* feat(cli): add plugins slash command

* fix(cli): address plugins command review feedback
2026-06-01 16:59:38 -07:00
Robin Newhouse db9971890e Add SDK telemetry for run_commands timeouts (#11149)
* feat(sdk): add run_commands timeout telemetry

* docs(sdk): document timeout telemetry event

* docs(sdk): move telemetry catalog to core docs

* fix(sdk): omit undefined timeout telemetry fields

* fix(sdk): mark timed out run_commands unsuccessful

* docs(sdk): defer telemetry catalog entry

* fix(sdk): limit run_commands timeout success override

* fix(sdk): tighten timeout telemetry plumbing
2026-06-01 16:22:04 -07:00
Tomás Barreiro d7cc9b6155 Move bun from the sdk/ to root (#11104)
* Move bun to root

* Fix scripts and pre-commit

* Update scripts

* Update workflows

* Fix cd

* fix pre-commit

* fix cli publish
2026-06-01 22:29:41 +02:00
Saoud Rizwan 05042d3ff7 docs(sdk): add env-blocker plugin example (#11192)
* docs(sdk): add env-blocker plugin example

Adds a beforeTool hook plugin that deterministically blocks the agent
from reading .env secret files via read_files, editor, or run_commands
(e.g. cat .env), while leaving .env.example/.sample/.template readable.
Demonstrates moving a security policy out of an AGENTS.md rule (a
suggestion the model can ignore) and into the execution path.

* docs(sdk): install env-blocker globally in usage examples

A secret-protection guard is most useful applied to every project, so
drop the --cwd . project-scoped install in favor of the global default.

* docs(sdk): trim env-blocker usage docs

* docs(sdk): limit env-blocker to read paths only

It is a read blocker, so only guard read_files and run_commands.
Drop the editor case (and with it the symmetric apply_patch concern),
keeping the example focused and simple.

* docs(sdk): rename env-blocker helpers for readability

collectPaths -> extractFilePaths, collectCommands -> extractShellCommands
so the beforeTool call sites read clearly at a glance.

* docs(sdk): rename commandTouchesEnv to commandReadsEnv

* docs(sdk): drop console.error from env-blocker hook
2026-06-01 12:12:16 -07:00
aikido-autofix[bot] dc2c662de6 [Aikido] Fix 53 security issues in @xmldom/xmldom, basic-ftp, axios and 14 more (#11145)
* fix(security): update dependencies

* fix: set unbounded axios fetch adapter limits for 1.16.0

---------

Co-authored-by: aikido-autofix[bot] <119856028+aikido-autofix[bot]@users.noreply.github.com>
Co-authored-by: TheRealSpencer <spencer@cline.bot>
2026-06-01 10:15:40 -07:00
Max 4b68826cb5 update changelog and bump version (#11184)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-01 09:26:14 -07:00
Mikołaj Kondratek 85be70bb25 fix(vscode): probe @vscode/ripgrep-universal layout for VS Code ≥1.122.0 (#11178)
VS Code 1.122.0 migrated its bundled ripgrep from `@vscode/ripgrep` to
`@vscode/ripgrep-universal`, which ships per-platform/arch subdirectories
(`node_modules/@vscode/ripgrep-universal/bin/<platform>-<arch>/{rg|rg.exe}`)
instead of the previous flat `node_modules/@vscode/ripgrep/bin/{rg|rg.exe}`.
The migration commit (microsoft/vscode@bf19e5ca / @c4471e24) landed on
`release/1.122` and ships in stable 1.122.0+. Microsoft hit the identical
bug in their own sandbox engines and patched it in microsoft/vscode#317978;
our `getBinaryLocation` was still on the retired layout.

Symptom: on VS Code 1.122.x, all four `checkPath` probes in extension.ts
miss, `getBinaryLocation("rg")` throws `Could not find ripgrep binary`,
and `searchFiles` returns `{results: [], errorReason: "unknown"}`. The
@-mention picker shows "No results found" immediately, regardless of
workspace or query.

Telemetry confirmed the regression bisects cleanly to the VS Code
version boundary, not any Cline release — `mention_failed` events with
`errorType=unknown` jumped from ~300/week on 1.121.0 to ~100k/week on
1.122.0/1, while JetBrains versions (which don't use this code path)
stayed flat. Every historical Cline version is affected when the user
is on 1.122+.

Fix: probe the new `@vscode/ripgrep-universal/bin/<platform>-<arch>/`
layout first (both regular and `.asar.unpacked` variants), then fall
through to the four legacy probes so users on ≤1.121.x keep working.
`<platform>-<arch>` is `${process.platform}-${process.arch}`, matching
the directory naming Microsoft documented in #317978
(darwin-arm64, darwin-x64, linux-x64, linux-arm64, linux-arm,
linux-ia32, win32-x64, win32-arm64, win32-ia32, etc.).

This also closes the residual #11105 reports that survived #11166:
Yufeng's PR moved the failure mode from `unknown` to
`ripgrep_spawn_failed` (bare `rg`/`rg.exe` on PATH fallback) but didn't
restore actual functionality for users on 1.122.x — they got
spawn-ENOENT instead of file-not-found. With this patch the bundled
binary resolves correctly and ripgrep runs as before.

Refs: https://github.com/cline/cline/issues/11105
Refs: https://github.com/cline/cline/issues/11142
Refs: https://github.com/microsoft/vscode/pull/317978
2026-06-02 00:11:02 +09:00
Saoud Rizwan c824d8380a bump version and update changelog (#11172) 2026-05-31 23:33:45 -07:00
Yufeng He fb80840086 fix: keep file search fallback alive (#11166)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-31 23:28:28 -07:00
morning-verlu 42e4ea60db Fix marketplace getting started link (#11170)
Co-authored-by: morning-verlu <258725120+morning-verlu@users.noreply.github.com>
2026-05-31 23:12:08 -07:00
Saoud Rizwan 31a118fc0c test(core): expect Opus 4.8 default in legacy provider migration 2026-05-29 12:15:37 -07:00
Saoud Rizwan c33c3176ef chore(cli): release v3.0.15 2026-05-29 12:06:12 -07:00
Bee f5a3c591c8 chore(sdk): Model Catalog v1780081026557 (#11140)
Updated model catalog to v1780081026557 with `bun run build:models`

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-29 12:00:26 -07:00
Bee be930a69a8 feat(plugin): support rule contributions in sandbox (#11127)
* feat(plugin): support rule contributions in sandbox

Add plugin rule registration to the sandbox descriptor and handler state so
plugins can contribute static or dynamic rule content.

Update plugin installation to omit peer dependencies and use
legacy-peer-deps to avoid peer resolution failures during isolated installs.

* feat(cli): support participant mute targets in Discord

Resolve /mute and /unmute targets from Discord user mentions and raw
user IDs so a specific participant can be muted within a thread.

Update Discord system rules to guide agents toward thread-level and
participant-level mute commands, and add tests for target parsing.

* Update sdk/apps/cli/src/utils/chat-commands.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(cli): normalize addressed bot command suffixes

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-29 11:59:58 -07:00
Ara 3651fe9a55 fix: stop Discord connector after repeated errors (#11121)
* fix(cli): clear connector sessions on hub shutdown

* fix: stop Discord connector after repeated errors

Added error tracking to Discord connector to prevent spam:
- Tracks errors by message within a 1-minute window
- After 3 identical errors, shuts down connector instead of posting
- Prevents repeated error messages flooding Discord channels
- Logs shutdown reason for debugging

* fix: address Greptile feedback

1. Error tracker now per-thread (includes thread.id in key)

   - Prevents cross-thread error aggregation

   - One thread having errors will not kill the whole connector

2. Use fixed time window instead of sliding window

   - Track firstSeen timestamp, not just lastSeen

   - Prevents indefinite spam from errors every 61s

   - Window properly resets after ERROR_WINDOW_MS from first error

3. Remove redundant delete in clearBindingSessionIds

   - binding.state.sessionId already deleted in earlier block

   - Cleanup was misleading/unnecessary
2026-05-29 11:52:17 -07:00
Saoud Rizwan 526d8e9c93 fix(cli): make oauth urls clickable in tui (#11139) 2026-05-29 11:50:12 -07:00
Bee fad8271f41 feat: Cline Hub web app (#10969)
* feat: Cline Hub web app

Add a Cline Hub app for monitoring connected clients, viewing and driving sessions, streaming assistant output, and restarting the local hub. Document local, LAN, and tunnel usage with room-secret gating, ignore generated Cline cache/config data, and update lockfile entries.

* feat: Cline Hub UI

* feat: provider config schema

* run command update

* Use Workspace versions

* fix: rename routines to schedules

* feat(schedule): add routine summary and update support

Include last execution data in routine schedule overviews and cache the
summary state in the UI to reduce unnecessary reloads.

Add support for updating routine schedules from the hub server, validate
required fields, and trigger schedules asynchronously after confirming they
exist.

* UI for Connectors

* Fix UI switch for telemetryOptOut

* Expands Recent Sessions UI - allow title update

* feat: Cline Hub routes

* Add "health" and "version" routes

* Refactor server.ts nto 17 focused modules

* Provider Model list search box

* Extensions -> Customizations

* fix: restore discord connector catalog

* fix

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-29 11:25:50 -07:00
Bee d6837edda8 feat(cli): support participant mute targets in Discord (#11126)
* feat(cli): support participant mute targets in Discord

Resolve /mute and /unmute targets from Discord user mentions and raw
user IDs so a specific participant can be muted within a thread.

Update Discord system rules to guide agents toward thread-level and
participant-level mute commands, and add tests for target parsing.

* patches

* update prompt
2026-05-29 11:16:10 -07:00
Ara d0f20ca135 fix(sdk): stabilize Windows test suite (#11128) 2026-05-29 10:57:09 -07:00
Robin Newhouse b2a113a2a5 test(sdk): fix Windows CI test failures (#11132) 2026-05-29 10:46:21 -07:00
Robin Newhouse 5efa8cfd3f fix: discover symlinked SDK skill directories (#11113)
* fix: discover symlinked SDK skill directories

* test: cover circular symlink skill discovery

* test: avoid native config watchers in snapshot tests
2026-05-28 17:57:21 -07:00
Ara 9f42aea85d feat(sdk): support global AGENTS rules (#11103)
* feat(sdk): support global AGENTS rules

* fix: address global AGENTS review feedback

* fix: classify global AGENTS by exact path
2026-05-28 16:56:55 -07:00
Bee b87f61f9e4 fix(cli): stabilize core tests on Windows (#11125)
* fix(cli): stabilize core tests on Windows

Avoid several Windows-specific failure modes in the core CLI test suite.

Vitest already runs test files inside worker pools. The workspace file indexer was lazily spawning a nested worker from a transformed TypeScript module via import.meta.url, which is fragile on Windows and can cause Vitest to report only a generic worker fork crash. Disable that worker path under VITEST and use the deterministic fallback indexer for tests.

Quote process.execPath in bash executor shell-string tests. Windows Node/Bun paths commonly contain spaces, so unquoted command strings can fail under PowerShell or cmd even though they work on Unix paths.

Make detached hub probing defensive by routing probeHubServer calls through a safe wrapper, so rejected or malformed probe results are treated as unreachable instead of destabilizing startup/prewarm flows.

Also clear CLINE_RUN_AS_HUB_DAEMON in daemon test setup so tests do not inherit daemon-mode state from the surrounding CLI environment except where explicitly set.

Validation: bunx vitest run --config vitest.config.ts src/hub/daemon/index.test.ts src/services/workspace/file-indexer.test.ts src/services/workspace/mention-enricher.test.ts src/extensions/tools/executors/bash.test.ts --reporter=dot

Validation: bun run typecheck

Validation: bun run test:unit

* patch
2026-05-28 16:41:49 -07:00
Bee 38f1c7eb14 fix(cli): steer active connector sessions across turn keys (#11115)
* fix(cli): bind discord sessions to individual message authors

Resolve Discord participants from normalized message author data and persist
participant-specific thread state in bindings. Restore or create sessions per
participant so different Discord users do not accidentally share chat state.

Also add coverage for bot author handling and owner user configuration.

* patches

* fix(cli): steer active connector sessions across turn keys

Detect active connector turns by session ID when the current turn key
does not match, so replies steer the existing runtime session instead of
starting a duplicate session.

Also treat queued runtime turns as a non-error completion and log the
queued state for connector transports.

* fix(cli): steer active connector sessions across turn keys

Detect active connector turns by session ID when the current turn key
does not match, so replies steer the existing runtime session instead of
starting a duplicate session.

Also treat queued runtime turns as a non-error completion and log the
queued state for connector transports.

* add /idel
2026-05-28 13:21:09 -07:00
Robin Newhouse 81121663a4 fix(sdk): pin SAP AI provider for smoke install (#11116) 2026-05-28 13:02:26 -07:00
Bee 854ac75fe0 feat(cli): bind discord sessions to individual message authors (#11114)
* fix(cli): bind discord sessions to individual message authors

Resolve Discord participants from normalized message author data and persist
participant-specific thread state in bindings. Restore or create sessions per
participant so different Discord users do not accidentally share chat state.

Also add coverage for bot author handling and owner user configuration.

* patches
2026-05-28 12:53:37 -07:00
Ara 107f0f8337 bump version and update changelog (#11112) 2026-05-28 10:58:07 -07:00
Dominic Cooney e330695cb5 chore(codeowners): replace @candieduniverse with @dominiccooney (#11111)
Eve Killaby (@candieduniverse) has left Cline; transfer her /.github/ codeowner slot to @dominiccooney so .github changes still have four code owners able to approve.
2026-05-28 10:38:42 -07:00
Saoud Rizwan c2879dba43 feat(models): add Claude Opus 4.8 provider support (#11110)
Add claude-opus-4-8 (200k) and claude-opus-4-8:1m model variants across the
Anthropic, Claude Code, Bedrock, and Vertex catalogs, mirroring the Opus 4.7
setup (same pricing, 1M tiers, global endpoint, adaptive thinking).

- Wire the OpenRouter/Vercel AI Gateway 1m suffix handling and Cline/OpenRouter
  model refresh derivation for anthropic/claude-opus-4.8
- Register 4.8 in adaptive thinking detection so it uses the reasoning-effort
  selector path
- Bump the Claude Code "opus" alias to 4.8
- Add context window switchers in the Cline and OpenRouter model pickers
- Add provider tests for the new model ids
2026-05-28 10:37:44 -07:00
Mikołaj Kondratek f2d692cfc2 ci: gate ext-jb-test-integration auto-trigger on PR author association (#11108)
* ci: gate ext-jb-test-integration auto-trigger on PR author association

Extend the existing MEMBER/OWNER/COLLABORATOR allow-list (already used
for the /test-jetbrains comment path) to pull_request_target [opened,
reopened] as well, so the same trust model applies regardless of how
the workflow is triggered. PRs from non-trusted authors no longer
auto-trigger; a maintainer can still opt them in via /test-jetbrains.

* ci: replace hardcoded app-id with CLINE_JETBRAINS_WORKFLOW_ID var

Matches the convention already in use in cline/intellij-plugin and lets us change the App ID without touching workflow code.

* Update .github/workflows/ext-jb-test-integration.yml

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* ci: rename CLINE_JETBRAINS_WORKFLOW_KEY to CLINE_JETBRAINS_APP_KEY

The secret holds a GitHub App private key. Matches the rename of the matching app-id var in the previous commit.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-05-28 10:36:38 -07:00
Ara 0c90bd9bcf feat: add Moonshot Kimi K2.6 model (#11109) 2026-05-28 10:17:50 -07:00
Bee 33e521e551 fix: SAP AI Core uses AI SDK community provider (CLINE-2307) (#11075) 2026-05-27 20:12:01 -07:00
Ara 9e942fbb6b Fix Discord connector registration (#11077)
* fix(cli): register discord connector

* fix(cli): scope Discord reply fallback

* docs(cli): expand Discord connector setup

* fix(cli): move Discord empty reply fallback to adapter
2026-05-27 18:47:20 -07:00
Bee 6f609e8945 fix: writeDiagnostic for logging ACP output (#11091)
Replace writeErr with writeDiagnostic for logging ACP output so that they don't show up as error.
2026-05-27 14:14:49 -07:00
Ara 762e3c42ab fix(vscode): show Qwen 3.7 Max cache support (#11079)
* fix(vscode): route Qwen cache requests

* fix(vscode): keep qwen cache alias request-scoped

* fix(vscode): mark Vercel prompt-cache models

* fix(vscode): show Qwen 3.7 Max cache support
2026-05-27 13:39:28 -07:00
Tomás Barreiro 49e8c1b324 Update CLI to 3.0.14 (#11094) 2026-05-27 12:06:31 -07:00
Tomás Barreiro 71b8f43a7a Fix OTEL variable bundling (#11092) 2026-05-27 20:58:48 +02:00
Saoud Rizwan 3068fcfedf docs(sdk): note single-file plugin dep limit and pluginPaths dir form (#11076)
Single-file plugins can only import Node builtins and @cline/*. As soon
as a plugin needs an npm dep it has to ship as a package. Adds one
sentence each to the writing-plugins guide (with dependencies in the
example package.json) and plugin-install (noting pluginPaths accepts a
package directory for fast iteration).
2026-05-27 11:21:08 -07:00
Ara 7530900166 fix: repair vscode nightly publish workflows (#11072) 2026-05-26 12:14:25 -07:00
Dominic Cooney 2b45b7b7aa Remove the VSCode Nightly (SDK) publish workflow; we are just running the regular publish workflow from the SDK branch now. (#11074) 2026-05-26 11:34:25 -07:00
Tomás Barreiro 791d238996 Move vscode to apps (#10961)
* Move all vscode related files to /apps/vscode

* Fix launch and biome

* Ignore generated files

* Remove unused icons

* fix tsconfig

* Update workflows (#10962)

* Add default branch

* Move files to the right dir

* fix: add vscode publish README placeholder

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-25 21:35:59 +02:00
Ara 9ee618111d bump version and update changelog (#11036) 2026-05-25 10:47:32 -07:00
tjandy98 4bec5931f6 add gpt-5.5 (#11032) 2026-05-25 06:30:21 -07:00
Saoud Rizwan 3115679031 feat: add DeepSeek V4 models (#11027)
* feat: add DeepSeek V4 models

* fix: align DeepSeek V4 cache pricing
2026-05-25 06:29:49 -07:00
Saoud Rizwan 8a6441fddd chore(cli): release v3.0.13 2026-05-22 17:41:54 -07:00
Saoud Rizwan 7ef5b1e7af test(llms): add provider VCR smoke tests to prevent e.g. ChatGPT regressions (#11012)
* test(llms): add provider vcr smoke tests

* fix(llms): harden provider vcr recording cleanup
2026-05-22 17:37:20 -07:00
Saoud Rizwan 3e58e7b024 fix(cli): show loading dialog for history resume (#11013) 2026-05-22 17:20:59 -07:00
Willis d16667f0b8 fix: use correct base URL for Vertex AI global endpoint with Claude models (#10288)
* fix: use correct base URL for Vertex AI global endpoint with Claude models

The AnthropicVertex SDK constructs the API hostname as
`${region}-aiplatform.googleapis.com`, which produces
`global-aiplatform.googleapis.com` when region is "global".
This hostname does not exist and returns 404.

Per Google Cloud docs, the correct global endpoint hostname is
`aiplatform.googleapis.com` (no region prefix). This fix overrides
the baseURL when region is "global" to use the correct hostname.

Fixes #10287

* chore: add changeset for vertex global endpoint fix

* fix: rebase on main and re-apply global endpoint baseURL override
2026-05-22 17:11:15 -07:00
Saoud Rizwan a740c49524 fix(cli): defer empty session creation after reset (#11000)
* fix(cli): skip empty clear session restart

* fix(cli): defer empty session creation after reset

* fix(cli): clarify session start race guard

* fix(cli): avoid re-resuming after new session reset
2026-05-22 17:02:47 -07:00
Saoud Rizwan 3bc1ee8382 feat(shared): add VCR request body contracts (#10997)
* feat(shared): add VCR request body contracts

* fix(shared): tighten VCR request body contracts

* test(shared): cover legacy VCR cassette playback
2026-05-22 16:00:36 -07:00
Saoud Rizwan ecf354c753 chore(cli): release v3.0.12 2026-05-22 15:14:29 -07:00
TheRealSpencer a66c5ee973 chore(deps): pin protobuf to 7.5.8 via overrides (#10998) 2026-05-22 15:11:06 -07:00
Saoud Rizwan 46659eebeb fix(cli): show loading dialog during model settings transitions (#10999)
* fix(cli): show loading dialog during model settings transitions

* docs(cli): explain loading dialog render yield
2026-05-22 14:54:16 -07:00
Saoud Rizwan 899ee0bfea fix(cli): add inline ask question tool prompt (#10989)
* fix(cli): inline runtime tool prompts

* fix(cli): address inline prompt review
2026-05-22 13:57:01 -07:00
Bee db4598e310 Cline SDK 0.0.42 (#10994)
Version bump
2026-05-22 13:31:48 -07:00
Saoud Rizwan 627e539266 fix(cli): bypass release age gate for manual updates (#10987)
* fix(cli): bypass release age gate for manual updates

* fix(cli): use yarn env override for update age gate
2026-05-22 13:29:25 -07:00
Saoud Rizwan 87ff61915f chore(cli): release v3.0.11 2026-05-22 13:03:25 -07:00
Saoud Rizwan a305556452 fix(llms): revert implicit output cap regression (#10990)
* fix(llms): avoid implicit output token caps

* test(llms): cover ChatGPT OAuth output token regression
2026-05-22 12:59:43 -07:00
Robin Newhouse ca12e97288 fix(cli): make config footer toggle hint contextual (#10976) 2026-05-22 12:34:48 -07:00
Bee 0c89716849 feat(core): includes tool names in tool results across messages (#10975)
* fix(llms): Use Google auth for Vertex Gemini

- Pass `providerConfig.gcp.projectId` through to Vertex Gemini as `googleAuthOptions.projectId`
- Disable Vertex API-key express mode when GCP project config is present so `google-auth-library` handles auth
- Add coverage for `AgentConfig.providerConfig.gcp` forwarding and Vertex Gemini provider creation
- Fix vertex model list only contains Claude models issue

* feat(core): includes tool names in tool results across messages

Updated `tool_result` content blocks to consistently include the `name` of the tool being executed. This change propagates through provider helpers and applies the new schema across all associated unit and live tests, ensuring proper tracking and logging of tool interactions within messages.
2026-05-22 11:36:32 -07:00
Robin Newhouse 1c401dbe1a Fix SDK LLM live provider configs (#10977) 2026-05-22 11:04:33 -07:00
Bee ed78404e4b fix(llms): Use Google auth for Vertex Gemini (#10974)
- Pass `providerConfig.gcp.projectId` through to Vertex Gemini as `googleAuthOptions.projectId`
- Disable Vertex API-key express mode when GCP project config is present so `google-auth-library` handles auth
- Add coverage for `AgentConfig.providerConfig.gcp` forwarding and Vertex Gemini provider creation
- Fix vertex model list only contains Claude models issue
2026-05-22 11:02:35 -07:00
Saoud Rizwan 0157ed9efb chore(cli): release v3.0.10 2026-05-21 20:23:07 -07:00
Saoud Rizwan 5574c95ff2 docs(cli): note ignore-scripts local pack guard quirk in publish-cli skill 2026-05-21 20:22:44 -07:00
Saoud Rizwan 31ee8eb744 feat(cli): install plugins from file URLs (#10945)
* feat(cli): install plugins from file URLs

* fix(cli): harden remote plugin installs

* docs: document plugin file URL installs

* docs: simplify plugin file URL wording

* docs: trim CLI plugin example
2026-05-21 19:58:20 -07:00
Renee Huang 7952e230ae Add Ollama API key note in TUI settings (#10947)
* Add Ollama API key note in TUI settings

* refactor: centralize provider config field metadata

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-21 16:27:31 -07:00
Bee 6bcfa34ba9 feat(sdk): track idle and pending session states (#10959)
* feat(runtime): track idle and pending session states

Propagate idle and pending as non-terminal session statuses across the CLI, hub records, and active-session checks. Update runtime handling so interactive sessions remain active while idle or awaiting approval instead of being treated as ended.

Removed `status` from showing up in `cline history` for now.

* hydrate: false no longer reads message files for every session.
2026-05-21 16:09:45 -07:00
Ara cf18793434 feat(sdk): add Poolside provider (#10956) 2026-05-21 16:04:38 -07:00
Saoud Rizwan a7ba25d6e1 feat(uri): add /lg-task webhook integration for LG dashboard flow (#10194)
* feat(uri): add LG task deeplink webhook integration

* feat(uri): include prompt-file path in LG task prompt

* refactor(uri): move LG webhook setup into integration service
2026-05-21 16:03:21 -07:00
Ara 2af2028861 feat: add Gemini 3.5 Flash to Gemini providers (#10955) 2026-05-21 15:43:44 -07:00
Ara 932c8e68e1 Infer Telegram bot username from token (#10954)
* fix(cli): infer Telegram bot username from token

* fix(cli): address Telegram connector review feedback

* test(cli): confirm Telegram schedule delivery bot metadata

* fix(core): publish schedule completion events to connectors

* Revert "fix(core): publish schedule completion events to connectors"

This reverts commit 0d64f037b9.
2026-05-21 15:03:36 -07:00
Bee 3cd13352eb fix(cron): publish failed schedule execution events (#10937)
* fix(cron): publish failed schedule execution events

Add cron runner execution event publishing for completed and failed runs.
Update connector adapters to react to failed schedule executions so clients are notified when scheduled work does not complete successfully.

* add unit tests
2026-05-21 14:57:26 -07:00
Tomás Barreiro 2c842328a8 Inject OTEL variables into the cli at buildtime (#10958)
* Inject OTEL variables

* Add telemetry to the nightly

* Add variables to the build script
2026-05-21 23:43:30 +02:00
Saoud Rizwan d3b3ff1c33 fix(cli): satisfy config item hook lint (#10968) 2026-05-21 14:38:36 -07:00
Robin Newhouse 2508e76af8 Fix SDK model catalog token-limit semantics ENG-2100 (#10946)
* fix(llms): preserve catalog output limits

* chore(llms): regenerate model catalog
2026-05-21 14:19:35 -07:00
Ara a702bf9b38 fix(cli): soften rejected tool call display (#10871) 2026-05-21 14:00:55 -07:00
Robin Newhouse a8cea1dca3 Fix disabled skill availability in SDK CLI ENG-2058 (#10876)
* fix(sdk): hide skills tool when skills are disabled

* fix(cli): refresh skill slash commands after toggle
2026-05-21 11:23:14 -07:00
Bee 2a351ffdd5 fix: Bedrock legacy migration for awsProfile (#10943)
The new Bedrock path is not failing because credential_process is unsupported. Both old and new code use AWS SDK v3’s fromNodeProviderChain, which can load credential_process.

The practical difference is that the migrated config does not include the AWS profile name:

```
"aws": {
  "region": "us-east-1",
  "authentication": "profile"
}
```

There is no `"profile": "bedrock"` So the new llms provider never targets [profile bedrock]. It calls fromNodeProviderChain({ ignoreCache: true, clientConfig: { region } }), which means AWS SDK will use AWS_PROFILE if present, otherwise default.

## Cause

The migration code only migrates awsProfile when legacy awsUseProfile is true. But the old extension treats profile auth as active when awsAuthentication === "profile" too:

```
profile:
  legacyGlobalState.awsAuthentication === "profile" || legacyGlobalState.awsUseProfile
    ? trimNonEmpty(legacyGlobalState.awsProfile)
    : undefined
    ```

## Fixes

The migration now preserves awsProfile when awsAuthentication === "profile", even if the old awsUseProfile flag is missing. It also treats profile-based Bedrock settings as enough to migrate Bedrock without static AWS keys.
2026-05-20 16:46:38 -07:00
Bee 69f148bad9 refactor: cache global settings reads by file metadata (#10933)
* refactor: cache global settings reads by file metadata

Avoid repeated global settings file reads by caching parsed settings and
validating the cache with path, mtime, and size. Invalidate the cache after
writes so updates remain visible, and clarify the legacy skills config name.

* refactor for performance

mtime-keyed cache of the parsed GlobalSettings — repeated reads do statSync + 4 comparisons instead of readFile + JSON.parse + zod (~30-100× speedup on hot path).
statSync(filePath, { throwIfNoEntry: false }) — avoids exception construction on missing-file path.
Cache invalidated on write — doesn't rely on filesystem mtime resolution.
loadSettingsFromDisk helper — pulls the read/parse/validate flow into one place, eliminates the previous three duplicated settingsCache = {...} assignments.
toggleDisabledTool cleaned up — single set construction + single write call, no branched copy of writeGlobalSettings.

* add unit tests for caching logic

* object freeze
2026-05-20 12:11:46 -07:00
Bee 6ca92794e1 chore: model catalog updated 1779302019893 (#10934)
All files automatically changed and formatted by `cd sdk && bun run build:models`

Generated model catalog version updated to 1779251127504

This includes the new X AI build
2026-05-20 11:52:25 -07:00
Bee 27bd4c6c65 chore: generated model catalog update (#10921)
version 1779251127504
2026-05-20 09:03:39 -07:00
Saoud Rizwan 015b61924f chore(cli): release v3.0.9 2026-05-19 19:48:23 -07:00
Saoud Rizwan d238239a0f fix(core): handle hub abort cleanup failures (#10918) 2026-05-19 19:38:42 -07:00
Bee e81c35d7c0 fix: Speed up CLI plugin loading and config toggles ENG-2082 (#10884)
* fix: Speed up CLI plugin loading and config toggles

Load sandboxed plugins concurrently during initialization while preserving
existing duplicate override ordering. Update plugin tool discovery to use a
single sandbox per listing and cache descriptor results by plugin path stats,
provider, and model.

Make CLI plugin/tool config toggles persistence-only from the data loader and
update the TUI optimistically, avoiding full config reloads and repeated plugin
imports when users disable tools or plugins.

* patches

* fix: refresh plugin tools when config update lacks data

Reload config data with plugin tools included when a plugin action does not return updated data. This keeps the config view in sync and clears stale plugin tool errors after refresh.

* fix(cli): preserve config item state on missing toggle data

Only update the dialog item when toggle responses include a matching item. This avoids applying fallback enabled-state changes that can desync the UI when returned config data is missing or incomplete.

---------

Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>
2026-05-19 18:18:47 -07:00
Ara 1ac5525fee fix: route Poolside Laguna models through next-gen prompts (#10910) 2026-05-19 16:40:19 -07:00
Saoud Rizwan a4d0937030 fix(cli): restore fuzzy file mention ranking (#10909)
* fix(cli): restore fuzzy file mention ranking

* fix(cli): address mention autocomplete review feedback
2026-05-19 16:09:43 -07:00
Bee 6d5c61f044 chore: gitignore SDK session and database files (#10907)
Ignore .cline session data, temp directories, SQLite artifacts, and generated metadata to prevent local SDK state and user data from being committed.
2026-05-19 13:54:53 -07:00
Mark Percival 8a6d031e8f fix(cli): keep interactive session live after cancel (#10903)
* fix(cli): keep interactive session live after cancel

* fix(sdk): use shared finish reason type
2026-05-19 13:44:59 -07:00
Renee Huang 3f808b369e sdk: add ClineCore CLI agent example (#10895)
* add a separate CLI agent using ClineCore, in comparison to the one built using Agent

* update SDK lockfile for ClineCore CLI example
2026-05-19 13:22:04 -07:00
Robin Newhouse e3b3e2306e fix(cli): accept dash-prefixed prompts after separator (#10905) 2026-05-19 12:47:07 -07:00
Tomás Barreiro b601b6e623 Update diff to 8.0.4 (#10904)
* Update diff to 8.0.4

* Remove types diff
2026-05-19 21:30:18 +02:00
Robin Newhouse ed008bd36e Route GLM thinking via provider metadata ENG-2019 (#10692)
* Route GLM thinking via provider metadata

* Address GLM provider routing review feedback
2026-05-19 12:12:51 -07:00
Saoud Rizwan 6e964c3ef0 chore(cli): release v3.0.8 2026-05-19 10:39:16 -07:00
Max 2e8579ebec bump version and update changelog (#10898)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-05-19 09:54:36 -07:00
Ara cb0515d5f4 fix(cli): use Telegram numeric participant ids (#10879) 2026-05-19 08:16:38 -07:00
Bee 8c00eb9a77 fix(cli): keep failed plugins visible in config UI ENG-2073 (#10878)
* fix(cli): keep failed plugins visible in config UI ENG-2073

Preserve plugins that fail during load or setup and attach their
diagnostic phase and error message. Surface those errors in config detail
rows so users can identify and fix broken plugin definitions.

* fix(cli): preserve multiple plugin initialization failures

Aggregate all setup/load failures per plugin path instead of overwriting earlier errors. Summarize multiple errors in the config UI so rows remain readable while retaining the full failure details.
2026-05-18 22:06:57 -07:00
Saoud Rizwan 1a4122eb53 fix(cli): avoid option right fork shortcut (#10883) 2026-05-18 19:06:22 -07:00
Ara e6fa037c4c Add real PR dashboard to code review bot example (#10812)
* add real PR dashboard to code review bot example

* docs: add code review bot dashboard screenshot
2026-05-18 17:49:24 -07:00
Ara 6e0ae8db58 Add Hub Monitor UI to menubar example (#10688)
* add menubar hub monitor dashboard

* docs: add menubar preview screenshot
2026-05-18 17:44:32 -07:00
Bee 0b1fe6a982 fix: remove demo runner from weather metrics example (#10875)
Remove the embedded runDemo entry point, ClineCore import, and runDemo export so the weather metrics example behaves as a plugin-only module.
2026-05-18 16:39:13 -07:00
Tomás Barreiro c18eef5d4c Remove unused stuff on sdk (#10869)
* Clean-up root files

* Clean vscode duplicated stuff

* Remove duplicated license

* Remove duplicated tailwind recommendation

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-19 01:36:34 +02:00
Saoud Rizwan 65443a01ae fix(sdk): stage readme during publish (#10870)
* fix(sdk): publish readme with sdk package

* fix(sdk): use absolute readme links

* fix(sdk): stage readme during publish
2026-05-18 16:22:08 -07:00
Robin Newhouse 7d56dd1e69 refactor(sdk): remove unused codex model list helper (#10874) 2026-05-18 16:21:45 -07:00
Saoud Rizwan 501f512066 chore: remove Kanban launch modal (#10864)
* chore: remove Kanban launch modal

* chore: remove unused Kanban state key

* test: tolerate missing post-login modal
2026-05-18 16:19:52 -07:00
John Simone 5810a5d1e7 Fix AWS Bedrock region and auth configuration in CLI (#10807)
* fix AWS region and profile detection logic

* add additional bedrock config fields in provider config screens

* address greptile feedback

* remove .cline directory

* Fix Bedrock catalog and AWS region handling

---------

Co-authored-by: Robin Newhouse <robin@cline.bot>
2026-05-18 15:56:08 -07:00
Ara 6a262b395a Polish multi-agent example war room demo (#10690)
* polish multi-agent war room demo

* docs: add multi-agent war room screenshot
2026-05-18 15:45:09 -07:00
Robin Newhouse 5117b7b0b8 docs: clarify SDK agent test workflow (#10860) 2026-05-18 15:35:31 -07:00
Tomás Barreiro 30ce8b1e72 Clean-up package-lock (#10872) 2026-05-18 23:43:30 +02:00
Tomás Barreiro 2fcac58e24 Extract GH variables as env variables during workflows (#10862)
* Extract GH variables as env variables during workflows

* Update other variables
2026-05-18 13:41:34 -07:00
Saoud Rizwan 112dbaa15b ci(cli): align release changelog formatting (#10863) 2026-05-18 13:17:51 -07:00
Saoud Rizwan 996a2f6a9c fix(agents): reset usage at start of each execute() call (#10713)
AgentRuntime.execute() reset iteration, pendingToolCalls, and lastError
between run/continue calls but not usage. This caused result.usage to
accumulate across calls, and local-runtime-host then added the
already-cumulative result onto the session baseline, double-counting
every prior turn's tokens on each new turn.
2026-05-18 12:50:10 -07:00
Renee Huang 47f6d00f61 docs: add back memorybank; correct CLI install commands (#10830)
* add back memorybank; correct CLI install commands

* add more details on plugin

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-18 12:02:04 -07:00
Mikołaj Kondratek 9ba2c932b4 fix(mcp): disable Restart Server button when server is toggled off (#10852)
* fix(mcp): disable Restart Server button when server is toggled off

The expanded 'Restart Server' button in the MCP server row stayed clickable even when the server's toggle was off, but the backing restartMcpServer call is a no-op for disabled servers, so the click did nothing.

Bring the expanded button into parity with the collapsed icon-button (line 227 in the same file), which already gates on server.disabled. No state, label, or styling change beyond the disabled predicate.

Fixes cline/intellij-plugin#559.

* Update webview-ui/src/components/mcp/configuration/tabs/installed/server-row/ServerRow.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-05-18 18:55:47 +02:00
Robin Newhouse 01aaa5dd04 fix(e2e): harden VS Code teardown (#10796)
ENG-2064
2026-05-18 09:31:31 -07:00
Mikołaj Kondratek 38bac25721 fix(cli): satisfy useExhaustiveDependencies in ConfigPanelContent (#10850)
Destructure `loadConfigData` from `props` alongside the other props already
destructured at the top of `ConfigPanelContent`, and use the local binding in
the plugin-tools `useEffect` (both inside the body and in the dependency
array). This clears two Biome `lint/correctness/useExhaustiveDependencies`
errors introduced by #10819:

  sdk/apps/cli/src/tui/views/config-view.tsx:265:2
    × This hook specifies a dependency more specific than its captures:
      props.loadConfigData
    × This hook does not specify its dependency on props.

The previous shape mixed `props.loadConfigData` in the dep array with `props`
captures inside the effect body, which Biome flags because it cannot prove
that re-reading `props.loadConfigData` between renders is equivalent to
re-reading `props`. Destructuring once at the top resolves both diagnostics
without broadening the dep set to all of `props` (the rule's suggested
"unsafe fix") — the effect still only refires when `loadConfigData` itself
changes, matching the original intent.

No behavior change. The destructure pattern matches how `resolve`, `dismiss`,
`dialogId`, and `config` are already pulled off `props` on the same line.

The failure was first surfaced by the JetBrains-plugin integration workflow
on PR #10819, where `cline/intellij-plugin`'s `:buildClineCoreZip` task runs
`npm run lint` against the merged tree. Verified locally with:

    npx @biomejs/biome lint --diagnostic-level=error \
        sdk/apps/cli/src/tui/views/config-view.tsx

— clean, exit 0. Full repo `biome lint`: 2537 files, no errors.
2026-05-18 17:05:00 +02:00
Saoud Rizwan b4d1b83bad chore(cli): release v3.0.7 2026-05-17 17:58:53 -07:00
Saoud Rizwan 8e6780201c fix(sdk): align ChatGPT OAuth model catalog (#10841) 2026-05-17 17:56:03 -07:00
Saoud Rizwan 087c6449c3 fix(cli): avoid ChatGPT model refresh on startup (#10839)
* fix(cli): avoid ChatGPT model refresh on startup

* fix(cli): remove dead ChatGPT private model merge
2026-05-17 17:09:02 -07:00
Saoud Rizwan 5cd72e5892 chore(cli): release v3.0.6 2026-05-17 16:29:39 -07:00
Saoud Rizwan 2b2fa8a473 fix(sdk): fix ChatGPT provider models (#10836)
* fix(sdk): restore ChatGPT subscription models

* fix(sdk): tighten ChatGPT provider model tests
2026-05-17 16:26:38 -07:00
Saoud Rizwan acca9186f1 docs: clarify plugin SDK peer dependency (#10823) 2026-05-16 15:44:13 -07:00
Saoud Rizwan f3bddc4ec1 chore(cli): release v3.0.5 2026-05-16 15:28:24 -07:00
Saoud Rizwan 5c9cd557c7 fix(cli): hydrate plugin tools and commands in settings (#10819)
* fix(cli): list settings plugin tools through sandbox

* fix(cli): hydrate plugin slash commands asynchronously

* fix(cli): preserve config reload options on toggle

* fix(cli): preserve hydrated plugin tools on settings toggles
2026-05-16 15:16:33 -07:00
Saoud Rizwan 0383be5375 chore(cli): release v3.0.4 2026-05-16 11:59:08 -07:00
Saoud Rizwan 4ee1ce507e fix(cli): improve light theme TUI colors (#10817)
* fix(cli): improve light theme TUI colors

* fix(cli): address light theme review feedback
2026-05-16 11:54:27 -07:00
Saoud Rizwan 22b3d654db fix(cli): fix plugin tools in production build (#10809)
* fix(cli): ship sdk deps for plugins

* fix(core): load plugin sdk imports through jiti virtual modules

* fix(core): avoid transforming compiled plugin sdk deps

* test(core): cover packaged plugin sandbox bootstrap

* fix(cli): preflight sdk deps before wrapper publish

* test(core): keep plugin tla test lightweight
2026-05-16 10:27:45 -07:00
Saoud Rizwan 52493c5ba1 ci: add path filtering to root workflows (#10777)
* ci: add path filters to root workflows

* ci: simplify qlty workflow condition

* ci: run root workflows on main pushes

* ci: update path filters for renamed workflows

* ci: include missing workflow filter inputs

* fix(ci): include scripts in testing platform filter

* fix(ci): preserve required test gate
2026-05-15 13:19:56 -07:00
Saoud Rizwan e65e5d5900 chore: rename GitHub Actions workflows (#10793) 2026-05-15 11:58:45 -07:00
tjandy98 b144baf102 Add support for SAP AI Core Hosted LLMs to SAP AI Core Provider (#10751)
* add hosted llms

* Update sapaicore.ts

* Update sapaicore.ts

* Update sapaicore.ts

* Update sapaicore.ts
2026-05-15 02:26:34 -07:00
Renee Huang f70725dd57 nit: small improvements to readme (#10779) 2026-05-14 23:04:09 -07:00
Saoud Rizwan 3821f7e52e Add CHANGELOG links to product index in README 2026-05-14 16:59:58 -07:00
Saoud Rizwan 8bbd1ea0f1 chore(cli): release v3.0.3 2026-05-14 16:03:27 -07:00
Saoud Rizwan 9739e4d11f fix(cli): restore OpenAI compatible provider (#10761)
* fix(cli): restore OpenAI compatible provider

* fix(cli): canonicalize OpenAI compatible provider id

* fix(core): preserve migrated OpenAI compatible model metadata

* fix(core): keep OpenAI compatible migration scoped

* fix(cli): allow OpenAI compatible auth base URL

* fix(core): preserve stored model metadata
2026-05-14 15:57:38 -07:00
Robin Newhouse c8d6efd831 Route Ollama default-on reasoning disable (#10678) 2026-05-14 15:24:19 -07:00
Robin Newhouse b17dfa1593 Add typed model metadata facts (#10677) 2026-05-14 14:55:19 -07:00
Robin Newhouse d70b0d913f fix: exclude agent tooling from VSIX package (#10769) 2026-05-14 14:54:49 -07:00
Robin Newhouse 1468a7445d Refactor llms provider routing facts (#10676) 2026-05-14 14:45:34 -07:00
Saoud Rizwan 83dcd4c178 feat: add cline-sdk skill for Claude Code agents (#10766)
* feat: add cline-sdk skill for Claude Code agents

Add a comprehensive skill that teaches Claude Code how to build AI agents
with the Cline SDK (@cline/sdk). The skill covers the Agent runtime,
ClineCore sessions, custom tools, plugins, events, LLM providers,
scheduling, multi-agent teams, and production deployment.

Sourced from cline/sdk-skill, installed via the skills system.

* feat: add symlink for cline-sdk skill in .claude/skills
2026-05-14 13:06:37 -07:00
Saoud Rizwan 18181829f2 fix: handle detached hub event errors (#10760) 2026-05-14 12:04:22 -07:00
Robin Newhouse 8688678ea7 fix(cli): resolve macOS screenshot paths with narrow no-break space (#10691)
* fix(cli): resolve macOS screenshot paths with narrow no-break space

macOS Sonoma+ embeds U+202F (NARROW NO-BREAK SPACE) before AM/PM in
screenshot filenames like "Screenshot 2026-05-12 at 4.42.48\u202FPM.png".
When the path travels through clipboards, terminals, or anything that
normalizes whitespace, U+202F collapses to a regular space (U+0020) --
but the on-disk filename still contains U+202F, so readFileSync fails
with ENOENT and the paste handler silently drops the image.

Add resolveExistingImagePath() with a tiered Unicode-variant lookup
(narrow no-break space before AM/PM, NFD normalization, curly
apostrophe for localized names, and a parent-directory canonical-
whitespace scan as a last-resort fallback). Wire it into the TUI image
paste handler so dragged/pasted screenshots resolve correctly.

Tests added in image-paste.test.ts cover both the targeted AM/PM
variant and the generic NBSP-in-filename fallback path.

* fix(core): resolve macOS U+202F screenshot paths in read_files tool

The first commit on this branch fixed the CLI image-paste handler only.
A live repro from session 1778635423953_jac9b showed the same U+202F bug
on a different surface: the agent`s read_files tool. When the LLM was
asked about a macOS screenshot, it called read_files with the path text
from the user message -- which had a regular space where the on-disk
filename has U+202F -- and fs.stat returned ENOENT.

Extract the tiered Unicode-variant resolver into @cline/shared as
resolveExistingFilePath (the algorithm is generic, not image-specific).
Wire it into:

  - packages/core/src/extensions/tools/executors/file-read.ts (the
    new fix; read_files now tolerates U+202F / NFD / curly apostrophe
    variants before falling back to a parent-dir scan).
  - apps/cli/src/utils/image-attachments.ts (refactor to use the
    shared helper instead of duplicating the algorithm).

Regression tests in packages/shared/src/path-resolution.test.ts and
packages/core/src/extensions/tools/executors/file-read.test.ts; the
CLI tests from the first commit continue to pass via the shared helper.

Live verification against the real screenshot from the bug repro
(/Users/robin/Desktop/Screenshots/Screenshot 2026-05-12 at 6.18.40 PM.png,
on-disk name uses U+202F): direct invocation of the executor with a
regular-space path now returns an image block (42,296 bytes) instead
of throwing ENOENT.

* fix(shared): tighten unicode path variant probes

* fix(shared): move path resolver to storage exports

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-14 12:03:58 -07:00
tjandy98 da58ddd35e Add support for Opus 4.6 & 4.7, Gemini 2.5 Flash Lite & Flash Image, Amazon Nova models to SAP AI Core Provider (#10635)
* Add opus 4.6 and 4.7

* Add gemini 2.5 flash lite and flash image

* Add support for amazon nova models

* Update sapaicore.ts

* Update sapaicore.ts

* Update api.ts
2026-05-14 11:44:38 -07:00
Saoud Rizwan 1f5e2086a1 chore: remove legacy cli/ source and adjacent dead glue (#10658)
* chore: remove legacy cli/ source and direct references

The legacy React Ink CLI in cli/ has been superseded by the new SDK
CLI at sdk/apps/cli/ (published as cline@nightly today, taking over
the cline npm package on the next latest cut).

This commit deletes the cli/ source tree (~9.5MB, 142 files) and the
remaining references that point at it:
- .clinerules/cli.md (the per-area tribal-knowledge file for working
  in cli/)
- package.json workspaces: drop the "cli" entry (root no longer
  publishes a workspace from there)
- package.json coverage excludes: drop the stale **/evals/cli/**
  paths (the directory does not exist)
- tsconfig.json: drop "cli/src/**/*" from the include list so the
  root typecheck stops trying to walk into a missing tree
- .github/copilot-instructions.md: drop the CLI architecture bullet
  and the cli/src/components/ModelPicker.tsx mention from the
  add-API-provider checklist

Intentionally left alone:
- .claude/hooks/claude-code-for-web-setup.sh references
  github.com/cli/cli (the gh CLI), not our deleted cli/
- src/core/locks/SqliteLockManager.ts comments mention
  cli/pkg/common/schema.go which is the Go-based cline-core schema,
  a different component
- .github/workflows/cline-evals-regression.yml is already disabled
  pending rewire at the new SDK CLI

* chore: remove orphaned legacy CLI distribution scripts

With cli/ gone, the public-install path (curl | bash → install.sh →
download CLI binaries from cline/cline GitHub releases) and the
enterprise endpoint-bundling helpers no longer have anything to
install or bundle:

- scripts/install.sh — curl-bash installer that downloaded the old
  CLI binary from cline/cline GH releases. The new install path is
  npm i -g cline.
- scripts/test-install.sh — only tested install.sh.
- scripts/test-bundled-endpoints.sh — built a VSIX *and* CLI tgz with
  bundled staging endpoints for enterprise distribution. The CLI half
  is dead; the VSIX half can be done with vsce + add-endpoints-to-vsix.sh
  directly. The script as a whole was niche test infra, not production.
- scripts/add-endpoints-to-npm.sh — injected endpoints.json into the
  old CLI's npm tarball. Companion add-endpoints-to-vsix.sh and
  add-endpoints-to-jetbrains.sh stay (they target the extension and
  JetBrains plugin).
- package.json: drop the orphaned `test:install` root script that
  wrapped scripts/test-install.sh.

If install.cline.bot (or any public URL) was still serving
scripts/install.sh as a curl|bash target, that URL will 404 after
this lands. Worth checking and either redirecting or stubbing with
a `npm i -g cline` hint.

* chore: clean up legacy CLI references

* chore: disable legacy smoke eval workflow

* Revert "chore: disable legacy smoke eval workflow"

This reverts commit 193ee78718.

* chore: remove disabled smoke eval workflow

* docs: clarify disabled smoke eval CI
2026-05-13 22:13:58 -07:00
Robin Newhouse 59e5eac3e0 feat(cli): add --worktree flag to auto-create and run in a new git worktree ENG-2028 (#10730)
* feat(cli): add --worktree flag to auto-create and run in a new git worktree

Adds a --worktree flag to the CLI root command and 'cline task' that detects the git repo root for the current directory, creates a detached-HEAD worktree at ~/.cline/worktrees/<uuid>/<repoName>/ (matching Kanban's convention), and runs the task with cwd set to the new worktree.

The core implementation lives in src/utils/git-worktree.ts as createTaskWorktree() so any surface (CLI, VS Code, JetBrains, future tools) can use the same helper.

Works with --taskId and --continue so the legitimate workflow of 'resume this task in a fresh clean worktree to try a different approach' is supported. For --continue, the most-recent-task lookup uses the original cwd before the worktree rewrite so history resolves correctly.

Refs: ENG-2028

* address greptile review

- Distinguish 'git not installed' from 'not a git repo' so the error message points at the actual problem instead of misleading the user.

- Reject taskIds containing a null byte; would otherwise sneak past the '..' substring check on some kernels (e.g. 'safe\\0../escape').

- Reject `--worktree` without a prompt / --taskId / --continue before kanban auto-launches, so 'cline --worktree' no longer creates an orphan worktree that's abandoned if the user exits the welcome TUI without submitting a task.

* feat(cli): move worktree flag to sdk cli

* fix(cli): match kanban worktree ids

* fix(cli): handle piped worktree input

* chore: remove legacy worktree diff

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-13 21:38:13 -07:00
alex-lum fd2c5e1a73 feat(sdk): add auth and provider-configured telemetry to SDK TUI onboarding (#10686)
* feat(core): add user.provider_configured event and captureProviderConfigured helper

Adds a new standardized telemetry event for when a user successfully
saves a BYO (bring-your-own) provider API key, separate from the
existing user.auth_* OAuth events.

- Add USER.PROVIDER_CONFIGURED = 'user.provider_configured' to the
  core event registry.
- Add captureProviderConfigured(telemetry, provider) helper that
  mirrors the { provider } payload shape of captureAuthSucceeded and
  routes through capture() (so it respects telemetry opt-out).
- Export captureProviderConfigured from @cline/core.
- Add focused tests for payload shape, provider=undefined fallback,
  and telemetry=undefined no-op.

* feat(core): thread optional telemetry through loginLocalProvider

Adds an optional telemetry parameter to loginLocalProvider so callers
can opt into standardized auth event emission without changing
existing behavior when no telemetry is passed.

- loginLocalProvider() accepts optional telemetry service.
- Forwards telemetry to loginClineOAuth, loginOcaOAuth, and
  loginOpenAICodex for the start/success/failure auth events.
- For Cline device-code login, passes telemetry into
  completeClineDeviceAuth().
- Telemetry remains optional throughout; existing callers continue to
  compile and run unchanged.

* feat(sdk-tui): emit auth and provider_configured telemetry in onboarding flows

Wires the SDK TUI onboarding login page into the standardized
telemetry events introduced in earlier commits.

- runOAuthAuthFlow() and runDeviceCodeAuthFlow() accept an optional
  telemetry service and forward it into loginLocalProvider so the
  start/success/failure auth events fire from the underlying core
  auth helpers.
- useOnboardingController() passes getCliTelemetryService() into the
  OAuth and device-code login paths so events use existing CLI
  metadata and respect telemetry opt-out.
- saveByoConfig() fires captureProviderConfigured for the BYO
  (bring-your-own API key) onboarding path right after the
  credential save. Invalid credentials still surface later as the
  existing task.provider_api_error on the first API call.
- Esc/cancel remains a local abort and does not synthesize extra
  failure telemetry unless the underlying auth helper itself emits
  an error.
- Adds a focused test confirming the SDK TUI auth path passes
  telemetry into the underlying auth helpers.

ENG-2018

* test(core): lock in telemetry opt-out policy for captureProviderConfigured

Adds captureProviderConfigured to the existing telemetry-policy
regression suite (`describe("telemetry policy: helpers respect
telemetry opt-out")`) so a disabled adapter is verified to drop the
event end-to-end through a real TelemetryService instance, matching
the coverage the other capture* helpers already have.

Addresses PR #10686 review feedback.

* test(core): include captureProviderConfigured in disabled-adapter policy test

The policy regression test that asserts a correctly-policed disabled
adapter drops all non-required events was missing a call to
captureProviderConfigured. Add it alongside the other captures and
include the expected dropped event in the assertion.
2026-05-13 18:05:27 -07:00
Bee 6d93c8af2d fix(core): clarify tool and task execution guidance (#10734)
Update tool definitions and the shared system prompt to emphasize exact output formatting, unsupported file read cases, long-running shell command handling, and final verification requirements.

This helps agents follow expected formats, avoid unsupported reads, and complete tasks only after relevant validation passes.
2026-05-13 15:38:54 -07:00
Renee Huang 1acb2b428c add skills to example readme as well (#10733) 2026-05-13 14:57:44 -07:00
Bee be55c3ac5d feat(cli): show session status in history view (#10685)
Display session status labels in CLI history output and refresh the
standalone history TUI so running sessions can update in place.

Merge refreshed status rows with existing hydrated metadata to preserve
titles and cost details while keeping status current.
2026-05-13 14:30:38 -07:00
Bee 52872f300c fix(llms): align Bedrock auth config for API key support (#10731)
* fix(llms): align Bedrock auth config for API key support

Add Bedrock bearer token env support, accept the apikey auth alias,
and map AWS profiles correctly for gateway config. Cover bearer, IAM,
and profile credential handling with new Bedrock provider tests.

* patches
2026-05-13 12:51:02 -07:00
Tomás Barreiro 8c7617cead Update simple-git (#10720)
* Update simple-git

* Pin specific version
2026-05-13 19:55:40 +02:00
Robin Newhouse d62077d59a feat(sdk): emit telemetry for compaction lifecycle events (#10694)
* feat(sdk): emit telemetry for compaction lifecycle events

Adds two SDK telemetry events to give us observability into the
compaction pipeline, which previously only fired a generic
`agent.status-notice` pass-through that carried no result data.

- `task.compaction_executed` fires after a successful compaction with
  strategy, mode, before/after message counts, before/after token
  counts, threshold, durationMs, provider, modelId, and agent identity.
- `task.compaction_skipped` fires when the configured strategy returns
  `undefined` (reason: "no_result"). Strategy exceptions still propagate.

`createContextCompactionPrepareTurn` is extended to accept `telemetry`
and `sessionId` from `CoreSessionConfig`; the CLI manual `/compact`
path forwards both so manual compactions emit events alongside auto.

Known gap (documented inline): plugin `registerMessageBuilder()` and
runtime hook `beforeModel` compactions bypass this wrapper and emit no
telemetry today.

Field names follow the existing `TASK.*` capture-function convention
(`provider`, `modelId`, `ulid`, `Partial<TelemetryAgentIdentityProperties>`)
so downstream PostHog joins remain consistent.

Fixes CLINE-2174

* test(sdk): cover compaction telemetry opt-out policy

* fix(cli): wire telemetry service into run config so compaction events fire
2026-05-13 10:22:38 -07:00
Saoud Rizwan fece9f232e Fix link formatting for Kanban entry in README 2026-05-13 10:11:58 -07:00
Robin Newhouse 99b65b421a test(cli): cover streaming token display update (#10726) 2026-05-13 10:02:08 -07:00
Max 80783b62cb changelog and version bump (#10727)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-05-13 09:37:31 -07:00
Saoud Rizwan c7f836bcd7 chore(cli): release v3.0.2 2026-05-13 09:22:51 -07:00
Saoud Rizwan 9e64e8966a fix(cli): prevent inflated TUI token count (#10725) 2026-05-13 09:16:53 -07:00
Tomás Barreiro a88b84785e Update Axios to prevent Critical vulnerability (#10719) 2026-05-13 17:25:44 +02:00
Saoud Rizwan 27539b589c Update SDK packages to 0.0.41 (#10722) 2026-05-13 08:20:38 -07:00
Tomás Barreiro e6f27c70f4 Update SAP Connectivity package to fix node-forge vulnerability (#10718) 2026-05-13 17:09:40 +02:00
Ara 6b9f2c0803 chore(cli): prepare 3.0.1 release (#10717) 2026-05-13 07:43:27 -07:00
Saoud Rizwan 6c9f72049f Improve README for clarity (#10710)
Updated README to improve clarity and organization of content, including changes to headings, links, and descriptions.
2026-05-13 00:41:05 -07:00
Saoud Rizwan 95ae525853 docs(sdk): add SDK skill section to overview and README (#10711)
* docs(sdk): add SDK skill section to overview and README

* Add 'subagents' to Cline SDK feature list

Updated the overview to include 'subagents' in the feature list.
2026-05-13 00:39:45 -07:00
Saoud Rizwan 7547229d6a docs(sdk): reframe runtime page as ClineCore, remove tier choice from overview (#10708)
* docs(sdk): remove Runtime Choices from overview, rename runtime to session-management

The SDK overview page had a prominent "Runtime Choices" section with an
"Agent vs ClineCore" comparison table and CTA link front and center.
New users don't have the context to make that decision on the overview
page. Removed that section entirely so the overview flows cleanly from
install to snippet to next steps.

Renamed sdk/runtime.mdx to sdk/session-management.mdx with updated
title and sidebar label to frame the page around session management
rather than two competing class names. Updated all nav entries and
redirects in docs.json.

* docs(sdk): rename runtime page to ClineCore, reframe as full harness

Renamed sdk/runtime.mdx to sdk/clinecore.mdx. Reframed the page to
lead with ClineCore as the full Cline harness, with Agent explained
at the bottom as the primitive you use when you want to skip the
harness. All original content preserved, just reordered so ClineCore
sections come first. Updated nav and redirects in docs.json.
2026-05-12 23:05:03 -07:00
Ara 7b48ead590 fix(core): avoid duplicating aggregate session usage (#10689) 2026-05-12 20:01:17 -07:00
Tomás Barreiro 11202ee476 Update to 0.0.40 (#10696) 2026-05-13 04:00:36 +02:00
Ara d364700d9b fix(llms): avoid double-counting OpenRouter usage cost (#10668) 2026-05-12 18:57:11 -07:00
Tomás Barreiro 95db8638ad Fix Windows clean script (#10683)
* Replace rm -rf with rimraf

* Use a script instead

* Remove rimraf references
2026-05-12 21:53:13 -03:00
Robin Newhouse 0e1a41133e Add concurrency to SDK live provider tests (#10672)
* Add live provider test concurrency

* Address live provider runner review comments
2026-05-12 17:30:44 -07:00
Saoud Rizwan 0b0ca9b1fa docs: add SDK documentation (#10350)
* docs: add comprehensive SDK documentation as top-level tab

Adds 27 new documentation pages for the Cline SDK (@clinebot/core,
@clinebot/agents, @clinebot/llms, @clinebot/shared) organized into
a dedicated "SDK" tab in the Mintlify docs navigation.

Structure:
- Getting Started: overview, quickstart, examples
- Core Concepts: agents, sessions, tools, streaming/events, extensions,
  hooks, providers/models
- Guides: building an agent, custom tools, writing extensions, permission
  handling, scheduled agents, multi-agent teams, connectors, production
- Architecture: layered stack overview, hub-spoke RPC, package reference
- API Reference: ClineCore, Agent, Gateway, Tools API, Events, Types
- CLI: commands, configuration, connector setup

Removes the old single-page SDK overview (cline-sdk/overview.md) that
documented the previous ACP-based ClineAgent API, and removes its
reference from the Cline CLI navigation group.

* docs: fix SDK docs review feedback

- Change "desktop app" to "JetBrains plugin" in overview (not released yet)
- Rename "Providers & Models" to "Model Providers"
- Rename "Streaming & Events" to "Streaming Events"
- Remove duplicate cli/connectors.mdx (guides/connectors.mdx covers everything)

* docs: add wizard commands, Discord connector, and platform credential details

* feat(docs): create CLI tab with new feature pages and updated reference

- Add CLI as its own top-level tab in docs navigation
- Remove CLI group from Docs tab and SDK tab
- Delete SDK CLI pages (content moved to CLI tab)
- Add new feature pages: connectors, scheduling, MCP servers, agent teams
- Rewrite cli-reference.mdx with all new commands (connect, mcp, schedule,
  rpc, checkpoint, doctor) and new flags (reasoning-effort, thinking,
  sandbox, teams, spawn, tool-enable/disable, autoapprove)
- Update configuration.mdx with new directory structure, env vars
  (CLINE_DATA_DIR, CLINE_RPC_ADDRESS, CLINE_SESSION_BACKEND_MODE,
  CLINE_SANDBOX), and MCP wizard reference

* docs: rename Docs tab to Extension and reorder tabs

Tab order: Extension, CLI, SDK, Kanban, Enterprise, API, Learn

* docs: simplify SDK install to single @clinebot/core package

@clinebot/core re-exports from agents, llms, and shared, so users
only need one install and one import source. Updated all getting-started,
quickstart, examples, and guide pages to import from @clinebot/core.
Concept and reference pages keep individual package imports since they
document those specific packages.

* docs: use @clinebot/sdk as the primary install and import path

@clinebot/sdk is an alias for @clinebot/core that re-exports from all
packages. All user-facing pages (overview, quickstart, examples, guides)
now show npm install @clinebot/sdk and import from @clinebot/sdk.
Architecture and reference pages keep individual package names since
they document the internal package structure.

* docs: rename Extension tab to Cline

* docs: fix packages page and merge duplicate imports

* fix: use getting-started page in CLI tab nav and remove conflicting redirect

* docs: rename CLI Getting Started page title to Overview

* docs: rename cline-cli/ to cli/ and cline-sdk/ to sdk/

Shorter, cleaner URL paths. Updated all internal links across every
doc page and added 44 redirects for old paths so existing URLs
don't break.

* docs: update hub-spoke architecture from gRPC/RPC to WebSockets

- Rewrite hub-spoke.mdx: WebSocket protocol, capability brokerage,
  spoke workers, session participants, hub daemon discovery
- Replace all RPC/gRPC/sidecar references with hub/WebSocket across
  all SDK and CLI docs
- Backend modes: local/hub/remote/auto (was local/rpc/auto)
- Hub command replaces rpc command (cline hub start/stop/status/ensure)
- Default port 25463 (was 4317), log file hub-daemon.log
- Remove @clinebot/rpc and @clinebot/scheduler from architecture docs
  (functionality absorbed into @clinebot/core)
- Update ClineCore reference to remove rpc options
- Add capability brokerage and session participant concepts

* docs: add Ecosystem page to SDK tab

* docs: move Ecosystem page after Guides and fix opening sentence

* docs: rename extensions to plugins across all SDK and CLI docs

- Rename AgentExtension to AgentPlugin in all code examples
- Rename ExtensionAPI to PluginAPI
- Rename extensions.mdx to plugins.mdx, writing-extensions.mdx to writing-plugins.mdx
- Rename all variable names (databaseExtension -> databasePlugin, etc.)
- Rename config field from extensions to plugins
- Update all prose, section headers, and cross-links
- Add redirects for old paths

* clean up attempt 1

* remove deprecated features

* part 1 of large provider changes

* a large pass on workflows

* cli ref updates

* config rewrite

* more concise instsallation and model selection

* provider reorg cont

* update providers

* one step further cleaning up

* kanban finishing clean up

* wip...

* another big cleanup - remove the CLI tabgroup

* sdk tighten up pt1

* more sdk doc tightening

* making more progress

* doc update based on bee latest branch

* nit update on codepaths

* remove learn section

* docs: add plugin install command documentation

Document the new clite plugin install command across CLI reference,
customization plugins page, SDK plugins concept page, and the writing
plugins guide. Cover all three source types (npm, git, local), the
package.json manifest format with cline.plugins field, host-provided
dependency handling, auto-detection logic, and the install directory
structure.

Reference cline/typescript-lsp-plugin as a concrete install example.

* docs: deduplicate plugin install docs

Trim redundant plugin install content from CLI reference, SDK plugins
concept page, and writing-plugins guide. Each now links to the
customization/plugins page as the single source of truth for manifest
format, install commands, and directory layout.

* further simplify the doc

* further clean up

* mark warnings

* docs: rename @clinebot to @cline and clite to cline

SDK packages moved to the @cline npm org. Update all docs references
to use @cline/sdk, @cline/agents, @cline/core, @cline/shared, @cline/llms
and the cline CLI binary name.

* docs: streamline SDK docs with example references (#10617)

* docs: streamline SDK docs with example references and @cline/sdk imports

Replace large standalone code blobs in SDK docs with references to
working examples in the SDK repository. Users can now clone and run
real code instead of copy-pasting from docs.

- Update all imports from @cline/agents, @cline/core, @cline/shared
  to @cline/sdk (the public-facing package)
- Fix model IDs from claude-sonnet-4-6 to claude-sonnet-4-20250514
- Quickstart: trim duplicate code patterns, add cards linking to
  cli-agent, code-review-bot, multi-agent, desktop-app examples
- Overview: add examples table with difficulty progression, update
  install instructions to use @cline/sdk
- Building an Agent: rewrite as a walkthrough of the code-review-bot
  example rather than inline code blobs across 4 separate files
- Creating Custom Tools: rewrite to use createTool with zod, add
  completion tools section, reference working examples
- Tools: show createTool with zod as primary pattern
- Events: reference multi-agent example for streaming UI pattern
- Architecture: update install to @cline/sdk

* fix: use claude-sonnet-4-6 model ID across all SDK docs

* remove connector page from sdk

* small reordering

* clean up sdk app and plugin examples

---------

Co-authored-by: Renee Huang <renee@cline.bot>

* remove features/connectors

* doc revisions

* fix references after folder change

* docs: fix SDK review feedback after rebase

* docs: address Greptile SDK review feedback

* docs: move TUI page under CLI nav

* docs: restore TUI page placement

---------

Co-authored-by: Renee Huang <renee@cline.bot>
Co-authored-by: John Simone <john@cline.bot>
Co-authored-by: Arafatkatze <arafat.da.khan@gmail.com>
2026-05-12 16:55:33 -07:00
Saoud Rizwan 1f0246655c Fix typo in notice-dialog.tsx terminal message 2026-05-12 15:36:13 -07:00
Saoud Rizwan 4494a29541 Revise changelog for Cline CLI 3.0.0 release
Updated the changelog to reflect the new Cline CLI features and improvements.
2026-05-12 15:32:57 -07:00
Ara 006ce519ff Fix SDK Qwen prompt cache routing (#10659)
* fix sdk qwen prompt cache routing

* Address qwen routing review findings

* Preserve anthropic lineage routing
2026-05-12 15:28:01 -07:00
Max 2e173d540d reduce workflow priveleges so that test.yml can run successfully (#10673)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-05-12 15:21:03 -07:00
Saoud Rizwan c91bafc73d ci(cli): wire CHANGELOG, README, and metadata into the publish flow (#10670)
Prepare cli-v3.0.0 as the first proper CLI release cut from cline/cline.

Workflow (.github/workflows/publish-cli.yaml):
- Add Get Previous CLI Tag step (git describe --match 'cli-v*' so the
  lookup ignores the VS Code extension's v* tags)
- Add Get Changelog Entry step (awk extraction from
  apps/cli/CHANGELOG.md, mirroring the extension's publish.yml)
- Switch to softprops/action-gh-release@v1 for the release step and
  paste the changelog content into the body
- Slack payload now includes the changelog body via toJSON plus an npm
  link, mirroring the extension's pattern
- Both the GitHub release compare link and the Slack compare suffix
  are guarded on prev_tag != '' so the first cli-v* release ships
  without a broken compare URL
- Drop bold formatting (**Full Changelog**, *Cline *) per the
  repo's no-bold rule

Publish script (sdk/apps/cli/script/publish-npm.ts):
- Copy apps/cli/README.md into the generated wrapper package so the
  npm registry listing has a real landing page
- Forward keywords, author, homepage, and bugs from the source
  package.json into the wrapper (the script previously only forwarded
  name/version/description/license/repository/bin/scripts/optionalDeps,
  so the metadata polish would have been invisible on the npm page)

Package metadata (sdk/apps/cli/package.json):
- Drop [EXPERIMENTAL] from description, align wording with the VS Code
  extension
- Add keywords, author, homepage, bugs

Changelog (sdk/apps/cli/CHANGELOG.md):
- Strip publication dates from all entries so the awk regex is just
  '^## [0-9]'
- Replace the bare 3.0.0 placeholder entry with a proper section
  framing this as the first release from the cline/cline monorepo

README and dev docs (sdk/apps/cli/README.md, DEVELOPMENT.md):
- Rewrite README as a user-facing npm landing page with header image
  and links table, aligned with the new cline/cline monorepo voice
  (Run Cline in your terminal — interactive chat or fully headless
  for CI/CD and scripting)
- Add Headless mode for CI/CD section with concrete pipe / JSON
  examples
- Cover all five registered connectors (Telegram, Slack, Google Chat,
  WhatsApp, Linear) with correct flag names
- Move dev-only sections (Publishing, Runtime Ownership, Connector
  runtime behavior, Logging Adapter) into DEVELOPMENT.md

Skill doc (sdk/apps/cli/.cline/skills/publish-cli/SKILL.md):
- Document the no-date header format and the workflow's awk-based
  changelog extraction
2026-05-12 15:19:34 -07:00
Saoud Rizwan b05ed159f1 Remove disclaimer from README
Removed disclaimer about active development and public contributions.
2026-05-12 14:55:20 -07:00
Saoud Rizwan 09885cb889 docs: rewrite README for monorepo structure (#10461)
* docs: rewrite README as product showcase and remove locales

Replaces the VS Code-only README with a product line overview
covering CLI, VS Code extension, JetBrains plugin, and SDK.
Adds dedicated feature sections for code editing, terminal
commands, Plan/Act mode, MCP and plugin extensibility,
multi-agent teams, messaging platform connectors, scheduled
agents, headless CI/CD mode, rules, and model provider support.

Removes the outdated locales/ directory with translated
README, CONTRIBUTING, and CODE_OF_CONDUCT files for 8 languages
that were no longer maintained.

* docs: add Kanban to product grid and reorganize as 2x2 layout

* docs: add line breaks at end of each product table cell

* docs: add line breaks at end of each product table cell

* docs: add more bottom padding to product table cells

* small updates to readme

* first attempt

* docs: fix README product links and examples

* docs: address Greptile README followups

* docs: add Vercel AI Gateway provider

* nit

* one more small change

* Update VS Code Extension link and status in README

* Fix link formatting for VS Code Extension in README

---------

Co-authored-by: Renee Huang <renee@cline.bot>
Co-authored-by: Arafatkatze <arafat.da.khan@gmail.com>
2026-05-12 14:29:17 -07:00
Renee Huang 05643483a8 fixing broken links (#10669) 2026-05-12 14:07:13 -07:00
Max c262bb61a6 publish-nightly improvements (#10539)
* publish-nightly improvements

* Apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Add concurrency group to publish-nightly workflow to prevent parallel runs on the same branch

The nightly publish script generates the extension version from a seconds-resolution timestamp, so parallel manual runs on the same ref can collide on the same version and cause publish failures or inconsistent tagging.

- Uses github.ref as the concurrency key so different branches run independently
- Sets cancel-in-progress: false to allow in-progress publishes to finish

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-12 14:00:26 -07:00
Saoud Rizwan 7cdaca0135 build: use README.marketplace.md for VS Code Marketplace publishes (#10663)
vsce reads README.md from the extension root at vsce package / vsce
publish time and has no flag to point it elsewhere, so to use a
different README on the VS Code Marketplace than on the GitHub repo
landing page we swap README.marketplace.md into README.md just before
packaging and put the original back afterwards.

scripts/marketplace-readme.mjs is the shared swap helper. The swap is
idempotent so the CI step in publish.yml can wrap the whole
package-and-publish block while the inner npm scripts still swap
themselves when run locally.

Wired into all three marketplace-bound paths:
  - npm run publish:marketplace and :prerelease via the new
    scripts/publish-marketplace.mjs wrapper
  - npm run publish:marketplace:nightly via publish-nightly.mjs
  - The vsce package call in publish.yml that builds the .vsix
    attached to the GitHub release, so manual installs match the
    marketplace listing

README.marketplace.md starts as a verbatim copy of README.md; future
PRs can repurpose README.md as a multi-product landing page covering
the SDK, JetBrains plugin, CLI, and VS Code extension while the
marketplace listing stays focused on the VS Code UX.
2026-05-12 12:32:07 -07:00
Tomás Barreiro 35062b05e9 Add SDK installation instructions to the CONTRIBUTING.md (#10661)
* Add SDK installation instructions to the CONTRIBUTING.md

* Add a build step

* Bun run build directly

* Instruct them to go back
2026-05-12 12:20:33 -07:00
Bee f84893b8c4 fix(core): hide teammate sessions from history by default (#10662)
Filter teammate/subagent sessions out of session history unless explicitly
requested. Increase the backend scan limit to compensate for filtered rows so
root session listings can still satisfy the requested limit.
2026-05-12 12:10:51 -07:00
Ara 5a4e63f31f fix sdk app examples (#10660) 2026-05-12 11:32:20 -07:00
Tomás Barreiro 03f47045f3 Fix bun run lint (#10652)
* Fix bun run lint

* Fix lint issues

* revert

* fix(cli): avoid unstable TUI effect dependencies

* fix(cli): remove stale effect suppression

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-11 19:47:50 -07:00
Saoud Rizwan 9b6fba6214 ci: move SDK publishing into cline/cline and retire legacy CLI infra (#10648)
* ci: migrate SDK publish workflows to repo root

Move publish-cli.yaml, publish-sdk.yaml, and test.yml (renamed to
sdk-test.yml) from sdk/.github/workflows/ to the repo root so GitHub
Actions actually picks them up. Adapt them to run with cwd sdk/ via
workflow-level defaults.run.working-directory, repoint repo guards
from cline/sdk to cline/cline, switch publish-sdk's nested test call
to sdk-test.yml, add path filters on sdk-test.yml so it doesn't fire
on extension-only PRs, and re-enable NPM_CONFIG_PROVENANCE now that
cline/cline is public.

Update the publish-cli skill with a cwd note so the documented
release commands keep working from sdk/.

* ci: remove legacy CLI publish workflows

The legacy publish chain (publish-cli-trusted.yaml dispatching into
npm-main.yaml + npm-nightly.yaml) publishes the old cli/ folder to
the same cline npm package the new SDK CLI is taking over. Leave
both wired up and a maintainer could accidentally publish an old
build over the handoff. Remove the dispatcher, the two callees, the
PR-preview tarball workflow (pack-cli.yml + build-cli-artifact.sh),
the TUI test workflow that only fed into them, and the npm packaging
script those workflows shared.

The cli/ source itself is left in place for a separate removal PR.

* chore: remove legacy CLI dev and eval helpers

With the legacy CLI publish workflows gone, the surrounding dev and
eval glue that only existed to feed those workflows is also dead.
Delete tests/e2e/cli/ (TUI tests), evals/smoke-tests/ (CLI smoke
evals), and the cline-evals-regression.yml workflow that drove them.

Trim the root package.json scripts that pointed at this infra:
- cli:link, cli:build, cli:run, cli:build:production, cli:watch,
  cli:test, cli:dev, cli:unlink (all delegated into cli/)
- compile-standalone-npm and postcompile-standalone-npm (only the
  removed npm-main/npm-nightly workflows called them)
- test:e2e:cli:tui (only the removed cli-tui-tests.yml called it)
- eval:smoke:*, which chained through cli:build + cli:link

Drop the trailing `cd ../cli && npx tsc --noEmit` segment from
check-types so the root typecheck stops walking into cli/.

The cli/ source itself stays in place for a separate removal PR.

* docs(evals): note removed smoke-tests layer

The evals/README.md and evals/ARCHITECTURE.md were structured around
smoke-tests as Layer 2 of the pyramid. With evals/smoke-tests/ and
the eval:smoke scripts gone, those references are stale. Add a top-
of-doc banner pointing at the removal rather than gutting both files
in this PR; a follow-up can scrub the structure when the framework
is updated for the new SDK CLI.

* chore(evals): restore smoke-tests, disable workflow pending rewire

The smoke-test scenarios in evals/smoke-tests/ are CLI-agnostic — each
scenario is just a config.json prompt plus optional template files —
so they're worth preserving across the legacy CLI sunset. Restore the
directory and the cline-evals-regression.yml workflow, but reduce the
workflow's triggers to workflow_dispatch only so it doesn't auto-run
in its current legacy-CLI-coupled form. Add a header comment pointing
at the rewire work.

Update the evals/README.md and evals/ARCHITECTURE.md banners from
"removed" to "temporarily disabled" to match reality.

Wiring the workflow at the new SDK CLI (and restoring the eval:smoke
root scripts) is left for whoever picks up the eval framework refresh.

* chore(evals): restore eval:smoke:run for ad-hoc smoke checks

The runner (`evals/smoke-tests/run-smoke-tests.ts`) shells out to
whichever `cline` is on $PATH, so it already works against the new
SDK CLI once `npm i -g cline` installs it. Add back just the single
`eval:smoke:run` script so docs and manual validation have a working
entrypoint. The build-and-link chain (`eval:smoke:build`, `eval:smoke`,
`eval:smoke:ci`) stays out — those need rewiring before they function.

Update the README/ARCHITECTURE banners accordingly.

* fix(ci): update SDK npm repository metadata

* fix(ci): tighten SDK publish workflows
2026-05-11 19:13:25 -07:00
Tomás Barreiro 6363b4ff6e Add Root False to the sdk biome config (#10647)
* Add Root False to the sdk biome config

* Ignore the SDK from the main biome files

* Ignore the sdk from the vscode build
2026-05-12 03:06:03 +02:00
Tomás Barreiro 17728dfc7c Merge pull request #10641 from cline/migration-update-sdk
Migrate from the Clinebot to the Cline org
2026-05-12 02:16:32 +02:00
BarreiroT f055082b7f Merge cline/sdk into sdk/ 2026-05-11 21:10:52 -03:00
Bee 6183dd6b4e fix: hide error for prompt upload (#99)
Do not display error about prompt upload to user.

Update Next.js from 16.1.6 to 16.2.6 and refresh related @next/env and SWC lockfile entries to keep framework dependencies current.
2026-05-12 01:55:35 +02:00
Saoud Rizwan bff7bf1c49 feat(cli): add kanban command and notice dialog (#97)
* feat(cli): add kanban migration flow

* refactor(cli): clean up kanban update flow

* fix(cli): match legacy kanban process handling

* test(cli): suppress migration notice in e2e

* fix(cli): address kanban notice review

* fix(cli): refine kanban notice copy
2026-05-11 16:53:08 -07:00
Robin Newhouse 2560c0c64a Support .cline workflow config paths (#93) 2026-05-11 23:22:21 +00:00
Robin Newhouse cf9fbaf64a ENG-1970 Preserve provider auth for CLI manual /compact (#23)
* Fix CLI manual compaction provider auth

* Fix CLI compaction reasoning settings
2026-05-11 16:22:15 -07:00
Robin Newhouse 991fb5ca69 Add CLI compaction mode setting (#61)
* Add CLI compaction mode setting

* Address CLI compaction review feedback

* Limit CLI compaction mode aliases

* Simplify config panel navigation state

* Use session callback props directly
2026-05-11 23:12:20 +00:00
Saoud Rizwan 79f61bfde6 feat(cli): publish as cline instead of @cline/cli (#96)
* feat(cli): publish wrapper as cline

* chore(cli): publish only cline wrapper

* docs(cli): assume cline package handoff

* docs(cli): clarify cline wrapper publishing
2026-05-11 16:04:50 -07:00
Robin Newhouse 9018513051 compaction: correctness pass for basic and agentic strategies (CLINE-2136) (#66)
* Truncate text block tool results in compaction

* Log context sizing diagnostics

* Use default reserve for compaction trigger

* Cap default compaction trigger at ninety percent

* Use provider-sized estimates for compaction

* Update manual compaction test for conservative estimates

* Truncate retained tool results during compaction

* Add live Codex compaction coverage

* Make basic compaction tool-pair atomic

Basic compaction's predicate-based removal could split a tool_use from its matching tool_result, leaving the assistant message with an orphaned tool_use. MessageBuilder then synthesized "Tool execution was interrupted before a result was produced." which the model echoed back to the user.

Make removal expand to all candidates linked by tool_use_id so tool_use and tool_result share a fate.

* Protect latest turn from basic compaction

* Extract token estimator to @cline/shared

agent-runtime had a local /4 estimator while compaction-shared had
a /3 estimator. Both compute the same concept (char-to-token rough
estimate) but with different bias. Reviewer noticed the discrepancy.

Centralize on /3 (slightly conservative, fires compaction trigger
earlier rather than later). Diagnostic logging in agent-runtime now
uses the same estimator, eliminating divergence.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Snap agentic compaction cut to a turn-start boundary

findCutIndex previously walked back by token budget and could land
between an assistant tool_use and its matching user tool_result.
The tool_use ended up folded into the summary while the tool_result
was preserved in the tail, producing an orphaned tool_result that
the provider rejects with:

  No tool call found for function call output with call_id ...

The same failure mode can occur in the inverse direction (orphaned
tool_use). Both leave the session unrecoverable.

Snap the cut to the nearest turn-start boundary at or before the
budget candidate. This keeps each turn — its typed user message
plus any tool_use/tool_result/assistant follow-ups — together,
either fully summarized or fully preserved.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 16:03:52 -07:00
Robin Newhouse 3e95f535da fix(cli): restore context bar with catalog model metadata (#94) 2026-05-11 15:12:40 -07:00
Saoud Rizwan b35804895c fix(core): allow type-only imports in TypeScript plugins (#68) 2026-05-11 14:53:07 -07:00
Saoud Rizwan 069135f8b4 feat(cli): rename command alias to cline (#95) 2026-05-11 14:52:25 -07:00
Bee b65585c978 fix: propagate turn mode through session input ENG-1951 (#59)
* fix: propagate turn mode through session input

Forward the selected agent mode from interactive submissions through the
hub client/server boundary into runtime hosts. Validate supported modes so
queued or steered turns honor the user's chosen execution mode.

* mode switching promise

* docs: note hub turn mode fallback

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-11 14:52:08 -07:00
dependabot[bot] 8224a82440 chore(deps): bump next (#69)
Bumps the npm_and_yarn group with 1 update in the /apps/examples/desktop-app directory: [next](https://github.com/vercel/next.js).


Updates `next` from 16.1.6 to 16.2.6
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v16.1.6...v16.2.6)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.2.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 23:45:18 +02:00
dependabot[bot] 6c189111c5 chore(deps): bump the cargo group across 2 directories with 2 updates (#70)
Bumps the cargo group with 1 update in the /apps/examples/desktop-app/src-tauri directory: [tauri](https://github.com/tauri-apps/tauri).
Bumps the cargo group with 1 update in the /apps/examples/menubar/src-tauri directory: [tauri](https://github.com/tauri-apps/tauri).


Updates `tauri` from 2.10.2 to 2.11.1
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-v2.10.2...tauri-v2.11.1)

Updates `rand` from 0.7.3 to 0.9.2
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-random/rand/compare/0.7.3...rand_core-0.9.2)

Updates `tauri` from 2.10.3 to 2.11.1
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-v2.10.2...tauri-v2.11.1)

---
updated-dependencies:
- dependency-name: tauri
  dependency-version: 2.11.1
  dependency-type: direct:production
  dependency-group: cargo
- dependency-name: rand
  dependency-version: 0.9.2
  dependency-type: indirect
  dependency-group: cargo
- dependency-name: tauri
  dependency-version: 2.11.1
  dependency-type: direct:production
  dependency-group: cargo
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 23:36:08 +02:00
Bee cf011d0f29 feat: error telemetry to gateways (#91)
* feat: propagate runtime telemetry to gateways

Thread the configured telemetry service from agent runtimes into
gateway/provider calls so model activity can be observed consistently.

Emit agent telemetry using structured capture payloads and report
unhandled agent run failures via SDK error capture. Update tests and
mocks to use the expanded telemetry service API.

* apply feedback

* patches
2026-05-11 14:32:09 -07:00
Saoud Rizwan 9bef7613fa feat: add SDK cookbook examples with difficulty progression (#90)
* feat: add SDK cookbook examples with difficulty progression

Add three new example apps to apps/examples/ organized as a
difficulty ladder from beginner to advanced, similar to Cursor's
SDK cookbook. Includes a top-level README linking all examples.

- quickstart: minimal ~15 line agent, send a prompt, stream response
- cli-agent: interactive terminal chat with a shell tool
- code-review-bot: structured code review with custom tools and
  completion lifecycle

* feat: add multi-agent fan-out example with streaming web UI

Spawns three specialist agents in parallel, streams their responses
to the browser via SSE, then a synthesizer agent combines their
findings into a unified answer. Single-file server with inline HTML.

* chore: replace pnpm with bun in example docs and source

* fix: use claude-sonnet-4-6 model ID in examples

* fix(examples): use Cline keys in cookbook

* docs(examples): add SDK build step
2026-05-11 14:31:55 -07:00
Saoud Rizwan 9aac7f903d fix(cli): improve steering ux (#87)
* Improve queued prompt steering UX

* fix(cli): refine queued prompt steering

* fix(core): clear queued prompts on abort

* test(cli): isolate root keyboard routing helper
2026-05-11 14:20:13 -07:00
Robin Newhouse 4c5519af38 fix headless thinking stats stdout (#92) 2026-05-11 14:16:39 -07:00
Saoud Rizwan 025bdb0ef4 feat: add MCP OAuth support (#72)
* feat: add MCP OAuth support

* feat: add MCP OAuth to CLI wizard

* chore: remove desktop sidecar MCP OAuth change

* chore: remove architecture MCP OAuth note

* fix: validate MCP OAuth callback state

* fix: address MCP OAuth review feedback
2026-05-11 23:05:58 +02:00
Saoud Rizwan 92cb86d784 feat(cli): group providers by popularity (#64)
* feat(cli): group providers by popularity

* refactor(llms): expose popular providers as capability

* refactor(cli): preserve provider catalog order
2026-05-11 13:47:38 -07:00
Bee 2c455d9ca0 feat: gitignore read file plugin example (#54) 2026-05-11 13:22:21 -07:00
Bee 2ba7f8fa43 docs: add comprehensive Cline plugin authoring guide (#81)
Add a new .cline skill documenting how to design, build, package, test, and distribute Cline agent plugins. Cover lifecycle, manifests, capabilities, tools, hooks, and single-file versus package workflows so agents can reference a self-contained authoring guide.
2026-05-11 13:18:00 -07:00
Robin Newhouse 278d37b72a fix(llms): use limit.input as contextWindow when present (CLINE-2136) (#84)
* Use input limits for model context windows

* Fix maxTokens discount when input becomes contextWindow

The existing "if contextWindow === outputToken, discount to 5%" heuristic was a safety net for models.dev returning bogus context==output data. After switching contextWindow resolution to min(input, context), the equality check started misfiring on legitimate input==output configurations like gpt-5-pro, o3-pro, and codex-mini.\n\nPin the discount to the raw context limit so the original safety net behavior is preserved.

* Rename ModelInfo.contextWindow to maxInputTokens

The field has been used as an input-token budget throughout the
codebase (compaction trigger, status-bar utilization, gateway
passthroughs) but the name suggested combined input+output context.
After the catalog change to read limit.input from models.dev, the
field's semantics now match its actual use: a prompt ceiling.

Renames:
- ModelInfo.contextWindow -> ModelInfo.maxInputTokens
- GatewayModelDefinition.contextWindow -> maxInputTokens
- CoreCompactionConfig.contextWindowTokens -> maxInputTokens
- CoreCompactionContext.contextWindowTokens -> maxInputTokens

Internal consumers updated. User-facing settings schemas
(provider-settings, remote-config, RPC settings, test fixtures)
intentionally keep their existing names to avoid breaking user
configs and the API server contract.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Rename internal helpers and constants to match maxInputTokens

Naming-only refactor to complete the field rename. No behavior change.

- DEFAULT_CONTEXT_WINDOW_TOKENS -> DEFAULT_MAX_INPUT_TOKENS
- DEFAULT_CONTEXT_WINDOW -> DEFAULT_MAX_INPUT_TOKENS
- FALLBACK_MANUAL_COMPACTION_CONTEXT_WINDOW_TOKENS -> FALLBACK_MANUAL_COMPACTION_MAX_INPUT_TOKENS
- resolveContextWindowLimit -> resolveMaxInputTokens
- getContextWindowTokens -> getMaxInputTokens
- resolveModelContextWindow -> resolveModelMaxInputTokens
- hasContextWindow -> hasMaxInputTokens
- formatContextWindow -> formatTokenCount

Also drops the legacy snake_case context_window fallback in
resolveModelMaxInputTokens, since this PR is a hard break and
no downstream data path emits that shape any longer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Rename TokenConfig.maxContextTokens to maxInputTokens

Last consumer of the 'context tokens' naming, used internally to
override a model's input ceiling. Renaming for consistency with
ModelInfo.maxInputTokens.

User-facing settings.contextWindow stays as-is and now maps to
TokenConfig.maxInputTokens at the boundary.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Keep context window alongside input limits

* Fallback to contextWindow for interactive compaction

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 20:02:18 +00:00
Tomás Barreiro 6bb730ef19 Update axios (#10575)
* Update axios

* fix types
2026-05-11 21:54:19 +02:00
Tomás Barreiro 44bc07310c Remove axios from evals (#10574) 2026-05-11 21:42:36 +02:00
Saoud Rizwan dadf57a029 feat: add MCP server settings toggles (#82)
* feat: add MCP server settings toggles

* fix: address MCP manager review feedback
2026-05-11 12:39:51 -07:00
alex-lum 52bf59f4ff chore: add Greptile configuration for telemetry rules (#73)
Introduce `.greptile/config.json` and `.greptile/files.json` to enforce
SDK telemetry conventions during code review, including tool handler
instrumentation, session lifecycle helpers, event name sourcing from
CORE_TELEMETRY_EVENTS, auth flow completeness, and documentation sync.
2026-05-11 21:27:36 +02:00
Bee a06658a09b fix(llms): reasoning config for OpenRouter (#67)
* fix(llms): reasoning config for OpenRouter

OpenRouter is the exception because it does not reliably interpret the generic AI SDK/OpenAI-compatible thinking knobs the same way as the routed underlying model.

For most OpenAI-compatible providers, disabling thinking can be represented as something like:

```ts
providerOptions: {
  openaiCompatible: {
    thinking: { type: "disabled" }
  }
}
```

But OpenRouter has its own first-class reasoning control object. To suppress reasoning/thinking content, OpenRouter expects:

```ts
providerOptions: {
  openrouter: {
    reasoning: { exclude: true }
  }
}
```

That is why we need a separate OpenRouter rule.

The bug was happening because “thinking set to none” was being translated through the generic path, or mixed with family-specific `thinking` patches. For OpenRouter-routed models, that generic `thinking.type=disabled` shape may not actually tell OpenRouter to exclude reasoning content from the response. So the user could set thinking to none, but OpenRouter would still return reasoning/thinking text.

The refactor makes this explicit:

- OpenRouter suppresses generic thinking options.
- OpenRouter suppresses generic flat effort options.
- OpenRouter maps our internal request shape to OpenRouter’s expected `reasoning` shape:
  - `enabled: false` -> `{ exclude: true }`
  - `budgetTokens` -> `{ max_tokens: ... }`
  - `effort` -> `{ effort: ... }`
  - `enabled: true` -> `{ enabled: true }`

So the fix is not just “separate config for neatness”; it is required because OpenRouter’s disable semantics are different. `reasoning.exclude=true` is the instruction that actually prevents reasoning content from being returned.

* patches

* qwen caching

* feedback

* chore(llms): format usage normalization fallback

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-11 11:55:33 -07:00
Mikołaj Kondratek 418a8a6325 perf(file-search): route @-mention picker through host index when available (#10592)
* perf(file-search): route @-mention picker through host index when available

Adds a new SearchWorkspaceItems RPC on WorkspaceService that lets
hosts serve the @-mention file search from their own native index
(JetBrains FilenameIndex, eventually anything similar). When the
host returns results, core skips ripgrep entirely; when the host
throws, core falls back to ripgrep as today.

Why: on slow filesystems (the CLINE-2092 reporter is on a
2000-mile SSHFS mount) ripgrep's stat fan-out blows up to 15s+
per keystroke as it walks the workspace. JetBrains already has
the answer in memory.

Contract:
- searchWorkspaceItems thrown error  -> host can't answer, use ripgrep.
- searchWorkspaceItems returns items -> authoritative, including [].
This split lets a host that says "zero matches" short-circuit the
ripgrep fallback, which is the entire point of the change for slow
filesystems.

Implementation notes:
- VS Code / CLI / ACP host adapters throw "not implemented". Core
  swallows the throw silently in the @-mention path because on
  these hosts the throw is steady state, not an error worth
  logging on every keystroke.
- Telemetry: captureMentionSearchResults now records a search_source
  property (host_index | ripgrep) so we can see how often the host
  index actually picks up the load per fs_class.
- Multiroot aggregation reports source=host_index only when *every*
  contributing root used the host index; any root falling back to
  ripgrep marks the aggregate as ripgrep so the metric isn't misleading.
- Telemetry calls in searchFiles.ts are now fire-and-forget; the
  webview shouldn't block on a metrics flush.

CLINE-2092

* fix(file-search): scope host-index per workspace root in multiroot

In multi-root projects searchWorkspaceFilesMultiroot calls
searchWorkspaceFiles once per root. Each call hit the host index
without telling it which root, so a JetBrains host returned
project-wide results; the caller then path.join'd those against the
wrong base, lstat silently swallowed the ENOENT, and the user saw
fabricated paths.

Add an optional workspace_path field to SearchWorkspaceItemsRequest
and forward the workspacePath argument into executeHostIndexForFiles.
Hosts that can't honor the field ignore it; this preserves
project-wide behavior for older plugins paired with newer core.

Surfaced in code review on CLINE-2092.

* file-search: distinguish unimplemented host index from real failures

Previously the catch in executeHostIndexForFiles returned null on every
exception with no client-side trace. That's the right behavior on
VS Code/CLI/ACP where the RPC stub throws every keystroke, but it also
hides real degradation on JetBrains (UNAVAILABLE during indexing,
INTERNAL, transport errors) — operators have no way to tell whether a
ripgrep fallback was expected or a slow-path regression.

Split: gRPC code 12 / messages matching /not implemented/i log at debug
(steady state, stays quiet); everything else logs at warn with the code
and message so degraded sessions are visible. Fallback policy unchanged
— still returns null and lets the caller use ripgrep — and core has no
useful action on the error type, so we don't propagate further.

Surfaced in code review on CLINE-2092.

* fix(file-search): dedup host folders against parent-walk inferred dirs

When the host index returns a folder explicitly (e.g. 'src') *and* a file
underneath it (e.g. 'src/main.ts') for the same query, the parent-walk that
seeds the inferred directory set was re-adding 'src' as an inferred parent,
producing two identical entries in the picker.

Pre-pass the host items to record which directory paths were returned as
explicit folders, then skip those during the parent-walk so we don't double
list them. Transitive ancestors above an explicit folder are still added
because the loop keeps walking up.

Adds a regression test that reproduces the duplicate and fails without the
fix.
2026-05-11 12:00:16 +02:00
Ara 4850ac4ebd add Exa web search plugin (#40)
* add web search plugin example

* simplify web search plugin to exa

* make web search plugin installable

* clarify web search plugin CLI usage

* Address web search plugin review feedback

* chore: merge main into web search plugin PR

* chore: remove shared changes from web search PR

* docs: limit web search README changes

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-10 18:06:04 -07:00
John Simone e36626815f Sanitize Unicode Surrogates (#34)
* sanitize unicode surrogates

* change ordering of sanitization
2026-05-10 12:28:40 -07:00
Bee 21ce68e3a3 chore: add .tool-versions with pinned bun (#75)
Replace mise.toml with a .tool-versions file for toolchain version management,
aligning with the asdf/mise .tool-versions convention and adding an explicit
bun version pin alongside node.

- Remove mise.toml (previously only pinned node 22)
- Add .tool-versions pinning node 22 and bun 1.3.13
- Tighten the bun engine field in package.json from >=1.3.13 to an exact
  1.3.13 pin, matching the new toolchain file
2026-05-10 12:13:55 -07:00
Saoud Rizwan e107f6eea9 fix(cli): skip API key prompt for Codex CLI provider (#83)
* fix(cli): skip API key prompt for Codex CLI provider

* fix(cli): address Codex CLI review feedback
2026-05-10 12:11:33 -07:00
Robin Newhouse 9d45b7582a Fix Gemini live cache probe (#89) 2026-05-08 21:50:41 -07:00
Tomás Barreiro 00b0d805ac Upgrade version to 0.0.39 (#86) 2026-05-09 02:10:28 +02:00
Ara ab2a791e84 Separate aggregate team usage (#4)
* fix: aggregate agent team usage

* fix: separate aggregate team usage

* fix: address usage aggregation review feedback

* fix: consolidate session usage summary

* fix: project and persist aggregate usage
2026-05-08 16:05:56 -07:00
Tomás Barreiro 7ab12dbde8 Replace the DEFAULT_API_BASE_URL with a dynamic one derived from the env (#80)
* Introduce Cline environments to the SDK

* Add partial to the process env

* Replace the default cline api url with a dynamic one

* Make the default base url dynamic

* add tests
2026-05-08 23:45:56 +02:00
Tomás Barreiro 05848affdf Introduce Cline environments to the SDK (#78)
* Introduce Cline environments to the SDK

* Add partial to the process env
2026-05-08 23:23:05 +02:00
Bee a91e9f1caf refactor: promote remote-config primitives from enterprise (#65)
* feat: promote remote-config primitives from enterprise

Move remote-config schemas, managed instruction materialization, blob upload metadata, and OpenTelemetry config normalization into @cline/shared so managed configuration can be reused without an enterprise package dependency. Update architecture guidance and runtime coverage for rule/workflow materialization.

* patches

---------

Co-authored-by: BarreiroT <tomasmbarreiroi@gmail.com>
2026-05-08 23:19:57 +02:00
Tomás Barreiro 0e9d36bb54 Centralize the API url (#77)
* Centralize the API url

* Export constant in the index.browser
2026-05-08 22:12:00 +02:00
Bee 526fa3949d chore: rename and polish plugin examples (#76)
* chore: rename and polish plugin examples

## Summary

Renames and reorganises the plugin example directories for clarity and consistency, and rewrites the top-level `plugins/README.md` to be more scannable:

- `weather-plugin.example.ts` → `weather-metrics.ts`
- `subagent-plugin/` → `agents-squad/` (package renamed to `cline-agent-squad-plugin`)
- `typescript-lsp-plugin/` → `typescript-lsp/`
- Drops `maxIterations` caps from agent preset frontmatter (`anvil`, `oracle`, `phantom`, `inquisitor`)
- Bumps `inquisitor`'s model from `gpt-5.4` → `gpt-5.5`
- Removes the inline standalone demo from `typescript-lsp/index.ts` (was duplicating the pattern already in `weather-metrics.ts`); export surface trimmed to just `plugin`
- Removes the `AgentExtension` duplicate export and adds a `Logger` alias for `BasicLogger` in `@cline/core`'s public index; also drops types that leaked into the published surface

* path updates
2026-05-08 13:00:13 -07:00
Mikołaj Kondratek c52e419dc2 Tag mention-search telemetry with workspace filesystem class (#10567)
* Tag mention-search telemetry with workspace filesystem class

Adds best-effort filesystem-type detection for the workspace root and
emits it as `fs_class` (local | network | unknown) and `fs_type` (apfs,
ext4, macfuse, nfs, smb, ...) on `task.mention_search_results` and
`task.mention_failed`. Lets us slice slow / empty / failing mention
searches by mount type so reports of "the @-picker is slow / returns
nothing" can be triaged against actual environment, not anecdote.

Implementation:

- `src/utils/fs-info.ts` — `getFsInfo(path)`, cached per-path for the
  process lifetime (filesystems don't change at runtime). Returns the
  `unknown` sentinel on every error path; the picker must never break
  because of telemetry. Detection is platform-specific:
    * macOS: parses `mount(8)` output and picks the longest matching
      mount point. BSD `stat` has no portable filesystem-type flag —
      `stat -f` is a format-string mode where `%T` means "file type"
      (regular/directory/etc.), not "filesystem type", which is a
      common pitfall when porting Linux scripts.
    * Linux: GNU `stat -f -c %T -- /path` returns the FS type as a
      string ("ext2/ext3", "btrfs", "fuseblk", ...).
    * Windows: not implemented yet, returns the unknown sentinel.
      `GetVolumeInformationW`/`GetDriveType` would be the way in if
      signal warrants it.
  Symlinks are resolved with `fs.realpath` before matching so e.g.
  `/tmp` (a symlink to `/private/tmp` on macOS) classifies correctly.
  All FUSE variants — sshfs, FUSE-T, NTFS-3G, gocryptfs, rclone — are
  bucketed as "network" because macFUSE doesn't expose the underlying
  driver and they share the not-actually-local performance profile
  that motivated this work.

- `src/services/telemetry/TelemetryService.ts` — both
  `captureMentionSearchResults` and `captureMentionFailed` gain an
  optional `fsContext` parameter; properties are emitted sparsely so
  the nine existing `captureMentionFailed` callsites in
  `mentions/index.ts` are untouched.

- `src/core/controller/file/searchFiles.ts` — declares `fsContextPath`
  at function scope so the catch block can also pass it; sets it from
  the resolved workspace path (multi-root: primary root only — tagging
  per-root would mean per-root events, which we'd rather defer until
  we see signal). Hits both success and error paths.

- `src/utils/fs-info.test.ts` — covers undefined / empty / non-existent
  paths, tmpdir classification, and cache-hit identity.

PostHog properties are schemaless, so no schema migration: `fs_class`
and `fs_type` start appearing in the events index automatically once
the first event with them lands. Pre-PR events have those properties
NULL, so dashboard queries should scope to `timestamp >= <merge-time>`
for clean network-vs-local comparisons.

* Tag mention-search telemetry with workspace filesystem class - fixes

* Tag mention-search telemetry with workspace filesystem class - fixes

* Don't block searchFiles response on FS-class telemetry

Move getFsInfo() + telemetryService.capture* off the awaited path in
searchFiles. The picker awaits this RPC on every keystroke, so any
delay here translates directly into a frozen UI; we already saw a
report (CLINE-1814) where SSHFS made the realpath/mount lookups slow
enough that users saw an apparent 'no results' state until the call
completed. Capture is now scheduled via a fire-and-forget helper that
swallows and logs errors.

Also add a 3s outer Promise.race timeout in fs-info around realpath +
mount detection. The inner execFile already has its own 2s, but
realpath has no timeout and a stale network mount can hang it. With
the outer cap, getFsInfo always settles within ~3s and falls back to
the unknown sentinel. The picker no longer waits on it anyway, but
this also keeps the per-process cache from being polluted by an
indefinitely pending entry.

* Tag fs-class telemetry against the workspace actually searched

In multi-root mode searchFiles always wrote getRoots()[0] into
fsContextPath, so a search hinted at a slow secondary root
(e.g. an SSHFS-backed sibling of a local-disk primary) was tagged as
the primary's fsClass. That hides exactly the cases we wrote this
telemetry to surface.

Resolve workspaceHint to its root the same way searchWorkspaceFilesMultiroot
does (by name first, then by path), and tag against that. Cross-root
searches with no hint still fall back to the primary root — attributing
a single event correctly there would require per-root events, deferred
until we see signal.

Don't poison the fs-info cache with UNKNOWN

Previously every getFsInfo() result, including the shared UNKNOWN
sentinel returned by failure paths (timeout, exec error, unsupported
platform), was written to the per-process cache. A single transient
slow detection — exactly the case this telemetry was added to surface
— would therefore tag that path as 'unknown' for the rest of the
process and never re-attempt classification.

Skip the cache write when info === UNKNOWN. Reference equality
distinguishes the failure sentinel from a successful detect() that
happens to land in the 'unknown' bucket (which returns a fresh object
with a real fsType — that result is still cached normally).

Adds a _getFsInfoCacheSizeForTests helper and a test that asserts the
cache stays empty across UNKNOWN-returning calls, then grows by one
on a successful classify.
2026-05-08 10:45:19 -07:00
Mikołaj Kondratek c8a1f2d217 fix: allow write_to_file with empty content (#10593)
* fix: allow write_to_file with empty content

The `!rawContent` check treated empty string as falsy, rejecting
legitimate empty-file creation. Changed to `rawContent == null` in
both the execute guard and validateAndPrepareFileOperation so that
`content: ""` flows through normally.

Three sites were affected:
- execute(): `!rawContent` → `rawContent == null`
- validateAndPrepareFileOperation(): `else if (content)` → `else if (content != null)`

The handlePartialBlock early-return (`!rawContent && !rawDiff`) is left
as-is — during streaming an empty string genuinely means "no data yet",
so skipping is correct there.

* Fixes
2026-05-08 15:14:54 +02:00
Saoud Rizwan edf5ec52ba fix(cli): abort active turn before mode switch (#71) 2026-05-07 23:02:40 -07:00
Renee Huang e215d3d91c chore: rename example folders; fix references (#60)
* rename folders

* fix examples reorg references

* move over files

* having agent fixing and validating all examples are still working

* fix sidecar paths

---------

Co-authored-by: abeatrix <beatrix@cline.bot>
2026-05-07 20:38:04 -07:00
Robin Newhouse 2b2ff9667d Fix Codex OAuth model list (#63) 2026-05-07 20:27:25 -07:00
Saoud Rizwan 649b80ce82 feat(cli): set terminal title from active chat (#62) 2026-05-07 17:40:04 -07:00
Bee a85047cb8a fix: resolve workspace export aliases reliably (#56)
* fix: resolve workspace export aliases reliably

Support additional package export conditions when building workspace aliases and fall back from dist JS exports to src TS files when needed. Sort aliases by specificity so jiti resolves subpath aliases before broader package aliases.

* prefer import.meta.resolve for host module lookup

Resolve host runtime specifiers with import.meta.resolve first and normalize file URLs to paths. Fall back to require.resolve for CommonJS packages and older resolution edge cases.

* patches

* patch

* fix
2026-05-07 16:57:41 -07:00
Saoud Rizwan 19ac9e1608 feat(cli): add command palette (#21)
* feat(cli): add command palette

* fix(cli): preserve keyboard behavior with palette shortcuts

* feat(cli): add skills to command palette

* fix(cli): simplify command palette row copy

* fix(cli): remove command palette skill section
2026-05-07 16:16:13 -07:00
Robin Newhouse 0903768dd9 Fix TUI startup slash crash (#38) 2026-05-07 14:37:58 -07:00
Bee d082f580c6 feat: add plugin install support for npm, git, and local sources (#35)
* feat: add plugin install support for npm, git, and local sources

Implement plugin source parsing and installation into the Cline plugin directory. Support local files/directories, npm packages, and git repositories, including force installs and dependency handling. Add tests for parsing, install behavior, and config discovery.

Examples:

```sh
clite plugin installi ./my-plugin
clite plugin i https://github.com/owner/repo.git
clite plugin i --npm @scope/plugin
clite plugin i --git github.com/owner/repo
clite plugin install --git https://github.com/owner/repo.git
```

Manuall Test:
```
bun run build
bun run cli plugin install https://github.com/abeatrix/demotest.git
```

* patches:

.git exclusion now filters by basename(sourcePath) !== ".git", so the .git directory itself is not copied.
Hostname-style sources like github.com/owner/repo now throw a guided error unless --git is used, instead of falling through to a confusing local-path failure.
plugin-module-import no longer reads and statically scans every plugin file unconditionally. It pre-registers known host-provided @cline/* SDK aliases, and only reads the plugin source for static specifier analysis when preferHostRuntimeDependencies actually requires it.

* tighten

* tighten

* add --omit=peer
2026-05-07 13:15:40 -07:00
Bee 40b0d2ee5d fix: retry daemon spawn when CLI binary is busy CLINE-2115 (#27)
* fix: retry daemon spawn when CLI binary is busy CLINE-2115

Retry detached hub daemon startup on transient ETXTBSY spawn failures before polling discovery. This handles package-manager updates that replace the CLI binary during restart, with tests and architecture docs covering the behavior.

* use fake timer

* retry prewarm

* retry

* @clinebot to @cline
2026-05-07 13:15:13 -07:00
Saoud Rizwan 41cb3047a3 fix: harden CI permissions and add workflow CODEOWNERS (#53)
* fix: harden CI permissions and add workflow CODEOWNERS for public repo

- Remove unnecessary checks:write and pull-requests:write from test.yml
  (no step uses them; GitHub creates check runs automatically with
  contents:read)
- Move publish-sdk.yaml permissions from workflow-level to job-level so
  the test caller job gets only contents:read while the publish job
  gets contents:write + id-token:write
- Add CODEOWNERS entry restricting .github/workflows/ changes to
  saoudrizwan, abeatrix, and maxpaulus43 (enforced by the existing
  ruleset's require_code_owner_review setting)

* fix: add BarreiroT to workflow CODEOWNERS

* fix: scope CODEOWNERS for .github to saoudrizwan, abeatrix, and BarreiroT
2026-05-07 13:08:14 -07:00
Bee 6d4a323476 chore: publish only dist artifacts in package exports (#52)
Remove development export entries that pointed at source TypeScript files and limit published package files to dist output. Update core tsconfig path handling so local builds still resolve workspace sources while consumers use compiled artifacts.
2026-05-07 11:58:43 -07:00
Ara 3cb94a920c docs: clarify Telegram connector usage (#42)
* docs: clarify Telegram connector usage

* docs: add Telegram connector guide

* docs: fix Telegram command list wording

* docs: clarify Telegram chat schedule commands
2026-05-07 18:52:49 +00:00
Saoud Rizwan b53f7a54f2 chore(cli): release v0.0.13 2026-05-07 11:34:35 -07:00
Ara ec7fd4a400 Derive prompt-cache capability from cache write pricing (#44)
* Derive prompt-cache from cache write pricing

* fix: prompt cache detection (#45)

Regenerate the model catalog with updated provider metadata, including Xiaomi MiMo naming, family, capability, and token limit changes.

Detect prompt-cache support when either cache read or cache write pricing is present so write-only cache support is represented correctly.

* Update packages/llms/src/catalog/catalog-live.ts

Co-authored-by: Bee <68532117+abeatrix@users.noreply.github.com>

* require number to be non-zero

---------

Co-authored-by: Bee <68532117+abeatrix@users.noreply.github.com>
Co-authored-by: abeatrix <beatrix@cline.bot>
2026-05-07 18:26:59 +00:00
Saoud Rizwan 3062ead300 feat(cli): dual-publish @clinebot/cli mirror wrapper (#43)
* feat(cli): dual-publish @clinebot/cli mirror wrapper

After publishing @cline/cli, the publish script now also publishes an
identical wrapper under @clinebot/cli so existing users who installed
via npm i -g @clinebot/cli continue receiving updates. The mirror
wrapper references the same @cline/cli-* platform packages.

* fix(cli): preserve legacy wrapper updates
2026-05-07 11:23:11 -07:00
Renee Huang a73a73096e docs: update README (#46) 2026-05-07 18:18:27 +00:00
Heyang Wang b8f40f4edc fix(mcp): ensure callTool sends empty object when toolArguments is undefined (#10553) (#10568)
- Fix toolArguments fallback to {} to comply with MCP SDK Zod validation (ZodRecord<ZodString, ZodUnknown>)

- Add comprehensive unit tests for McpHub.callTool() covering argument handling, request structure, error handling, telemetry, and response handling

Co-authored-by: heyang.why <heyang.why@alibaba-inc.com>
2026-05-07 09:08:41 -07:00
Bee ad47f527db test: clean up blocker sockets in hub fallback test (#37)
Track and destroy sockets opened by the blocking HTTP server so the port fallback test can close reliably. Also mark an unused compaction test callback argument.
2026-05-06 20:11:19 -07:00
Robin Newhouse 1fd763e0b7 fix: omit truncation for codex responses (#29)
* fix: omit truncation for codex responses

* test: cover openai codex live provider

* test: address greptile review feedback

* test: move live provider tests under tests

* fix: update live test helper package scope
2026-05-06 19:22:09 -07:00
Saoud Rizwan 5fa1ee8cff chore(cli): release v0.0.12 2026-05-06 19:18:22 -07:00
Saoud Rizwan 3caea18cf7 fix(cli): embed tree-sitter parser worker in compiled binary (#36)
* fix(cli): embed tree-sitter parser worker in compiled binary

The standalone CLI binary was missing syntax-highlighted markdown
rendering (headers, inline code, quotes, bold, etc.) while tables
rendered fine. This happened because the tree-sitter parser worker
from @opentui/core was never embedded as an entrypoint in the
compiled binary, so TreeSitterClient.startWorker() couldn't find it
at runtime.

The fix adds parser.worker.js as a second entrypoint to the build
command, enables --splitting (required for multi-entrypoint compile),
and defines OTUI_TREE_SITTER_WORKER_PATH so opentui resolves the
embedded worker from Bun's virtual filesystem (bunfs). The relative
path is computed from the workspace root (rootDir) to match how Bun
lays out files in bunfs.

* refactor(cli): switch build from CLI command to Bun.build() API

Aligns with how opencode structures their compiled binary build.
Uses the programmatic Bun.build() API with explicit entrypoints,
splitting, and define options instead of shelling out to
`bun build --compile`.
2026-05-06 19:15:57 -07:00
John Simone 84c9c95307 bump up timeout for yolo mode test (#33) 2026-05-07 02:02:08 +00:00
Saoud Rizwan 5b30ab1f8e Add security policy
Updated the security policy to clarify supported versions and reporting procedures.
2026-05-06 18:09:26 -07:00
Robin Newhouse a79f2de5da fix: respect --thinking none when persisted reasoning exists CLINE-2087 (#26)
* fix: respect --thinking none when persisted reasoning exists

Two-layer fix for DeepSeek API error "thinking options type cannot be
disabled when reasoning_effort is set":

1. CLI: Add thinkingExplicitlySet flag to ParsedArgs so --thinking none
   is distinguished from "not passed" and not overridden by persisted
   ProviderSettings.reasoning.effort

2. LLMs: Guard buildCompatibleEffortOptions on reasoning.enabled !== false
   to prevent conflicting reasoningEffort/effort fields from being emitted
   alongside thinking.type=disabled in the openaiCompatible provider options

Closes: CLINE-2087

* test: add DeepSeek v4 Flash thinking-off regression entries

Add two live test entries for the model reported in CLINE-2087:
- deepseek-v4-flash-thinking-off: basic enabled=false
- deepseek-v4-flash-thinking-off-with-effort: enabled=false with
effort=xhigh (exact bug scenario — effort leaking alongside disabled)
2026-05-07 00:14:45 +00:00
Saoud Rizwan c84da27d09 refactor: rename @clinebot npm scope to @cline (#24)
* refactor: rename @clinebot npm scope to @cline

Rename all package names, dependencies, imports, workflow references,
build scripts, and documentation from @clinebot/* to @cline/* to match
the newly claimed @cline npm organization.

* fix: complete @cline scope rename

* chore: update root lockfile package scope

* fix: update usage cost import scope
2026-05-06 17:01:54 -07:00
Robin Newhouse 3a071c779f Fix CLI prewarm mock (#32) 2026-05-06 16:51:31 -07:00
Saoud Rizwan fe47a63d0e fix: inject steer prompts before model requests (#30)
* fix: inject steer prompts before model requests

* chore: split dev hub owner changes

* chore: keep tool result ordering fix in core

* fix: wrap steered prompts as user input
2026-05-06 16:41:23 -07:00
Saoud Rizwan aecaedbf11 fix(cli): handle file index prewarm timeouts (#20) 2026-05-06 15:49:27 -07:00
Saoud Rizwan da07818ac0 fix(cli): add keyboard shortcuts for scrolling (#19)
* fix(cli): add keyboard transcript scrolling

* fix(cli): preserve input editing keys

* fix(cli): document transcript page scroll shortcuts
2026-05-06 15:47:46 -07:00
Robin Newhouse 6de238e883 Preserve input when selecting skills CLINE-2124 (#28)
* Preserve input when selecting skills

* Address skill picker review comments
2026-05-06 15:29:42 -07:00
Saoud Rizwan b307a1159f fix(cli): hide chatgpt subscription provider usage costs (#18)
* fix(cli): hide subscription provider usage costs

* fix(llms): source usage cost display from provider metadata

* fix(cli): hide subscription costs in remaining displays
2026-05-06 15:25:38 -07:00
Saoud Rizwan d73f1a44da fix(cli): resolve onboarding provider models (#17) 2026-05-06 14:14:24 -07:00
Bee 7fb949e5d6 refactor: centralize provider option patch handling (#14)
* refactor: centralize provider option patch handling

Consolidate thinking, reasoning, alias, and Moonshot Kimi provider option patch helpers to keep bucket generation consistent across providers. Add table-driven coverage with complete request fixtures to guard provider-specific routing behavior.

* apply feedback

* Qwen uses anthropic cachine

* sorted
2026-05-06 13:16:46 -07:00
Ara f936d53d25 fix(openrouter): enable cache control for Qwen models (#10578) 2026-05-06 12:58:51 -07:00
Ara 4c514d5e01 Fix CLI exit summary terminal cleanup (#6) 2026-05-06 09:22:38 -07:00
Saoud Rizwan d0a9533cfa chore(cli): release v0.0.11 2026-05-06 00:23:22 -07:00
Saoud Rizwan 7ff18ca2fb fix(cli): prevent tui log fallback leaks (#22) 2026-05-05 23:33:54 -07:00
Robin Newhouse af197a9e14 Force manual context compaction (#11) 2026-05-05 21:02:26 -07:00
Bee 89c8f3d29b fix: disable search from yolo mode (#13)
* fix: disable search from yolo mode

Disable search tool from yolo mode so it would use bash for search instead.

* update test

* fix tests
2026-05-06 03:09:29 +00:00
Bee 24f5138726 fix: disable strictJsonSchema by default (#10)
* fix: disable strictJsonSchema by default

Set strictJsonSchema: false helps when the provider itself is rejecting or over-constraining tool schemas because strict mode is on.

Before we migrate to AI SDK, the OpenAI SDK has strict set to false by default, so it uses the union schema we have set to reduce tool calls failture. However,
in AI SDK 6, OpenAI/openai-compatible default strictJsonSchema is true, and it is used for strict JSON schema / tool-call generation behavior. Turning it off can reduce provider-side failures around schemas that strict mode does not support well, especially unions, optional fields, nullable fields, anyOf, etc.

* tighter fix

* unit tests
2026-05-05 18:11:10 -07:00
Robin Newhouse adb803a8ad fix(cli): add skills picker slash command [CLINE-2052] (#8)
* fix(cli): add skills picker slash command

* fix(cli): address skills picker review feedback

* fix(cli): keep skills picker label focused

* fix(cli): keep skills command visible without installed skills

* fix(cli): always show skills install guidance

* fix(cli): reserve space for skills picker footer

* fix(cli): make skills marketplace selectable

* fix(core): leave workflow command descriptions blank

* fix(cli): address skills picker review feedback
2026-05-05 17:42:54 -07:00
Robin Newhouse 4ba11c3ce3 fix: wire submit_and_exit completion policy (#9) 2026-05-05 16:58:34 -07:00
Bee a6a932e279 fix: missing tool result on resumed session ENG-1954 (#7)
* fix: missing tool result on resumed session

The runtime emits the assistant message before tools execute, so an abort between those two steps can leave the persisted transcript ending with tool_use blocks. On resume, the provider request is built from that transcript, so this is a transcript repair problem at the provider-message boundary rather than a tool executor problem.

MessageBuilder.buildForApi now repairs provider-bound transcripts by inserting synthetic tool_result messages for assistant tool_use blocks that have no result.
The synthetic result uses text block array content and is_error: true, e.g. “Tool execution was interrupted before a result was produced.”
Inserted results are placed before the next user prompt/assistant turn, so provider message ordering stays valid.
Read-tool compaction now skips error tool results so synthetic interrupted read results do not get rewritten as outdated file content.

* patches
2026-05-05 16:26:03 -07:00
Bee 63e6d2e54b fix: keep interactive sessions reusable after abort (#5)
Handle rejected agent runs during interactive aborts as aborted turns instead of failing the session. Preserve messages, clear abort state, and allow later turns to continue.

Also export AgentTool from the core entrypoint.
2026-05-05 16:16:26 -07:00
Bee f14fc3a214 feat: pass AI SDK system prompts via system option (#2)
Resolve the system prompt once and send non-empty string prompts through
streamText's system field instead of embedding them in message history.
This avoids duplicate prompt handling while preserving cached message logic.

Removes DOC.md as content will be moved to doc site.
2026-05-05 13:51:59 -07:00
Tomás Barreiro 7c82891d43 Add license file (#1) 2026-05-05 21:29:07 +02:00
BarreiroT 0b0a3d2592 Update references to the new repo 2026-05-05 16:11:08 -03:00
Bee 57f2b32083 Cline SDK 0.0.38 (#382) 2026-05-05 11:30:56 -07:00
Bee 4158146813 chore: generated model list update (#380)
to version 1778003433064
2026-05-05 11:18:33 -07:00
Saoud Rizwan 733e0d33aa chore(cli): release v0.0.10 2026-05-04 23:13:15 -07:00
Saoud Rizwan 1c9efbbcd7 fix(cli): preserve wrapped user message padding in chat view
Same root cause as #371 -- the trailing space in the > marker text
only applied to the first visual line. Wrapped lines rendered flush
against the marker column. Use a fixed-width box for the > marker
(matching assistant_text and tool_call entries) so the gap is
structural rather than inline text.
2026-05-04 23:00:55 -07:00
Bee e1968e3dd1 Merge pull request #378 from cline/bee/prepare-turn 2026-05-04 22:43:21 -07:00
Robin Newhouse ffc01770e2 fix(cli): improve local provider onboarding and model selection [ENG-1927] (#338)
**Status: draft. Latest implementation is pushed through `aa14b75d`. The
onboarding flow has been manually verified with Ollama by selecting a
configured local model.**

## Motivation

Interactive local-provider setup had several gaps:

- Ollama / LM Studio setup could ask for an API key but not the endpoint
users actually need to configure.
- Keyless local providers were blocked or confusing in UI paths even
though the flag path could already save base URLs.
- Onboarding could not manually enter a model ID when the provider
returned no models.
- Onboarding model lookup did not consistently use the saved local
provider endpoint, so live Ollama models could be missing.
- Cloud providers were showing editable base URLs too broadly because
the UI inferred editability from the existence of a default base URL.

## Current architecture

### Provider config projection belongs to core

`@clinebot/core` now owns the UI-facing provider config projection
through `getProviderConfigFields(providerId)` in
`packages/core/src/services/providers/local-provider-service.ts`.

- OAuth providers return `{ authMethod: "oauth", fields: {} }` and route
to OAuth login.
- API-key providers return `apiKey`.
- Built-in editable base URLs are intentionally limited to
endpoint-style providers: `ollama`, `lmstudio`, and `litellm`.
- User-added/custom providers with saved endpoints still expose
`baseUrl`, so custom OpenAI-compatible providers remain editable.
- Fields are not marked runtime-required; provider/upstream errors
remain the source of truth.

### Settings-to-runtime conversion stays canonical

The CLI no longer has a custom `ProviderSettings -> ProviderConfig`
projection for model lookup.

- `toProviderConfig(settings, { includeKnownModels: false })` was added
in core.
- `ProviderSettingsManager.getProviderConfig(providerId, options)` now
forwards that option.
- Model lookup paths can keep saved auth/base URL/routing settings while
avoiding bundled `knownModels` that would pollute live local discovery.

### Local model discovery is live-first

Ollama / LM Studio use public keyless model fetchers in
`packages/core/src/services/llms/provider-defaults.ts`.

- Ollama fetches `${baseUrl without /v1}/api/tags`.
- LM Studio fetches `${baseUrl}/models`.
- Public fetchers run even when no config object is passed, falling back
to the provider default base URL.
- When a public fetcher returns models, those results override generated
catalog entries so local pickers show what is actually
installed/available.

### CLI UX updates

The CLI provider-change and onboarding flows now consume the core
projection.

- Bring-your-own-provider onboarding renders `baseUrl` only when core
says it should.
- Base URL is focused first for endpoint-style providers; cloud
providers see only API key.
- The model picker supports manual model ID entry even when models are
present.
- The onboarding model picker also supports manual model ID entry when
the fetched list is empty.
- Existing configured-provider detection treats meaningful saved
endpoint/model/API-key settings as configured for non-OAuth providers
and requires an OAuth access token for OAuth providers.

## Main files changed

| Path | Change |
|---|---|
| `packages/core/src/services/providers/local-provider-service.ts` |
Adds provider config field projection and base URL editability policy |
| `packages/core/src/services/providers/local-provider-service.test.ts`
| Covers cloud, local/proxy, OAuth, unknown, and custom-provider config
field behavior |
| `packages/core/src/services/llms/provider-defaults.ts` | Adds/uses
public Ollama and LM Studio model fetchers with live-first merge
behavior |
| `packages/core/src/services/llms/provider-settings.ts` | Adds
`includeKnownModels` option to canonical `toProviderConfig` |
| `packages/core/src/services/storage/provider-settings-manager.ts` |
Exposes provider config conversion options through the settings manager
|
| `apps/cli/src/tui/components/dialogs/provider-picker.tsx` | Renders
configure fields returned by core |
| `apps/cli/src/tui/components/model-selector/model-selector.tsx` |
Allows manual typed model IDs in the regular model picker |
| `apps/cli/src/tui/components/searchable-list.tsx` | Supports synthetic
searchable rows for typed custom values |
| `apps/cli/src/tui/hooks/use-model-selector.tsx` | Uses saved config
without pre-seeded known models for lookup refresh |
| `apps/cli/src/tui/views/onboarding/*` | Adds BYO base URL config and
manual model ID entry to onboarding |
| `apps/cli/src/utils/provider-auth.ts` | Keeps provider auth helpers
small; no custom provider-config projection |

## Verified

- Pre-commit hook on `aa14b75d` ran:
  - `bun run types`
- `bun biome check --no-errors-on-unmatched --files-ignore-unknown=true`
- Additional focused verification run locally:
- `bun -F @clinebot/core test:unit --
src/services/storage/provider-settings-manager.test.ts
src/services/providers/local-provider-service.test.ts`
  - `bun -F @clinebot/cli typecheck`
  - `bun -F @clinebot/cli test:unit`
  - `git diff --check`
- Manual verification: onboarding worked with Ollama and showed the
available local model.

## Notes / residual risk

- TUI keyboard/focus behavior is covered mostly through typecheck/unit
coverage and manual verification rather than deep e2e coverage.
- The base URL allowlist is intentional product policy. Additional
providers such as Requesty can be added later, but should be explicit
rather than inferred from a default endpoint.
- `includeKnownModels: false` is deliberately scoped to lookup flows;
normal runtime config still includes known model metadata for
cloud/catalog providers.

## Out of scope

- VS Code webview parity for `getProviderConfigFields`.
- Custom-provider creation UX.
- Richer retry/error UI when local servers are unreachable.
- Multi-field provider-specific setup forms, such as SAP AI Core.

## Screenshots
<img width="615" height="290" alt="Screenshot 2026-05-01 at 7 00 49 PM"
src="https://github.com/user-attachments/assets/2a5dc945-2204-4263-abb8-eee24fcc1a33"
/>
<img width="619" height="209" alt="Screenshot 2026-05-01 at 7 00 53 PM"
src="https://github.com/user-attachments/assets/eb5e0d38-3442-470f-8916-25d2f919876e"
/>
<img width="541" height="220" alt="Screenshot 2026-05-01 at 7 01 08 PM"
src="https://github.com/user-attachments/assets/1b828c35-c815-44f2-a199-4d0ac32d3861"
/>
<img width="544" height="248" alt="Screenshot 2026-05-01 at 7 01 18 PM"
src="https://github.com/user-attachments/assets/b42853f0-b45d-4901-ae85-31ea5886be40"
/>
2026-05-05 05:24:24 +00:00
Dominic Cooney 7512a4ca4e fix(sdk): Handle allOf clauses when normalizing tool input schemas (#379)
Fix normalizeToolInputSchema to handle allOf correctly: At least one
constraint needs to be `"type": "object"`, not *all* constraints.

## Details

Follow up to feedback on #364.

That PR fixed Windows tool input schemas by requiring the input to be an
object, however it interpreted allOf constraints too conservatively.
Previously, we would erroneously reject constraints like:

```json
{
  "allOf": [
    {
      "type": "object",
      "properties": {
        "commands": { "type": "array" }
      }
    },
    {
      "required": ["commands"]
    }
  ]
}
```

But this is spurious: if allOf(A, B, ...) and one of A, B, ... is
`"type": "object"` then the whole thing is `"type": "object"` and should
be allowed.

## Test Plan

```
bun -F @clinebot/shared test
```
2026-05-04 22:24:10 -07:00
abeatrix ae975d84d2 address feedback 2026-05-04 22:20:43 -07:00
Bee 274f7d4f73 fix: reconnect websockets and keep run.start alive CLINE-2095 (#372)
Reconnect NodeHubClient after idle websocket closes and re-subscribe
active listeners so hub events continue without manual recovery.

Keep browser run.start commands open past the default timeout because
runs can exceed 30 seconds. Also pin Node 22 and update CLI doctor fix
wording and lockfile metadata.
2026-05-04 22:09:59 -07:00
Bee cf0bae4dd9 feat: export Zod schema for global settings file (#373)
Define GlobalSettingsSchema as the strict source of truth for persisted
global settings. Use it to normalize reads and writes by trimming,
deduplicating, sorting, and omitting empty disabled tool/plugin lists.

Document the settings file location and schema, and add tests for
validation behavior.
2026-05-04 22:05:49 -07:00
Robin Newhouse c8c7d49db7 Add rule for worktree dependency hygiene (#350)
## Summary
- Add a Cline rule documenting dependency hygiene for git worktrees
- Prefer centralized dependency installs over per-worktree installs

## Testing
- Not run; documentation/rule-only change
2026-05-04 22:04:40 -07:00
Robin Newhouse a211551e84 fix: constrain skill toggle writes to resolved records (#335)
## Summary
- require skill toggles to resolve through the instruction watcher
before writing
- only write the watcher-resolved skill record path instead of
caller-provided paths
- add regression coverage for rejecting outside-workspace path toggles
without modifying the outside file

## Tests
- bun test packages/core/src/settings/settings-service.test.ts
packages/core/src/hub/settings.test.ts
- bun run types

Related: 
#297
2026-05-05 04:59:52 +00:00
Robin Newhouse 8c239ab7e4 Fix CLI context usage and resumed session cost display [CLINE-1839] (#339)
## What

Fixes CLINE-1839: the CLI status bar token count next to the model name
could show huge values such as 1.4M tokens after only a few Sonnet
turns.

This also fixes the related resume display issue for current saved
sessions with message metrics: opening a saved conversation from history
now hydrates the same context-size/cost state used during normal chat.

## Root Cause

The status bar was using accumulated usage as if it were current
context-window usage:

- `AgentRuntime` correctly accumulates usage across every LLM call in a
turn.
- Each LLM call sends the full conversation, so summing input tokens
across calls over-counts context size.
- The CLI then read `getAccumulatedUsage()` and displayed `inputTokens +
outputTokens` against the model context window, compounding that over
every turn.

That accumulated token total is useful for reporting resource usage, but
it is not the number of tokens currently occupying the model context
window.

Cost is different: cost is additive per LLM call and should remain
cumulative.

## Solution

### Current context size

Adds `getCurrentContextSize(messages)` in `@clinebot/core` and exports
it from the core package.

It reads the latest assistant message's `metrics.inputTokens`, which is
the normalized prompt size for the most recent LLM call. This is the
status-bar context-window quantity.

Important decision: do **not** add `cacheReadTokens` or
`cacheWriteTokens` on top. Provider usage is normalized so `inputTokens`
already includes cached portions. Adding cache fields would double-count
prompt-cache tokens, especially on Anthropic/Sonnet.

### CLI wiring

The CLI now carries `currentContextSize` through:

- normal completed turns
- aborted turns with partial assistant messages
- `cline history` / `--id` deferred hydration
- in-chat history picker resume
- initial `props.initialMessages` hydration

The status bar uses `currentContextSize` when available instead of
cumulative usage tokens. If a provider omits usage metrics and
`currentContextSize` is unavailable, the UI leaves the prior displayed
token count unchanged instead of falling back to accumulated usage.

### Resumed sessions

Current `main` already reconstructs accumulated usage from persisted
message metrics on resume via
`summarizeUsageFromMessages(initialMessages)`. This PR builds on that
fix-forward behavior and does not add compatibility shims for older
sessions with incomplete per-message metrics.

On resume, the CLI asks `getAccumulatedUsage()` for cumulative cost and
uses `getCurrentContextSize(messages)` for the status-bar context count.

## Decisions

- Keep context-window tokens and cumulative usage separate.
- Put the context-size helper in core, not CLI, so the usage semantics
are shared and testable.
- Keep cumulative cost in core as the source of truth; CLI does not
independently recalculate total cost for resumed sessions.
- Do not use cumulative token totals for the context bar. They remain
over-count-prone by nature because each agent iteration sends the full
conversation.
- Fix forward only: rely on current persisted message metrics rather
than adding metadata-cost fallback behavior for old/unreleased sessions.

## Tests

After simplifying to fix-forward behavior, ran and passed:

- `bun -F @clinebot/core typecheck`
- `bun -F @clinebot/cli typecheck`
- `bun --cwd packages/core test:unit src/services/usage.test.ts
src/runtime/host/local-runtime-host.test.ts
src/runtime/host/runtime-host-support.test.ts` — 3 files, 55 tests
passed
- `bun --cwd apps/cli test src/connectors/session-runtime.test.ts
src/commands/history.test.ts` — 2 files, 10 tests passed
- `bun biome check --diagnostic-level=error` on touched files
- Commit pre-hook root `bun run types`

Earlier before the rebase, the full package test suite also passed
locally (`bun run test`). CI was rerun after a transient install/setup
failure and was green before the latest force-push.
2026-05-04 20:11:07 -07:00
Bee 8fc56edabd fix: store session history on disk after each assistant response ENG-1911 (#375)
Persist session messages immediately upon receiving an assistant
response event. This ensures users can recover from session crashes or
abnormal exits without losing conversation progress.
2026-05-04 19:11:05 -07:00
abeatrix 5ae4ff1732 patches 2026-05-04 18:31:50 -07:00
Tomás Barreiro 91b3f654be Force the CLI to use device auth (#365) 2026-05-05 03:19:02 +02:00
abeatrix d81100c5b9 fix: wire auto-compaction into the provider request
Wire prepareTurn into AgentRuntime model requests

Add prepareTurn to the runtime config contract and invoke it before
beforeModel/model.stream so host-owned context pipelines can rewrite the
transcript on the hot path to the provider.

When prepareTurn returns messages, replace the runtime transcript so
compacted history is persisted in the final run result. Core now adapts
its existing compaction callback into this runtime hook and passes
API-safe messages into compaction.

Also add a status-notice runtime event for auto-compaction notices and
regression coverage for runtime compaction persistence and core wiring.
2026-05-04 18:15:46 -07:00
Dominic Cooney 770e4f0637 fix(sdk): Windows shell tool schema should take a top-level input object (#364)
`createWindowsShellTool` claimed its input schema was a union including
various primitives, but Anthropic and other APIs are strict about this
being an object. Pass the right schema type, and if we ever set up a
tool with a union schema type, fail noisily.

## Test

In addition to the tests we added:

```
bun install
bun build:sdk
bun run cli -- --provider cline --model anthropic/claude-sonnet-4.6 "are you up?"
bun run cli -- --provider cline --model anthropic/claude-opus-4.7 "are you up?"
```

These should respond and NOT spew errors like:

(Sonnet 4.6)

```
error: Failed to create stream: inference request failed: failed to generate stream from Vercel: failed to invoke model 'anthropic/claude-sonnet-4.6' with streaming: request failed with status 400: {"error":{"message":"tools.3.custom.input_schema: input_schema does not support oneOf, allOf, or anyOf at the top level","type":"AI_APICallError","param":{"error":"tools.3.custom.input_schema: input_schema does not support oneOf, allOf, or anyOf at the top level","statusCode":400,"name":"AI_APICallError","message":"tools.3.custom.input_schema: input_schema does not support oneOf, ...
```

(Opus 4.7)

```
error: Failed to create stream: inference request failed: failed to invoke model 'anthropic/claude-opus-4.7' with streaming from OpenRouter: request failed with status 400: {"error":{"message":"Provider returned error","code":400,"metadata":{"raw":"{\"type\":\"error\",\"error\":{\"type\":\"invalid_request_error\",\"message\":\"tools.3.custom.input_schema: input_schema does not support oneOf, allOf, or anyOf at the top level\"},\"request_id\":\"req_...
```
2026-05-04 17:53:12 -07:00
Dominic Cooney b10cfac810 fix(cli): Hide console windows of stdio MCP servers on Windows (#363)
When we spawn MCP stdio servers, they create console windows. This
option suppresses the window creation.

## Test Plan

First, set up a couple of stdio MCP servers, then:

```
bun install
bun build:sdk
bun run cli hub stop
bun run cli
^C
bun run cli
(ask the model to use your MCP server)
```

You may see a console window flash during the first invocation, but the
window disappears; in subsequent invocations you see no console windows.
2026-05-04 17:53:00 -07:00
Saoud Rizwan 0aebfcdc18 fix(cli): align Ctrl+C and Escape keyboard behavior (#377)
## Summary

This PR fixes two related interactive TUI keyboard issues in `apps/cli`:

- `Ctrl+C` could cancel an active run, which conflicted with expected
terminal semantics for this CLI flow.
- Empty-looking input could still require an extra key press due to
stale input state checks.

The new behavior is:

- `Ctrl+C` only does clear-or-exit behavior.
  - If the input has non-whitespace content, it clears the field.
  - If the input is empty, it exits the CLI.
- `Escape` is the key that aborts an active run.
- Whitespace-only input is treated as empty for clear-or-exit decisions.

## Problem

There were two UX consistency issues in chat view:

1. While a run was active, `Ctrl+C` could trigger runtime abort instead
of just interacting with local input/exit flow.
2. Input emptiness checks relied on state that can lag behind the live
textarea contents, so users sometimes needed an extra `Ctrl+C` even when
the field appeared empty.

This made keyboard behavior feel unpredictable and mixed the
responsibilities of `Ctrl+C` and `Escape`.

## Technical approach

I changed key handling in the root keyboard hook and kept the rest of
the runtime stack intact.

- Added a live input accessor in `usePromptInputController`:
  - `getCurrentInputText(): string`
  - returns `textareaRef.current?.plainText ?? inputValueRef.current`
- Wired that accessor through `root.tsx` into `useRootKeyboard`.
- Updated `useRootKeyboard` logic:
  - derive `hasInputText` from `getCurrentInputText().trim().length > 0`
  - `Ctrl+C` now only clears input when `hasInputText`, otherwise exits
  - removed the `Ctrl+C` path that called `onAbort()` during active runs
  - `Ctrl+D` emptiness check now uses the same trimmed live input signal
- Updated help dialog copy so shortcuts match actual behavior.

## Why this design

I intentionally kept abort behavior on `Escape` and removed it from
`Ctrl+C` rather than introducing more branching based on run state. This
keeps key semantics stable:

- `Escape`: run cancellation intent
- `Ctrl+C`: local field/exit intent

Using the live textarea text also avoids timing windows from deferred
sync (`queueMicrotask`) and prevents whitespace-only input from being
treated as meaningful content.

## Files changed

- `apps/cli/src/tui/hooks/use-prompt-input-controller.ts`
- `apps/cli/src/tui/root.tsx`
- `apps/cli/src/tui/hooks/use-root-keyboard.ts`
- `apps/cli/src/tui/components/dialogs/help-dialog.tsx`

## Validation

Executed:

```sh
bun -F @clinebot/cli typecheck
bun -F @clinebot/cli test:unit
```

Results:

- typecheck passed
- unit tests passed (`67` files, `386` tests)

## Gotchas and notes

- The keyboard hook has early returns for dialog/onboarding modes; this
change preserves those guards.
- The patch deliberately does not alter run lifecycle or abort plumbing
in runtime services, only key routing decisions in TUI input handling.
- Help text was updated in the same PR to avoid behavior/documentation
drift.
2026-05-04 17:51:13 -07:00
Saoud Rizwan 7f95364557 fix(cli): preserve thinking level across clite restarts (#376)
Fixes https://github.com/cline/cline/issues/10507

In `clite`, thinking level selected in interactive model picker was
being persisted, but restarting `clite` without `--thinking` reset
runtime config to thinking off.

Repro:
1. Select a reasoning-capable model and choose a thinking level in
interactive mode
2. Exit with `/exit`
3. Restart `clite`
4. Thinking level is back to default/off

## Root Cause
`apps/cli/src/main.ts` computed startup reasoning with:

```ts
const effectiveReasoningEffort = args.reasoningEffort ?? "none";
```

That meant whenever `--thinking` was omitted, startup always forced
`none`, ignoring persisted provider settings at
`selectedProviderSettings.reasoning`.

Interactive flow was already persisting reasoning correctly in
`run-interactive.ts` during model changes, so this was specifically a
load-precedence bug at startup.

## Technical Approach
Updated startup reasoning resolution to use this precedence:
1. CLI flag (`--thinking`) when provided
2. Persisted provider reasoning settings
3. Fallback `none`

Implementation details:
- Read `selectedProviderSettings?.reasoning`
- Map persisted values to runtime effort:
  - `enabled: false` -> `none`
  - persisted `effort` (not `none`) -> that effort
  - `enabled: true` with missing effort -> `medium`
  - otherwise -> `none`

This preserves prior behavior for explicit flags while making persisted
interactive choices survive restart.

## Tests
Added targeted unit tests in `apps/cli/src/main.test.ts`:
- Uses persisted reasoning effort when `--thinking` is not provided
- Explicit `--thinking` overrides persisted reasoning effort

Ran:
- `bun run test:unit src/main.test.ts` (from `apps/cli`) -> passing

## Notes and tradeoffs
- The `enabled: true` + missing effort fallback to `medium` is
intentional to keep behavior stable for partially populated persisted
records.
- No changes to persistence format were needed; this only fixes startup
loading semantics.
2026-05-04 17:44:55 -07:00
Bee d62d606c4c fix(packages): normalize repository URLs to HTTPS format (#374)
Update package repository metadata across workspace packages by removing
the git+ prefix and .git suffix so URLs use standard HTTPS links.
2026-05-04 16:39:29 -07:00
Saoud Rizwan 8b7750f6e0 fix(cli): preserve wrapped chat input padding in chat field (#371)
## Problem
In the CLI chat input, once the first line wrapped, continuation text
could render immediately adjacent to the prompt marker `>` instead of
keeping the expected one-column gap.

## Technical approach
The input row previously relied on `gap={1}` between the prompt marker
and the textarea. That spacing only applied between sibling elements,
not to wrapped visual lines inside the textarea itself.

I moved the one-column spacer into the textarea column:
- removed row-level `gap={1}`
- wrapped the `<textarea>` in a `<box flexGrow={1} paddingLeft={1}>`

This keeps the first line and all wrapped lines aligned with the same
inset relative to `>`.

## Notes from debugging
The bug was layout-level rather than text wrapping logic. No textarea
wrapping mode changes were needed.

## Testing
- `cd apps/cli && bun run typecheck`

I also tried a direct test invocation with `bun test
src/tui/index.test.ts`, but that command path does not match this
package's Vitest setup and failed early with `vi.hoisted is not a
function`.
2026-05-04 16:08:32 -07:00
Bee fcc1b2d07f feat: new examples for hooks, cron, and plugins (#347) 2026-05-04 22:32:07 +00:00
alex-lum 855296d96b Add startup activation and workspace lifecycle telemetry (#357)
## Summary

Adds the legacy-compatible startup activation + workspace lifecycle
telemetry funnel to the SDK and wires it through the hub runtime daemon,
the CLI, and the VS Code extension. Re-opens the work from #348 (closed
without merge) on top of the latest `main`, with the follow-up fixes
(opt-out routing, hub re-exports, smoke harness hardening,
`submit_and_exit` anchoring) folded in.

Refs ENG-1902.

## Product behavior

**No user-facing behavior changes.** This PR is observability-only: it
emits new telemetry events and adds a `source` field to an existing
event. Tool execution, prompts, runtime semantics, persisted state
shape, public APIs, and CLI/VS Code UX are all unchanged. The opt-out
toggle continues to suppress every event introduced here (decision #2
below was specifically to preserve that). The only externally visible
change is that telemetry-enabled hosts now report the same activation
funnel the cline VS Code extension already does.

## Why

The cline VS Code extension already emits a tightly-coupled funnel —
`user.extension_activated → workspace.initialized →
workspace.path_resolved → task.created → conversation_turn →
task.completed` — that the warehouse and downstream analytics depend on.
The SDK had none of those events, so any host built on `@clinebot/core`
(CLI, VS Code SDK build, hub-backed sessions) silently dropped the
funnel. This PR introduces those events as first-class core helpers,
wires hosts to emit them, and adds smoke + unit coverage so the contract
holds going forward.

## Event catalog (new in `@clinebot/core`)

- `user.extension_activated` — emitted once per host process
- `workspace.initialized` / `workspace.init_error` — emitted from
`prepareLocalRuntimeBootstrap`
- `workspace.path_resolved` — gated, emitted from default tool executors
only when a `WorkspaceManager` exposes >1 root
- `task.completed` (existing) — now carries `source: "submit_and_exit" |
"shutdown"` so completion is attributable to an assistant declaration
vs. a process-shutdown fallback

Property shapes are snake_case to match the warehouse schema
(`root_count`, `vcs_types`, `has_git`, `is_multi_root`,
`init_duration_ms`, `is_remote_workspace`, etc.). A new
`TelemetryMetadata.is_remote_workspace` field in `@clinebot/shared`
carries the remote-workspace signal end-to-end.

## Key design decisions

### 1. Anchor `task.completed` on `submit_and_exit`, not process
shutdown
Original cline ties completion to the assistant explicitly invoking
`attempt_completion`. The SDK's previous behavior fired `task.completed`
on shutdown, which conflated successful completion with terminated
processes and broke funnel attribution. We now track
`submitAndExitObserved` on `ActiveSession`, set it when the
`submit_and_exit` tool fires, and consume it once in the runtime to
attribute completion. If shutdown happens without `submit_and_exit`, we
still emit `task.completed` but with `source: "shutdown"` so analytics
can distinguish the two paths.

### 2. Route activation/workspace events through `capture`, **not**
`captureRequired`
The first revision of this funnel used a `captureRequired` helper that
bypasses the user's telemetry opt-out toggle. On `main` only
`telemetry.provider_created` (a single internal heartbeat) is allowed to
bypass opt-out — broadening that policy to four new event families would
have shipped data for users who explicitly disabled telemetry. Removed
the `emitRequired` helper and routed the four helpers through
`telemetry.capture`. Locked it in with `core-events.test.ts`: a real
`TelemetryService` with a disabled adapter asserts the four event names
are dropped end-to-end, plus
`expect(captureRequired).not.toHaveBeenCalled()` per helper.

### 3. De-duplicate emission at the bootstrap layer, not per host
`workspace.initialized` / `workspace.init_error` are emitted by a
per-process de-duplicated emitter in `prepareLocalRuntimeBootstrap`
rather than by every host. That way CLI, VS Code, and hub-backed
sessions all get the events without each host having to re-implement the
dedup + payload shaping. Added `generateWorkspaceInfoWithDiagnostics` to
capture init duration / VCS types / first error while preserving the
existing non-throwing `generateWorkspaceInfo` signature for older
callers.

### 4. Gate `workspace.path_resolved` on multi-root only
Path-resolution telemetry only carries useful signal when the workspace
has multiple roots. The wrapper in `extensions/tools/path-telemetry.ts`
is inert in single-root setups (the current default in
`InMemoryWorkspaceManager`), which avoids spamming the funnel for the
common case. We thread `workspaceManager` through `RuntimeBuilderInput`
so the runtime builder can drive this without widening the
`@clinebot/agents` or `@clinebot/shared` contracts.

### 5. Forward telemetry through the detached hub daemon
The VS Code extension spawns the hub daemon as a separate process. To
make sure workspace lifecycle telemetry from hub-backed sessions reaches
the same OpenTelemetry pipeline as host-emitted events, hosts now
serialize telemetry metadata into the daemon argv (base64-encoded
snake_case payload: `extension_version`, `cline_type`, `platform`,
`platform_version`, `os_type`, `os_version`, `is_remote_workspace`). The
daemon decodes that, builds a configured `ITelemetryService`, and
threads it through the hub WebSocket server, schedule runtime handlers,
and `LocalRuntimeHost`. Best-effort flush + dispose on
`SIGINT`/`SIGTERM`.

### 6. Single shared `ITelemetryService` per host
On VS Code we now build the telemetry handle once in `activate()`
(`apps/vscode/src/telemetry.ts`) and pass the same instance into the
sidebar, panel command, and daemon spawn payload — instead of letting
each controller construct its own. That keeps the distinct-id, opt-out
state, and flush ownership in one place.

### 7. CLI: emit `extension_activated` *after* `setClineDir` /
`setHomeDir`
The CLI accepts `--config <dir>`. If we emit activation telemetry before
applying that override, the persisted distinct-id and other
telemetry-on-disk state lands under `~/.cline` instead of the user's
chosen config dir. Memoized `captureCliExtensionActivated()` is invoked
once in `main.ts` after the dir overrides are applied; tests cover
memoization, identify-before-capture ordering, and the no-account fast
path.

### 8. Hub helpers re-exported from `@clinebot/core/hub`
Per `AGENTS.md`, the detached hub daemon is a hub concern. Rather than
letting `hub-daemon.ts` reach into `@clinebot/core` and
`@clinebot/shared` directly, `createConfiguredTelemetryService` and
`ITelemetryService` are re-exported from
`packages/core/src/hub/index.ts` so the daemon entry point only consumes
the hub surface.

### 9. Typecheck the smoke harnesses
`tsconfig.dev.json` excluded `scripts/`, so the telemetry smoke harness
was never typechecked — that's how a missing `ToolContext` export and
broken hub-daemon imports slipped past `bun run check`. Added
`tsconfig.smoke.json` (includes `src/` + `scripts/`) wired into the
package's `typecheck` script as `typecheck:smoke`, so any helper
imported by the smoke harness has to compile in the same project as
core. While there, switched `path-telemetry.ts` to import
`AgentToolContext` from `@clinebot/shared` (the actual exported name)
instead of the non-existent `ToolContext`.

### 10. Smoke harness fails CI on contract regressions
Both `telemetry-smoke.ts` and `telemetry-smoke-host.ts` previously
logged warnings on count/order mismatches but exited 0. They now use a
shared `assertSmoke()` helper that sets `process.exitCode = 1` on every
block, mirroring the existing path-leak guard's policy. New blocks cover
`task.created`, `conversation_turn` (user + assistant), both
`task.completed` source variants, and a full lifecycle-ordering check.

## Verification

- `bun run types`
- `bun run test`
- New unit coverage:
  - `apps/cli/src/utils/telemetry.activation.test.ts`
- `packages/core/src/runtime/host/local-runtime-host.test.ts`
(completion-source contract)
  - `packages/core/src/services/local-runtime-bootstrap.startup.test.ts`
- `packages/core/src/services/telemetry/core-events.test.ts` (opt-out
routing + drop-on-disabled)
  - `packages/core/src/services/workspace/workspace-telemetry.test.ts`
- Smoke harnesses (`packages/core/scripts/telemetry-smoke{,-host}.ts`)
now exit non-zero on funnel regressions

## Related

- Supersedes #348 (closed without merge)
- ENG-1902

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-04 15:17:13 -07:00
Bee 0b1dc114c9 fix: make workflow config path assertions order-agnostic (#368)
Update the workflow search path test to verify expected entries without
relying on array order, while still asserting the total path count. This
prevents brittle failures if path ordering changes.
2026-05-04 15:10:05 -07:00
Saoud Rizwan 3dc1be57a2 feat(cli): make home robot follow chat cursor while typing (#370)
## Summary
This change updates the CLI home view robot tracking behavior so it
follows the chat input caret after the user starts typing, instead of
only following mouse movement.

## Problem
On the home screen, the robot tracked `onMouseMove` coordinates only.
Once users start typing in the chat field, the visual focus shifts to
text editing, but the robot keeps reacting to the mouse position. That
makes the interaction feel disconnected from what the user is actively
doing.

## Technical approach
I reused the existing tracking pipeline and added a caret position
signal from the input component:

- Added an optional `onVisualCursorChange` callback to `InputBar`.
- Emitted cursor updates from the textarea by reading
`inputRef.current?.visualCursor`.
- Wired the callback in `HomeView` and kept local state for `{
visualCol, visualRow }`.
- Computed robot target coordinates conditionally:
  - if input is empty: use existing mouse tracker coordinates
- if input has content: map the textarea visual cursor to terminal
coordinates and use those
- Kept `TrackedRobot` and `RobotAnimation` unchanged so animation
behavior remains stable.

## Debugging and gotchas
A couple of details mattered:

- Cursor updates must happen after key handling/content updates, so I
emit cursor changes in microtasks around content/key events to avoid
stale cursor positions.
- The initial commit attempt failed due formatter checks in the
pre-commit hook (`biome check`). I ran Biome formatting on the edited
files, then recommitted successfully.

## Alternatives considered
I considered directly querying global terminal cursor state from the
renderer, but that would couple robot behavior to lower-level rendering
internals. The callback approach keeps ownership clear: `InputBar` owns
cursor data, `HomeView` owns presentation logic.

## How to test
1. Start the CLI and land on the home view.
2. Move the mouse around the home view. The robot should follow the
mouse, as before.
3. Start typing in the chat field.
4. While typing and moving the caret (left/right, multiline), confirm
the robot tracks the text cursor location.
5. Clear input entirely and verify robot behavior returns to mouse
tracking.

## Validation
- `bun run --cwd apps/cli typecheck`
- Pre-commit checks passed (`bun run types`, `biome check`, gitleaks)
2026-05-04 15:07:58 -07:00
Saoud Rizwan 857e6bab3f fix(cli): render context bar progress visibly (#369)
## Problem
In `apps/cli`, the context window usage bar under the chat input looked
empty even while token usage was increasing. Users could see token
counts change, but the bar gave no visible feedback.

## Root cause
Two issues combined into the broken behavior:

1. Filled and empty segments were effectively not visually distinct in
practice.
2. The filled segment color path used terminal-derived foreground that
can be `undefined` on dark themes, and this lived inside a parent `<text
fg="gray">` container. In that case the filled span inherited gray, so
filled and empty looked identical.

A second UX issue also existed in the segment math: near-limit usage
could show all segments filled too early when using round-based
quantization.

## Technical approach
The status bar rendering in `apps/cli/src/tui/components/status-bar.tsx`
was updated to make the bar deterministic and visible across terminal
themes.

1. `createContextBar` now:
- normalizes width safely
- rounds usage upward for early visibility (`ceil`) so small nonzero
usage shows progress
- reserves the final segment until `used >= total` so the bar only
becomes fully filled at or above limit

2. Filled segment color now resolves via
`resolveContextBarFilledForeground`:
- uses terminal-aware foreground when available
- falls back to explicit `#ffffff` when terminal foreground is
unresolved, preventing gray inheritance inside the parent gray text node

3. Context text rendering is built with explicit spans for filled vs
empty segments:
- filled span uses resolved foreground
- empty span remains gray

## Why this solves the issue
This removes theme-dependent ambiguity. Even when terminal foreground
cannot be inferred, filled blocks now render with an explicit
contrasting color. Combined with revised quantization, the bar provides
immediate nonzero feedback without appearing fully saturated before the
limit.

## Verification and debugging notes
I reproduced the logic path from the status bar component and validated
behavior with focused unit tests.

Added tests in `apps/cli/src/tui/components/status-bar.test.ts` for:
- zero, partial, and full-width segment generation
- nonzero tiny usage (`7,000 / 1,000,000`) showing one filled segment
- near-limit usage (`999,999 / 1,000,000`) leaving one segment empty
- foreground fallback behavior when terminal foreground is undefined

Commands run:
- `bunx vitest run --config vitest.config.ts
src/tui/components/status-bar.test.ts`
- `bun run typecheck`

Both passed.

## Alternatives considered
1. Using different glyphs for empty segments (`░` vs `█`).
- Rejected as the final direction because color-based distinction better
matches the requested UX and keeps bar geometry consistent.

2. Keeping round-based quantization.
- Rejected because it can visually saturate the bar before the actual
limit.

## Risk and compatibility
Risk is low and localized to the CLI TUI status bar rendering path. No
runtime/session accounting behavior changed. The change is presentation
logic plus focused tests.

## How to test manually
1. Start CLI interactive chat with a model that has a large context
window.
2. Submit a prompt that produces nonzero usage.
3. Confirm the context bar shows at least one filled segment once usage
is nonzero.
4. Confirm the bar is not fully filled until usage reaches or exceeds
the context window.
2026-05-04 14:51:57 -07:00
Bee b4efd49c0d fix(ci): publish SDK package publicly with Bun auth checks (#367)
Pin Bun for deterministic publish behavior, changed the workflow to keep
Bun for packing/workspace resolution, then hand the Bun-created tarball
to npm publish so OIDC is used for the actual registry publish.

bun pm pack creates the tarball, preserving Bun’s workspace version
rewriting.
npm publish <tarball> publishes it, so npm CLI can use GitHub OIDC
trusted publishing.
2026-05-04 14:11:11 -07:00
Bee dfed3fc61f fix: remove legacy data dir from workflow search (#366)
Update workflow config resolution to search only workspace rules and the
Documents/Cline/Workflows directory. Adjust tests to ensure the
deprecated Cline data workflows path is excluded.
2026-05-04 12:57:11 -07:00
Mikołaj Kondratek 90c8112257 At-mention picker: show "Searching..." instead of misleading "No results found" (#10478)
* At-mention picker: show "Searching..." instead of misleading "No results found"

When the @-mention picker fires its initial empty-query searchFiles, slow
workspaces (e.g. network mounts) leave the call in flight for several
seconds. Three small UX bugs combined to make this look broken:

1. The 500ms delayed-loading effect was gated on `searchQuery` being
   non-empty, so the spinner never appeared during the initial open —
   the user just saw "No results found" forever.
2. While loading, the spinner row stacked above the "No results found"
   row, claiming both states at once.
3. The spinner also stacked above the static root-menu items
   ("Paste URL", "Problems", "Git Commits", "Add File", "Add Folder")
   when the picker first opens with empty input, even though those
   items are already actionable.

Fixes:
- Drop the `&& searchQuery` guard so the loading effect arms on empty
  queries too.
- In `filteredOptions`, strip the lone `NoResults` entry while
  `showDelayedLoading` is true — searching is not the same as nothing
  matched.
- Render the spinner only when `filteredOptions.length === 0`, so it
  never stacks above existing options.

The 500ms delay before the spinner appears is preserved, so fast
searches stay visually quiet.

* fixes

* Drop stale @-mention searchFiles responses to fix "No results" flash

* Track in-flight searches with a monotonic latestSearchTokenRef in
  ChatTextArea; resolve/error handlers bail when their captured token
  is no longer the latest.
* Send the token as mentionsRequestId; proto already supports it.
* Drop the never-read currentSearchQueryRef scaffold.
* Fixes the cancel-then-re-pick race (Add File → cancel → Add Folder)
  reported in CLINE-1814.
2026-05-04 15:46:28 +02:00
Saoud Rizwan f722d8bf37 chore(cli): release v0.0.9 2026-05-03 18:01:28 -07:00
Saoud Rizwan a62e3d1a25 fix(core): recover local hub websocket clients after daemon restart (#362)
When a newer CLI version is launched, startup update logic can restart
the shared local hub daemon so the hub runs the latest build. During
that restart, the hub websocket endpoint can change (port and discovery
record update).

Any already-running CLI process still points at the old websocket
endpoint. Its next command can fail with a transport error because the
old hub connection is gone.

## Root cause

Running clients were not re-resolving hub discovery after transport
breakage, so they stayed pinned to stale hub connection details.

## Why this fix works

On reconnectable local transport failure, the client re-resolves the
current compatible local hub endpoint from discovery, switches to it,
reconnects, and retries the command once.

That directly handles the stale-endpoint failure after hub restart.

## How it works

1. `NodeHubClient` now emits typed transport errors:
   - `HubTransportError`
   - `HubTransportErrorCode`
   - `isHubReconnectableTransportError`

2. `NodeHubClient.command()` performs a guarded one-time retry for
reconnectable transport failures.

3. During recovery, the client:
- resolves the current compatible local hub endpoint from discovery
(`ensureCompatibleLocalHubUrl`)
   - updates internal current URL
   - closes stale socket state
   - reconnects on the next command path and retries once

4. Recovery is gated by `allowLocalHubRediscovery` so pinned endpoints
are not silently redirected:
   - enabled for local hub flows
   - disabled for explicit or remote endpoint flows

5. Recovery is not attempted for `client.register` and
`client.unregister` to avoid registration-time recursion.

## Scope and behavior notes

- Recovery is lazy and occurs on the next command after disconnect.
- Retry is at-most-once per command call.
- In edge cases where the hub executed a command but the reply was lost
during disconnect, retry can re-issue that command.
2026-05-03 17:51:03 -07:00
Saoud Rizwan ab8753dc4e fix(cli): harden TUI stdio capture against recursion and OSC sequences (#361)
## Problem

PR #360 added a stdio capture layer that intercepts
`process.stdout.write`/`process.stderr.write` during TUI rendering and
routes them through `console.log`/`console.error` so OpenTUI's console
overlay picks them up. Two edge cases were identified during review:

1. If OpenTUI's `console.log` implementation (or any future code path)
internally calls `process.stdout.write`, the captured write calls
`console.log`, which calls `process.stdout.write`, which calls
`console.log` -- infinite recursion, stack overflow. This is especially
dangerous because it depends on OpenTUI's internal implementation
details, and a change on their side could silently introduce the
recursion.

2. The ANSI stripping regex only covered CSI (`\e[...`) and Fe (`\e` +
single char) sequences. OSC sequences (`\e]...ST`) like OSC52 clipboard
writes were not stripped. The codebase already uses OSC52 for clipboard
in the renderer, so any OSC sequence hitting stdout during capture would
leak raw escape content into the console overlay.

## Approach

Re-entrancy guard: each `createCapturedWrite` instance gets an
`emitting` boolean. When `emitLine` is about to call
`console.log`/`console.error`, it sets the flag. If the console method
triggers a re-entrant `process.stdout.write`, the captured write sees
`emitting === true` and returns early, breaking the cycle. The flag is
reset in a `finally` block so it's always cleared even if the console
call throws.

ANSI regex: added an OSC branch `\].*?(?:\e\\|\x07)` that matches OSC
sequences terminated by either ST (`ESC \`) or BEL (`\x07`). The key
subtlety was alternation order: `]` (ASCII 93) falls in the Fe catch-all
range `\` through `_` (92-95), so the Fe branch was consuming the
opening `]` as a single-character escape before the OSC branch could
match. Reordered to CSI first, then OSC, then Fe last as the catch-all.

## Testing

Two new test cases in `stdio-capture.test.ts`:

- "strips OSC sequences from captured output": writes an OSC52 sequence
(ST-terminated) and a window title sequence (BEL-terminated), asserts
only the non-escape text reaches `console.log`
- "does not recurse when console methods trigger stdout writes": mocks
`console.log` to re-enter `process.stdout.write`, asserts `console.log`
is called exactly once

```
bunx vitest run --config vitest.config.ts src/tui/stdio-capture.test.ts src/tui/index.test.ts
bun run typecheck
```
2026-05-03 14:58:17 -07:00
Saoud Rizwan d43fb2e3a0 fix(cli): capture stdio during TUI rendering (#360)
## Problem

The interactive CLI runs inside an OpenTUI renderer, but background CLI
work can still call process.stdout.write or process.stderr.write
directly. One visible example was the startup auto-update path printing
hub restart status while the TUI was active. Those writes bypass
OpenTUI's console capture and can appear as stray text on top of the
rendered interface.

OpenTUI already captures console.log, console.warn, and console.error
into its console overlay, but the default full-screen alternate-screen
renderer does not capture raw process stream writes. OpenTUI has a
built-in capture-stdout path, but it is tied to split-footer mode and is
not compatible with the current full-screen TUI layout.

## Approach

This PR adds a small stdio capture layer at the TUI boundary. After the
OpenTUI renderer is created, the CLI temporarily replaces
process.stdout.write and process.stderr.write for the lifetime of the
TUI. Captured stdout lines are routed through console.log, and captured
stderr lines through console.error, so they use OpenTUI's existing
console capture path instead of writing directly into the terminal.

The renderer is created before installing the capture. That matters
because OpenTUI stores its real stdout writer during renderer
construction, so renderer frames can continue writing to the terminal
normally while later application-level stream writes are intercepted.

The capture restores the original stream writers when the renderer emits
destroy. It also restores them if root creation or initial rendering
throws, so failures do not leave the process with patched streams.

A few implementation details:

- Captured text is buffered by line so partial writes do not create
fragmented console entries.
- Pending partial lines are flushed during restore.
- ANSI escape sequences are stripped before forwarding so status
messages are readable in the OpenTUI console cache.
- The capture uses the regular console methods instead of its own UI
surface, keeping this scoped to the renderer boundary.

## Debugging Notes

The initial symptom looked like an OpenTUI toast because text appeared
inside the TUI frame. Tracing showed the source was not the React toast
component and not console.log. The hub restart message came from the
auto-update startup path, which eventually called writeln. The CLI
output helper writes through process.stdout.write, which is why
OpenTUI's console capture did not catch it.

The broader issue is not specific to hub restarts. Any background code
path that writes directly to stdout or stderr during an interactive
session can corrupt the visible TUI. That is why this PR captures stdio
during the whole renderer lifetime instead of special-casing the
updater.

## Testing

- bun run typecheck
- bunx vitest run --config vitest.config.ts
src/tui/stdio-capture.test.ts src/tui/index.test.ts

The pre-commit hook also ran gitleaks, bun run types, and biome check
for the staged files.
2026-05-03 12:53:00 -07:00
Saoud Rizwan 76e323fabb chore(cli): release v0.0.8 2026-05-03 12:13:06 -07:00
Saoud Rizwan e3d7002a60 fix(cli): prevent crash when pressing Escape to cancel a running turn (#359)
## Problem

Pressing Escape to cancel an in-progress LLM stream crashes the CLI with
`error: script "dev" exited with code 1`. The process exits immediately
and the TUI is torn down.

## Root cause

When the user presses Escape, `AbortController.abort()` fires in the
agent runtime to cancel the active stream. The main run promise handles
this correctly and returns `finishReason: "aborted"`. However,
`AbortController.abort()` also synchronously triggers rejections on
internal promises deep in the AI SDK's streaming pipeline -- lazy
`DelayedPromise` getters for usage/finishReason/steps, plus an orphan
from the fetch body's `ReadableStream` internal `pipeTo()` promise.
These rejections have no `.catch()` handler, so they surface as
`unhandledRejection` events. The CLI's `unhandledRejection` handler in
`index.ts` treats these as fatal and calls `process.exit(1)`.

The orphan rejection is fundamentally unreachable from application code
-- it lives inside the Streams API plumbing between the fetch response
body and the AI SDK's transform stream. No amount of `.catch()` on the
AI SDK's exposed promise getters prevents it (we verified this by
instrumenting `suppressDanglingStreamPromises` to cover all 22 prototype
getters). The rejection fires ~30-50ms after the run has already
completed, from a promise that can't be accessed or caught from outside
the SDK.

## Fix

Two coordinated changes:

### 1. Listener swap during abort (`active-runtime.ts`)

`markAbortInProgress()` temporarily replaces all `unhandledRejection`
listeners (including OpenTUI's error overlay handler) with a single
suppressing handler that silently catches the expected orphan
rejections. `clearAbortInProgress()` restores the original listeners
after a 2-second grace window once the turn finishes. This is the only
mechanism that prevents the rejection from reaching both the CLI's fatal
handler and OpenTUI's error popup -- calling `promise.catch()` in a
single handler does not prevent other registered handlers from also
firing.

### 2. Abort-aware error handling in onSubmit (`run-interactive.ts`)

When an abort races with hub capability/session teardown, errors like
"Capability owner client disconnected before request was resolved" can
surface through the normal `onSubmit` catch path. These were previously
masked by the immediate crash. Now, if `isAbortInProgress()` is true
when the catch block runs, the error is treated as a successful abort
result (`finishReason: "aborted"`) rather than being re-thrown and
displayed as a chat error row.

## Debugging journey

Initial hypothesis was that the `execute()` catch block in the agent
runtime wasn't handling the abort correctly, but tracing showed it
returns `status: "aborted"` properly every time. Added file-based
logging (`/tmp/cline-abort-debug.log`) across the CLI process and the
hub daemon process (they're separate processes with separate cwds, which
required absolute paths and killing/restarting the hub daemon to pick up
instrumented code since `node_modules` symlinks to the main workspace).

Key discoveries from the trace logs:
- The orphan rejection fires 30-50ms after `execute()` has already
returned, ruling out any in-band error handling
- `suppressDanglingStreamPromises` successfully catches all 22 prototype
getter promises on the `StreamTextResult`, but the orphan comes from
somewhere else entirely (likely an internal `ReadableStream` `pipeTo()`
promise)
- `promise.catch(() => {})` in our `unhandledRejection` handler does NOT
prevent OpenTUI's handler from also seeing the event -- all registered
listeners fire regardless
- The "Capability owner client disconnected" error was always present
but masked by the immediate crash

## Test plan

- [ ] `bun run dev` in `apps/cli`, send a message, press Escape while
streaming -- should cleanly cancel without crash, popup, or error row
- [ ] Press Escape very quickly after sending (before first token) --
same clean behavior
- [ ] After canceling, send another message -- should work normally
- [ ] Ctrl+C during streaming -- should still abort cleanly (goes
through the same `abortAll` path)
- [ ] Normal message completion (no abort) -- unaffected, listeners are
never swapped
2026-05-03 12:06:12 -07:00
Saoud Rizwan 2efc811eff feat(cli): expose plugin tool toggles in settings (#356)
## Summary

This PR updates the CLI settings dialog so users can see and toggle
SDK-backed tools, including tools contributed by plugins, without adding
new core or shared settings infrastructure.

The main goal is to make the `/settings` UI line up with the settings
model that already exists in the SDK. The SDK already persists disabled
tools through the global `disabledTools` list and applies that list when
building the runtime tool set. Plugin files are disabled separately by
path through `disabledPlugins`. Earlier attempts at this feature mixed
in extra backend concepts that made the behavior harder to reason about.
This branch keeps the implementation in the CLI and uses the SDK APIs
that already own the behavior.

## Technical approach

The config data loader now routes tool toggles through
`createCoreSettingsService().toggle({ type: "tools" })`, passing the
same workspace and availability context the settings dialog uses for
listing. That keeps the dialog on the same path as the SDK runtime
filtering. Skill toggles continue to use the core settings service too,
including the active instruction service so the refreshed snapshot
reflects the frontmatter change before the dialog re-renders.

The Tools tab now includes built-in tools and plugin tools in one place.
Built-in tools come from the CLI tool catalog, which is backed by the
core built-in tool catalog and respects globally disabled tool ids.
Plugin tools come from the existing core `listPluginTools` helper,
because that helper exposes the plugin name and source metadata the
dialog needs for grouping.

The UI groups plugin tools by plugin for readability, but the group row
is display-only. That is intentional. The SDK stores disabled tools by
tool name, not by plugin path plus tool name, so a group-level toggle
would imply scoped behavior that does not exist. Individual plugin tool
rows remain toggleable because they map directly to the SDK's global
tool-name setting. When the same tool name appears under more than one
plugin, the row shows a `shared tool name` hint so the user has a clue
that toggling it can affect every plugin that exposes that name.

Plugin enable and disable remains on the Plugins tab and stays
path-based through the existing `setDisabledPlugin` core helper. That
matches how plugin loading works today: disabled plugin paths are
filtered before plugins are loaded.

After a tool, plugin, or skill setting changes from the interactive
dialog, the CLI refreshes the active interactive session policy. If the
session is idle, it restarts with the current messages before returning
from the toggle. If a turn is already running, the refresh is queued and
applied when the turn finishes. This lets changed tool availability take
effect without forcing users to restart the CLI.

The dialog also preserves its active tab and navigation position across
inline refreshes, so toggling a setting no longer jumps the user back to
the top of the settings list.

## Debugging notes and decisions

The main design correction here was realizing that the CLI should not
invent a new backend settings layer for plugin tool toggles. Core
already has the tool toggle mechanism, and runtime tool construction
already honors it through the global disabled tool list. The correct CLI
work is to list and present those SDK-backed settings accurately.

One important gotcha is duplicate plugin tool names. The SDK model is
name-based, so two different plugin files that both register the same
tool name cannot currently be enabled or disabled independently at the
tool level. Instead of hiding that, this PR makes the UI truthful:
plugin group rows are visual only, and duplicate tool-name rows get a
shared-name hint. Plugin-level toggles are still independent because
those are path-based.

Another subtle bug was grouped tool writes. A displayed built-in tool
can map to more than one underlying SDK tool name, for example
editor-related tool names. The loader now toggles each real SDK tool
name and reloads the config data afterward. Those writes are sequential
to avoid racing updates to the same global settings JSON file.

## How to test

Run:

```sh
bun biome check --diagnostic-level=error apps/cli/src/runtime/interactive/config-data.test.ts apps/cli/src/runtime/interactive/config-data.ts apps/cli/src/runtime/run-interactive.ts apps/cli/src/tui/hooks/use-config-panel.tsx apps/cli/src/tui/interactive-config.ts apps/cli/src/tui/views/config-view-helpers.ts apps/cli/src/tui/views/config-view.test.ts apps/cli/src/tui/views/config-view.tsx
bun -F @clinebot/cli typecheck
bun -F @clinebot/cli test:unit -- src/runtime/interactive/config-data.test.ts src/tui/views/config-view.test.ts
git diff --cached --check
```

Manual checks:

Open `/settings` in the CLI. On the Tools tab, built-in tools should
appear first, then plugin tools grouped under plugin headers. Plugin
group rows should show `x/y tools enabled` but should not toggle.
Individual tool rows should toggle inline and keep the settings dialog
open. The selection should stay near the same row after the data
refreshes.

Open the Plugins tab. Toggling a plugin should enable or disable that
plugin path, without implying per-tool scoping.
2026-05-02 21:39:00 -07:00
Saoud Rizwan bdf0ab7e67 feat: add @clinebot/sdk as user-facing alias for @clinebot/core (#229)
We want users to install `@clinebot/sdk` instead of `@clinebot/core`
because "sdk" rolls off the tongue better as the public-facing package
name. `@clinebot/core` already re-exports the key types from
`@clinebot/agents`, `@clinebot/llms`, and `@clinebot/shared`, so
`@clinebot/sdk` is a thin wrapper that just does `export * from
"@clinebot/core"`.

## What changed

New `packages/sdk/` directory containing:

- `package.json` -- named `@clinebot/sdk`, version `0.0.36` (matching
current published packages), single dependency on `@clinebot/core` via
`workspace:*`. Same publish-related fields (`main`, `types`, `exports`,
`files`, `publishConfig`) as core, adapted for the simpler
single-entrypoint structure.
- `src/index.ts` -- literally just `export * from "@clinebot/core"`.
- `bun.mts` -- minimal Bun.build config that externalizes
`@clinebot/core` so the output JS is just a re-export, not a bundle of
core's internals.
- `tsconfig.json` / `tsconfig.build.json` -- follows the same pattern as
other packages (extends `tsconfig.base.json`, emits declarations only
via tsc).

The release script (`scripts/release.ts`) was updated to add `"sdk"` to
`SDK_PUBLISH_ORDER` after `"core"`, so it gets published in the correct
dependency order during `bun release sdk`. The help text was also
updated to reflect the new package in the list.

No changes were needed for:
- Workspace registration: root `package.json` uses `"packages/*"` glob,
so `packages/sdk` is auto-discovered.
- Version bumping: `scripts/version.ts` iterates all package directories
and bumps non-internal packages automatically.
- Publish verification: `scripts/check-publish.ts` auto-discovers
non-internal packages. Verified it picks up `@clinebot/sdk` and the
package passes all checks (packing, manifest version alignment, npm
install in isolation, module resolution).

## Verification

- `bun run build:sdk` succeeds, `@clinebot/sdk` builds cleanly alongside
all other packages
- `bun -F @clinebot/sdk typecheck` passes
- `bun scripts/check-publish.ts` passes with all 5 published packages
(shared, llms, agents, core, sdk) verified
- Built output is minimal: `dist/index.js` is
`export*from"@clinebot/core";` and `dist/index.d.ts` is the
corresponding re-export declaration

## Test plan

- [x] `bun run build:sdk` builds all packages including sdk
- [x] `bun -F @clinebot/sdk typecheck` passes
- [x] `bun scripts/check-publish.ts` verifies all 5 packages pack,
install, and resolve correctly
- [ ] After merge, `bun release sdk` should publish `@clinebot/sdk` to
npm alongside the other packages
2026-05-02 20:01:34 -07:00
Bee 4545dce7f6 fix: improve hub recovery and session status UI (#355)
Add hub command error handling, logging, and recovery timeouts to make
Hub interactions more resilient and diagnosable.

Refresh interactive exit summaries with duration and highlighted resume
command, and avoid showing misleading context usage when a model context
window is unknown.
2026-05-02 02:35:10 -07:00
Bee 969ac0fc5f refactor: remove old Hook bridge (#351)
Key changes:
- Deleted `HookBridge`, `hook-registry`, and the old shared
`HookEngine`.
- Removed the old extension/plugin hook surface (`hookStages`,
`onRunStart`, `onBeforeAgentStart`, etc.).
- `AgentExtension` / plugins now provide runtime-native `hooks:
Partial<AgentRuntimeHooks>`.
- Updated plugin sandbox, hub hook contributions, CLI hooks, hook-file
hooks, checkpoint hooks, docs, and examples to use `beforeRun`,
`beforeModel`, `beforeTool`, `afterTool`, `afterRun`, and `onEvent`.
- Preserved the message-builder path before gateway model calls, so
`MessageBuilder.buildForApi`/registered builders are no longer skipped.
- Added `parentAgentId` to runtime snapshots so root/sub-agent hook
behavior can stay explicit without the old bridge.
2026-05-01 23:50:51 -07:00
Bee f81531db12 refactor: reduce core package size (#352)
Add a Shiki-lite highlighting layer for webview code blocks with cached
highlighters, supported language normalization, and a Streamdown plugin
to
render highlighted tokens efficiently.

Also minify VS Code extension builds and update hub imports to use the
dedicated @clinebot/core/hub entrypoint.
2026-05-01 18:15:51 -07:00
Bee 4a13618f88 fix: OAuth browser-launch failure (#353)
The core auth callback now catches both rejected opener promises and
synchronous launcher throws, so a missing xdg-open no longer aborts
login after printing the auth URL.
Hardened the interactive auth UI paths so they still show the manual URL
when open() throws synchronously
2026-05-01 18:12:19 -07:00
Robin Newhouse cd5056959f test(llms): add Google model live coverage (#332)
## Summary
- add Gemini 3.1 Pro and Gemma 4 entries to the live provider smoke
config
- add Gemini 3.1 Pro reasoning coverage to the reasoning live config
- add Gemini 3.1 Pro and Gemma 4 tool-call coverage to the tool live
config

## Verification
- pre-commit hook: bun run types
- pre-commit hook: bun biome check --no-errors-on-unmatched
--files-ignore-unknown=true
- bun run typecheck (packages/llms)
- focused smoke subset for gemini/gemini-pro/gemma
- focused tool subset for gemini-tools/gemini-pro-tools/gemma-tools
- focused reasoning subset for gemini-reasoning/gemini-pro-reasoning
2026-05-01 18:04:27 -07:00
Bee f6791fded1 refactor: replace Tool with AgentTool (#346) 2026-05-01 17:03:05 -07:00
Saoud Rizwan ff6324794c fix(cli): report compact no-op distinctly (#349)
## Problem

CLI manual compaction could print a misleading status such as `Compacted
300 messages to 300`. That output made it look like compaction
successfully ran but did not reduce anything.

The important detail is that core already distinguishes two cases:

- `undefined` means no compaction result was produced
- `{ messages }` means compaction produced messages, even when the count
is unchanged

The CLI integration collapsed both cases with `result?.messages ??
input.messages`, then always restarted the session and always printed a
success-looking compacted message.

## Approach

This keeps core compaction policy unchanged and fixes only the CLI
integration boundary.

`compactInteractiveMessages` now returns a result object with
`compacted` and `messages`. The `compacted` flag is derived from whether
core returned a compaction result, not from the message count.
`compactCurrentSession` now skips restarting the interactive session
when core returns no compaction result. The TUI status formatter uses
the flag to choose clearer output.

The visible behavior is now:

- Empty session: `No messages to compact.`
- Core returned no result: `No compaction needed.`
- Core returned changed messages with the same count: `Compacted
context; message count stayed at N.`
- Core returned changed messages with a different count: `Compacted N
messages to M.`

## Debugging notes

I read the core compaction path first to verify the contract.
`createContextCompactionPrepareTurn` returns `undefined` when compaction
should not run or a strategy has no result. Built-in basic compaction
can also return messages with the same count if it sanitizes or trims
content without removing entries.

The bug was in the CLI adapter. Returning the original input messages as
a fallback erased the difference between no result and real compacted
messages. Since session runtime only received a message array, it had no
reliable way to decide whether it should restart or what the TUI should
report.

## Gotchas

Message count is not a reliable signal for whether compaction happened.
A same-count result can still be meaningful if message content changed.
Conversely, equal before and after counts with no core result means
nothing happened.

The status formatter lives in a small pure utility so it can be tested
without importing the OpenTUI React hook stack.

## Testing

Ran focused tests:

```sh
bun run test:unit -- src/runtime/interactive/compaction.test.ts src/tui/hooks/use-local-command-actions.test.ts
```

Ran CLI typecheck:

```sh
bun run typecheck
```

Ran formatter and lint checks on touched files:

```sh
bunx biome check --diagnostic-level=error apps/cli/src/runtime/interactive/compaction.ts apps/cli/src/runtime/interactive/session-runtime.ts apps/cli/src/tui/types.ts apps/cli/src/tui/hooks/use-local-command-actions.tsx apps/cli/src/tui/utils/compaction-status.ts apps/cli/src/runtime/interactive/compaction.test.ts apps/cli/src/tui/hooks/use-local-command-actions.test.ts
```

Ran the full CLI unit suite:

```sh
bun run test:unit
```

One first full-suite attempt timed out in an unrelated
`src/main.test.ts` history JSON dispatch test. That single test passed
when rerun directly, and a second full CLI unit suite run passed with
all 346 tests.
2026-05-01 16:28:58 -07:00
Bee 35a2a5bcfa refactor: Runtime Capability (#337)
## Problem We Are Solving

The current SDK architecture makes client-owned interactive behavior too
easy to implement twice: once for the local runtime path and once for
the hub-backed runtime path.

Examples:

- `ask_question` works in CLI because CLI passes a direct
`defaultToolExecutors.askQuestion` callback.
- In hub mode, capability-backed tools are proxied through
`capability.requested` / `capability.respond`.
- VS Code and Code App currently create hub-backed `ClineCore`
instances, but do not register `askQuestion` / `submit` local executors,
so the core hub transport fix alone is not enough to show app-native
dialogs.
- Code App has separate hub approval plumbing for `approval.requested`,
while local mode uses `requestToolApproval`.

This split causes repeated app work, inconsistent behavior, missed
feature wiring, and bugs where a feature works in local mode but not in
hub mode.

## End Goals

1. App teams implement client-local runtime features once.
2. The same implementation works for local, shared hub, and remote hub
routing.
3. Apps should not need to manually handle hub transport events such as
`capability.requested` unless they are intentionally building a raw hub
client.
4. Core owns transport adaptation:
   - local mode invokes handlers directly;
- hub mode advertises handlers and replies to hub capability requests;
   - routing uses hub `sessionId`;
- semantic context still carries `conversationId`, `agentId`,
`iteration`, etc.
5. Approval UI and client-local tools should follow the same pattern
where possible, so we do not maintain parallel local/hub UI paths.

## Refactor Standard And Foundation Goals

This codebase is still WIP and does not have production consumers or
real external users depending on legacy behavior. Optimize for a clear,
scalable foundation over compatibility-preserving workarounds.

When making this change:

- Prefer the clean architecture we want long term, even if it requires
updating all call sites.
- Do not keep confusing APIs, duplicated paths, or transitional shims
just because they currently exist.
- If a current abstraction does not make sense, remove it and rebuild
the right one.
- Avoid fixes that only patch the immediate symptom while leaving
local/hub feature duplication intact.
- Future-proofing matters: design the capability layer so future
client-owned features can plug into one path instead of adding another
local/hub special case.
- Keep transport concerns inside core transport layers; keep app UI
behavior in app-owned capability handlers.
- Document any intentional boundary so future contributors understand
where new features should be added.

The goal is not just to fix `ask_question`. The goal is to set up a
maintainable runtime capability foundation for the whole SDK.

<img width="1141" height="737" alt="image"
src="https://github.com/user-attachments/assets/11a50846-28aa-43d1-a663-c823ab84b03a"
/>
2026-05-01 16:22:17 -07:00
Ara 85db13f7b5 Fix Telegram connector tool lock and raw replies (#255)
## Summary
- honor `cline connect telegram --no-tools` by reading Commander's
negated `tools` option correctly
- keep Telegram startup tool-disabled mode locked across persisted
thread state, `/tools` and `/yolo` commands, and runtime configs
- post Telegram runtime replies as raw text to avoid Telegram markdown
parse failures from model output
- let connector runtime turns opt out of the generic 30s hub reply
timeout while streaming progress
- add `/help` and `/start` connector help so Telegram users can discover
commands, current tools/yolo state, supported workflows, and
`--no-tools` lock state in chat
- correct Telegram/system prompt copy and update the CLI README example

## Testing
- `bun -F @clinebot/cli test:unit`
- `bun -F @clinebot/cli test:unit -- chat-commands`
- `bun -F @clinebot/cli test:unit -- chat-commands connector-host`
- `bun -F @clinebot/cli test:unit -- runtime-turn`
- `bun -F @clinebot/cli test:unit -- connector-host`
- `bun -F @clinebot/cli test:unit -- telegram`
- `bun -F @clinebot/cli typecheck`
- `bun -F @clinebot/core typecheck`
- `bun biome check packages/core/src/hub/session-client.ts
apps/cli/src/connectors/runtime-turn.ts
apps/cli/src/connectors/runtime-turn.test.ts
apps/cli/src/connectors/connector-host.test.ts
apps/cli/src/connectors/adapters/prompts.ts`
- `bun biome check apps/cli/src/utils/chat-commands.ts
apps/cli/src/utils/chat-commands.test.ts apps/cli/README.md`
- `bun biome check apps/cli/src/utils/chat-commands.ts
apps/cli/src/utils/chat-commands.test.ts
apps/cli/src/connectors/connector-host.ts`
- `bun run check`
- live Telegram smoke via local Telegram Desktop: `/whereami`, `/tools
on`, markdown/backtick stress reply, `/new`, fresh-session smoke message
- live tools-on/yolo-on Telegram turn: confirmed `/whereami` reported
`tools=on`; `/yolo on` reported `yolo=on`; tool-backed repo inspection
used `run_commands`, `read_files`, and `search_codebase`, then exposed
the 30s hub send timeout fixed in this PR
- live Telegram `/help` smoke after restart: help now lists connector
commands and shows the current tool/yolo state
2026-05-01 22:59:25 +00:00
Ara 11f7d515ea Add TUI exit session summary (#306)
## Summary
- Collect persisted interactive session details before graceful TUI
cleanup stops or detaches the session.
- Print a post-TUI `Session saved` summary with session ID, model, CWD,
message count, cost, and `clite --id <session>` continue command.
- Route TUI quit actions through the existing exit-request guard and add
formatter coverage.

## Validation
- `bun biome check apps/cli/src/runtime/interactive/exit-summary.ts
apps/cli/src/runtime/interactive/exit-summary.test.ts
apps/cli/src/runtime/interactive/session-runtime.ts
apps/cli/src/runtime/run-interactive.ts apps/cli/src/tui/root.tsx`
- `bun -F @clinebot/cli test:unit --
src/runtime/interactive/exit-summary.test.ts`
- `bun -F @clinebot/cli typecheck`
- `bun run build`
- `bun -F @clinebot/cli test:unit`
- `bun -F @clinebot/cli build`
- Manual TUI smoke: submitted a prompt in an isolated local session,
then `/quit`; verified the terminal restored and printed `Session saved`
plus `Continue clite --id ...`.
- Manual TUI smoke: resumed the same session with `clite --id ...`,
pressed Ctrl+D, and verified the same summary printed after shutdown.
2026-05-01 15:41:05 -07:00
Robin Newhouse 4212c7d447 Add staged secret scan pre-commit hook (#344)
## Summary
- use Gitleaks' documented staged pre-commit command instead of a custom
scanner
- add a minimal `.gitleaks.toml` that extends the built-in Gitleaks
rules
- keep the existing `lint-staged` hook after the secret scan

## Research notes
- Gitleaks' official pre-commit metadata uses: `gitleaks git
--pre-commit --redact --staged --verbose`
- Gitleaks git mode scans patches, avoiding the custom scanner's
full-file false-positive issue
- Gitleaks config can extend the default built-in rule set with
`[extend] useDefault = true`

## Verification
- `gitleaks version` -> 8.30.1
- `gitleaks git --pre-commit --redact --staged --verbose`
- `bun biome check package.json --diagnostic-level=error`
- staged a temporary Gitleaks README sample secret and confirmed the
scanner blocks it with redacted output

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-05-01 14:31:56 -07:00
Max d3c1459c50 add source maps option (#341)
- adds the option to build sourcemaps for debugging purposes

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-05-01 11:19:40 -07:00
Max 86f463496c bump versions and changelog (#10503)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-05-01 09:25:09 -07:00
Bee 76cbd0b45d fix: make CLI history resume transactional (#334)
Follow up on https://github.com/cline/sdk-wip/pull/328

## Issues

When resuming a session via /history in the CLI TUI, three failure
modes could leave the user with a broken UI or corrupt the historical
session on disk:

1. If readMessages() threw or the manifest was missing/corrupt, the
   current chat was already cleared and the current runtime already
   stopped before the failure surfaced. The slash-command dispatcher
   fires openHistory() without awaiting, so the rejection escaped
   silently — no error entry, blank chat.

2. If readMessages() returned [], we still called start() with the
   resumed sessionId and an empty initialMessages array, then set
   hasSubmitted(true) and switched to chat view — leaving the TUI in
   chat mode with nothing to render.

3. The read-only resume branch in LocalRuntimeHost.start() requires
   initialMessages.length > 0 to reuse the existing manifest. With an
   empty resume, that gate failed and a fresh manifest was written
   under the historical session id, mutating the on-disk record as if
   it were a brand-new session.

4. Subscribers of a hub session would see run.started followed by
session.updated: failed but no terminal run-level event when the agent
turn errored. Downstream clients map run.failed → agent_event
done/error,
ended, turn_done, and live UI cleanup, so the missing event left UIs
hanging on a session that had actually terminated.

## Root cause

resumeSession() in apps/cli was non-transactional: it stopped the
current runtime first, then read the target session's messages, then
started the resumed session. There was no validation that the target
existed or had any messages before destructive state changes, and no
rollback path if the read or start failed. The TUI hook compounded
this by clearing chat entries before the resume promise settled and
by not catching rejections.

VS Code's attachSession flow was not affected — it goes through the
hub via session.attach and never round-trips through start() with
initialMessages, so the read-only-resume gate never applies. The
core-side gate in local.ts is correct; the bug was strictly in the
CLI's resume orchestration.

For item 4, handleSessionInput sets a "run.start.reply" suppress token
before calling sessionHost.send, intending to take ownership of the
terminal run event from the projector once send returns. The session-
event projector suppresses the local "ended" event whenever the token is
present, regardless of the ended reason. On the success path the handler
then publishes the result-bearing run.completed/failed/aborted itself.
On the throw path (local transport failSession → shutdownSession emits
ended: "error", then send rethrows), the projector still suppresses the
ended event but the handler's catch block only cleared the token and
rethrew — so no terminal run event was ever published.

## Fix

- session-runtime.ts: resumeSession() now looks up the session record
  and reads its messages first. It throws a typed error if the session
  is missing or empty, and only then calls stopCurrentSession() and
  startResumedSession(). This guarantees initialMessages.length > 0 by
  the time start() runs, so the read-only resume branch is taken and
  the historical manifest is preserved.

- use-local-command-actions.tsx: openHistory() now wraps the resume in
  try/catch. The current chat is no longer cleared until hydration
  produces visible entries. Failures and empty hydrations append a
  kind: "error" entry instead of leaving a blank screen, and the view
  only switches to chat / sets hasSubmitted(true) on success.

- For item 4, In the catch block of handleSessionInput, after clearing
the
suppress token, publish run.failed with {reason: "error", error: <msg>}
before rethrowing. This restores the contract that every run produces
exactly one terminal run event, matching the returned finishReason:
"error" path.
2026-04-30 20:49:20 -07:00
Saoud Rizwan 11e5c499db fix(cli): compact slash command descriptions (#333)
## Problem

Skill slash commands can carry descriptions from markdown frontmatter.
YAML block descriptions can include hard line breaks, and the TUI
autocomplete menu rendered those breaks directly in the command row.
That made the slash-command dropdown taller and visually uneven for
descriptions that should read as a compact preview.

## Approach

Normalize slash command descriptions at the TUI registry boundary by
collapsing all whitespace runs into a single space and trimming the
result. This keeps the fix close to the autocomplete display path while
leaving core skill and workflow metadata untouched.

This also covers non-skill command sources that flow through the same
registry, such as plugin commands, without adding source-specific
behavior.

## Decisions

I initially considered normalizing at the core runtime command
projection too, but that would change exported command metadata for
every caller. The dropdown only needs display-safe text, so the cleaner
boundary is the TUI slash-command registry.

No regression test was added because the change is a tiny display
normalization and the request was to keep this lightweight.

## Testing

- Ran `bun run typecheck` in `apps/cli`
- Ran `bun biome check --diagnostic-level=error
apps/cli/src/tui/commands/slash-command-registry.ts`
- Ran `git diff --check`
2026-04-30 18:47:46 -07:00
Bee e8e3e7db4a fix: preserve cross-client session history and interactive turn status (#328)
## Summary

This fixes regressions introduced across the recent session/history
changes:

- #325 / `917a3ab7` reverted the manifest fallback from #317, so CLI
history could list Code app / VS Code sessions but could not reliably
load their records or messages. Restore manifest-backed `get()` and
`readMessages()` in the local runtime host.
- #296 / `406defe5` added VS Code post-send hydration after live
streaming, which replayed persisted user/assistant messages on top of
the live messages. Remove that replay path.
- The hub/runtime interactive lifecycle from #196/#203 left completed
interactive sessions persisted as `running`, then later cleanup rewrote
them as `cancelled`/hub `aborted`. Mark each interactive turn terminal
when it finishes while keeping the in-memory session available for
future sends.
- Fix hub terminal event suppression so clients receive one
result-bearing `run.completed`, not a duplicate terminal event race.

## Testing

- `bunx vitest run packages/core/src/transports/local.test.ts
packages/core/src/transports/hub.test.ts
apps/cli/src/session/session.test.ts
apps/cli/src/commands/history.test.ts`
- `bun run types`

Note: `packages/core/src/hub/server/boundary.test.ts` could not run here
because `node:sqlite` is unavailable before the changed code is
exercised. X
2026-04-30 17:44:47 -07:00
Saoud Rizwan 72562ea74e feat: add beta version checkbox to bug report issue template (#10490)
Add a checkbox for users to indicate they're on a beta version, and
auto-apply the 'beta' label via the existing auto-label workflow when
the checkbox is checked.
2026-04-30 17:35:32 -07:00
Bee a51daacc66 chore: clean up CLI program description (#331)
Move CLI log utilities under doctor command

Consolidate local diagnostics and maintenance commands under `clite
doctor` by replacing the standalone `dev log` command with `doctor log`
and moving stale-process cleanup from `doctor --fix` to the `doctor fix`
subcommand.

Also update CLI help, README docs, and tests to reflect the new command
structure, and add a shared `ensureFileExists` storage helper for
log-file creation.

```
Usage: clite [options] [command] [prompt]

Cline CLI - AI coding assistant in your terminal

Arguments:
  prompt                        Your prompt. Default to start in act mode with auto-approve enabled.

Options:
  -V, --version                 Output the version number
  -p, --plan                    Run in plan mode
  --json                        Output messages as JSON instead of styled text
  --auto-approve <boolean>      Set tool auto-approval for all tools (default: true)
  -c, --cwd <path>              Working directory
  --thinking <level>            Set reasoning effort level between none|low|medium|high|xhigh (default: medium)
  -i, --tui                     Open the terminal user interface (TUI) for interactive sessions
  --id <session-id>             Resume an existing session by ID
  -P, --provider <id>           Provider id (default: cline)
  -k, --key <api-key>           API key override for this run
  -m, --model <model-id>        Model to use for the session with the selected provider
  -s, --system <system-prompt>  Override the default system prompt
  -z, --zen                     Start a session that runs in the background hub
  --retries [value]             Number of maximum consecutive mistakes (retries) before exiting (default: 6)
  -t, --timeout <seconds>       Optional timeout in seconds (default: 0 for no timeout)
  --acp                         Run in Agent Client Protocol (ACP) mode for editor integration
  --config <path>               Configuration directory (default: ~/.cline/data/settings)
  --data-dir <path>             Use isolated local state at this directory path (default: ~/.cline)
  --hooks-dir <path>            Directory path to additional hooks for runtime hook injection (default: ~/.cline/hooks)
  --update                      Check for updates and install if available
  -v, --verbose                 Show verbose output
  -h, --help                    display help for command

Commands:
  auth [options] [provider]     Authenticate a provider and configure what model is used
  config [options]              Show current configuration
  connect [options] [adapter]   Connect to an editor or IDE adapter
  mcp                           Manage MCP servers
  doctor                        Diagnose and fix configuration issues
  history|h [options]           List session history or manage saved sessions
  hook                          Handle a hook payload from stdin
  schedule                      Manage scheduled tasks
  hub                           Manage the local hub daemon
  update [options]              Check for updates and install if available
  version                       Show Cline CLI version number
  kanban                        Launch the kanban app and exit
  ```
2026-04-30 17:26:35 -07:00
Bee 08c32f6bb0 refactor: clean up CLI flags ENG-1760 (#330)
remove --act
consolidates --thinking with -reasoning-effort
removes --reasoning-effort
renames --autoapprove with --auto-approve
2026-04-30 16:25:33 -07:00
Bee 201e324cb1 fix: configuration file path for hooks and rules CLINE-2032 (#329)
Final behavior in
`/Users/beatrix/dev/sdk/packages/shared/src/storage/paths.ts`:
- Global hooks resolve from `~/.cline/hooks`
- Global rules resolve from `~/.cline/rules`
- They no longer include:
  - `~/.cline/data/hooks`
- `~/.cline/data/rules` Updated tests in
`/Users/beatrix/dev/sdk/packages/shared/src/storage/paths.test.ts` to
assert the new locations and verify the old data paths are not included.
2026-04-30 16:00:21 -07:00
Saoud Rizwan 81371a629c chore(cli): release v0.0.7 2026-04-30 15:45:07 -07:00
Saoud Rizwan 3532987ce5 fix(agents): recover invalid tool call inputs (#327)
# Problem

Sonnet 4.6 can emit malformed tool arguments for `run_commands`. The
concrete failure seen in the local CLI session was shaped like this:

```json
{"commands": find /workspace/cline-sdk-wip/apps/cli/src -type f | head -20}
```

That is not valid JSON because the command string is not quoted, and it
also does not match the expected `commands` array shape. Opus 4.7 did
not hit the bug in the same task because it emitted valid tool
arguments.

The bad behavior was not just that the tool call failed. The bad
behavior was that the whole turn failed at the runtime level before the
model got a normal tool error result. That made a recoverable model
mistake look like a provider or runtime crash in the CLI.

The failed session that motivated this was
`~/.cline/data/sessions/1777586840324_gtsxp/`. Its final assistant
message already had `metadata.invalidToolCalls` with the raw malformed
input, and the assistant tool call fell back to `input: {}`. The logs
showed the runtime throwing from `packages/agents/src/agent-runtime.ts`
when `finishReason === "error"`. A comparable Opus session at
`~/.cline/data/sessions/1777586807560_bgl02/` completed normally with
valid JSON, normal metrics, and a tool result.

# What broke

The recovery path already mostly existed in the agent runtime:

- `parseToolInput` detects invalid JSON and records `inputParseError`.
- `prepareToolExecution` turns that metadata into a skip reason.
- `executePreparedTool` can return an error `tool-result` instead of
executing the tool.

The problem was ordering. The runtime built an assistant message
containing the tool call, but it threw immediately on `finishReason ===
"error"` before it looked at the tool calls. That prevented the existing
invalid-input recovery path from running.

There was a second adapter-level issue around AI SDK `tool-error` stream
parts. Those represent tool-call input failures that should be fed back
to the model as tool errors, but the adapter treated them like fatal
stream errors. That made malformed tool input indistinguishable from
provider transport or generation failures.

# Technical approach

This PR changes the AI SDK provider adapter so `tool-error` stream parts
are converted into recoverable `tool-call-delta` events with
`inputParseError` metadata. The metadata preserves the AI SDK error
message, keeps the existing provider/tool source metadata, and lets the
agent runtime produce a normal error `tool-result` for the same tool
call.

The agent runtime now only throws immediately for `finishReason ===
"error"` when there are no tool calls in the assistant message. If the
stream ended with an error but did produce tool calls, the runtime
continues into the normal tool execution path. For malformed arguments,
that path emits an error tool result and gives the model another turn to
correct itself.

Fatal stream errors are still fatal when there is nothing actionable to
return to the model. This keeps provider failures, auth issues, and
empty stream errors from being hidden as fake tool failures.

# Debugging notes

The key observation was that the failed Sonnet session was not missing
the malformed call entirely. The assistant message already had enough
information to recover:

```json
{
  "toolCallId": "toolu_01G6pAuUfiGHosn5XFHgyf4S",
  "toolName": "run_commands",
  "input": {
    "rawInputText": "{\"commands\": find /workspace/cline-sdk-wip/apps/cli/src -type f | head -20}",
    "parseError": "Tool call arguments could not be parsed as JSON. Ensure the outer tool payload is valid JSON and escape embedded quotes/newlines inside string fields."
  },
  "reason": "invalid_arguments"
}
```

So the fix did not need to invent a new parsing system. It needed to
stop treating every error finish as unrecoverable when a tool call
exists, and it needed to keep AI SDK `tool-error` parts in the tool-call
lane instead of moving them into the fatal stream-error lane.

One non-obvious gotcha is duplicate tool-call information. AI SDK can
emit a `tool-call` part and then a `tool-error` for the same call id.
The adapter tracks emitted tool call ids so the later `tool-error` can
attach metadata without replacing or duplicating the original raw input
text.

# Decisions

I kept the recovery behavior in `@clinebot/agents` because that package
owns the stateless tool orchestration loop and already has the
skip-result machinery for invalid inputs.

I kept the AI SDK `tool-error` handling in `@clinebot/llms` because it
is provider adapter behavior. The adapter should translate AI SDK stream
semantics into the shared `AgentModelEvent` contract without forcing the
runtime to know AI SDK internals.

I did not make `finishReason === "error"` universally non-fatal. That
would hide real model or provider failures. The runtime only continues
when it has at least one tool call it can answer.

# How to test

Focused tests run:

```sh
bun -F @clinebot/agents test -- src/agent-runtime.test.ts
bun -F @clinebot/llms test -- src/providers/gateway.test.ts
bun -F @clinebot/agents typecheck
bun -F @clinebot/llms typecheck
bun biome check --diagnostic-level=error packages/llms/src/providers/ai-sdk.ts packages/llms/src/providers/gateway.test.ts packages/agents/src/agent-runtime.ts packages/agents/src/agent-runtime.test.ts
```

The commit hook also ran the repo staged checks, including `bun run
types` and Biome on the staged files.
2026-04-30 15:35:45 -07:00
Robin Newhouse 134d9671a6 ENG-1890: Add core settings skill toggles (#297)
## Summary
- Adds a core-owned settings service/facade for listing and toggling
settings, with skill frontmatter mutation kept behind the settings path.
- Adds hub `settings.list` / `settings.toggle` handling and publishes
`settings.changed` after successful mutations.
- Updates CLI Settings to use the settings path for skill toggles while
preserving workflows under Skills as non-toggleable rows and keeping
plugin tool toggles inline.
- Tightens Settings detail panes so status/toggle hints only appear for
toggleable rows and long customization descriptions wrap/truncate
without covering labels.

Supersedes #295.

Linear: ENG-1890
Related cleanup overlap: ENG-1891

## Validation
- `bun run check`
- `bun --conditions=development test
packages/core/src/settings/settings-service.test.ts`
- `bun --conditions=development test
apps/cli/src/tui/components/dialogs/config-dialogs.test.ts`
2026-04-30 15:32:20 -07:00
Bee f597bd031b feat: Secure local hub daemon with discovery auth token SEC-68 (#298)
Generate a per-process random auth token when the local hub daemon
starts and store it in the owner discovery record with 0600 permissions.
Discovery-based local clients now carry that token into the WebSocket
URL, and the hub server validates it with a constant-time comparison
before accepting /hub upgrades or /shutdown requests.

This prevents arbitrary local processes from connecting to the hub,
reading or mutating sessions, injecting prompts, triggering tool
execution, or stopping the daemon. Public health/version metadata
remains available for compatibility probing, but command-bearing
WebSocket traffic and shutdown require the token.

Also documents the local hub authentication contract in ARCHITECTURE.md
and updates focused hub tests for token persistence, URL propagation,
authenticated shutdown, and daemon reuse.

---------

Co-authored-by: TheRealSpencer <spencer@cline.bot>
2026-04-30 14:40:57 -07:00
Saoud Rizwan 917a3ab739 Revert "fix(cli): load extension-created sessions from history" (#325)
Reverts cline/sdk-wip#317
2026-04-30 14:31:44 -07:00
Saoud Rizwan 5596608786 fix(cli): load extension-created sessions from history (#317)
## Problem

The CLI history picker can list sessions that come from the manifest
fallback. That includes sessions created outside the CLI, such as
extension-created sessions. Selecting one of those visible entries
called the normal resume path, but the runtime host only read messages
through the backend session row. If the backend row was missing,
`readMessages` returned an empty array even though the manifest had a
valid `messages_path`, so the TUI switched to chat with no entries and
appeared blank.

## Technical approach

This changes `LocalRuntimeHost.readMessages` to keep the existing
backend-row behavior first, then fall back to
`readSessionManifest(sessionId)` and load `manifest.messages_path` when
the row does not have a messages path. That makes the read side match
the history list behavior, which already merges backend rows with
manifest fallback rows.

After a deeper pass through the hub path, this also makes
`LocalRuntimeHost.get` fall back to the manifest and project it into a
`SessionRecord`. That matters because a hub-backed interactive runtime
validates `session.messages` by calling `sessionHost.get(sessionId)`
before it calls `readMessages`. Without the `get` fallback, the local
fix could still be bypassed whenever the interactive runtime was
connected through the local hub.

The CLI history action was also tightened so resume no longer clears the
chat before it knows what it can render. It now hydrates the resumed
messages first, replaces the session entries atomically, and shows an
explicit status or error entry when messages are empty or resume fails.
That prevents the blank-screen failure mode even for genuinely empty or
unreadable sessions.

## Debugging journey

I traced `/history` through `HistoryDialogContent`,
`useLocalCommandActions`, and
`createInteractiveSessionRuntime.resumeSession`. The Enter handler was
resolving the selected session id correctly. The empty screen came from
`onResumeSession` returning `[]`, after the UI had already cleared the
current entries and switched to chat.

The core issue was lower than the TUI. `listSessions` uses
`core.listHistory({ includeManifestFallback: true })`, so entries can be
visible from manifests even when they are not in the active backend
index. `LocalRuntimeHost.readMessages`, however, only looked up a
backend row and read `row.messagesPath`. For manifest-only entries, that
skipped the manifest artifact entirely.

A follow-up read found the same mismatch at the session record layer:
`LocalRuntimeHost.get` also only checked active sessions and backend
rows. Since the hub server calls `get` before serving
`session.messages`, manifest-backed sessions needed to be readable there
too.

## Gotchas and decisions

I fixed the runtime host rather than only adding a UI workaround because
command-line resume, hub-backed resume, export-style reads, and other
consumers of `readMessages(sessionId)` should be able to read any
session that history can list. The UI guard is still useful because a
session can legitimately have no persisted messages, or the artifact can
be missing.

I rebuilt the PR branch from the updated `origin/main` and cherry-picked
only the fix onto it after main was corrected, so this branch does not
carry the removed commits.

## Testing

Ran on the updated PR branch:

```sh
bun vitest run packages/core/src/transports/local.test.ts apps/cli/src/utils/resume.test.ts apps/cli/src/commands/history.test.ts
```

Also ran:

```sh
bun run typecheck
```

in `packages/core`. The commit hook also ran `bun run types` and Biome
for the staged files.
2026-04-30 14:09:22 -07:00
Saoud Rizwan b82db1caa2 fix(core): route hub tool capabilities by session id (#322)
## Problem

Hub-backed interactive CLI sessions could show a tool row for
`ask_question` but never show the dialog. The agent was waiting on a
capability request, while the CLI had only received the projected
`tool.started` event.

The root cause was a routing identity mismatch in hub capability-backed
tools. Hub event streams are keyed by the hub session id, but the
capability-backed executor was publishing requests with
`ToolContext.conversationId`. Runtime conversations usually use `conv_*`
ids, while the CLI subscribes to the hub `sessionId`. That meant the row
could arrive on the session stream while the actual UI request was
published on a different id.

This was easiest to notice with `ask_question` because it waits on a
visible dialog, but the underlying bug applied to every advertised
capability-backed local executor, including submit and other
client-local tool executors routed through the hub.

## Technical approach

The hub server now allocates or preserves the hub session id before
constructing capability-backed local executors during session create and
checkpoint restore. That session id is passed into
`createCapabilityBackedToolExecutors`, and capability requests are
published on that stable hub session stream.

The original tool context is still serialized into the payload, so
callers still receive the agent `conversationId`, `agentId`, and
iteration metadata. The change is only about transport routing, not tool
context semantics.

I also kept local tool executors registered after `run.completed` and
`run.aborted`. Interactive sessions continue across multiple turns, so
clearing session-local executors at the end of one run can make the next
turn reject a valid local capability with `No executor registered`.
Cleanup still happens through stop, delete, and dispose.

## Debugging notes

The symptom initially looked like a TUI dialog or focus issue because
the row rendered and no modal appeared. Tracing the flow showed that
these are separate events:

- `tool.started` is projected from agent events onto the hub session
stream.
- `capability.requested` is what actually invokes the local CLI executor
and opens the dialog.

Because those events were published under different ids, the CLI could
render the row and still never receive the dialog request. Restarting
the detached hub was required to validate the daemon-side fix locally.

## Tests

Added regression coverage for:

- capability-backed tools publishing `capability.requested` on the hub
session stream even when the tool context has a separate
`conversationId`
- local hub tool executors remaining available after a run completes

Commands run:

```sh
bun run vitest run --config vitest.config.ts src/hub/server/boundary.test.ts src/transports/hub.test.ts
bun run typecheck
cd apps/cli && bun run typecheck
bunx biome check packages/core/src/hub/server/helpers.ts packages/core/src/hub/server/handlers/session-handlers.ts packages/core/src/hub/server/boundary.test.ts packages/core/src/transports/hub.ts packages/core/src/transports/hub.test.ts
git diff --check
```

I also ran repo-wide `bun run types`, but it is currently blocked by
existing enterprise/plugin `settingsKey` type errors unrelated to this
change. Those same errors blocked the pre-commit hook, so the commit was
made with hooks disabled after the affected package checks passed.
2026-04-30 14:08:38 -07:00
Saoud Rizwan 52b9d9f510 fix(cli): restore safe auto-approve tool policies (#324)
Problem:
The CLI used to keep a small set of safe host-side tools auto-approved
even when auto-approve-all was disabled. That behavior was present on
`saoudrizwan/cli-tui-opentui-backup`, but it was lost after the CLI
runtime moved into the newer hub/spoke architecture. The current main
branch only carries a global `"*"` policy, so disabling auto-approve-all
turns `ask_question`, `read_files`, and other safe tools into
approval-required tools.

Approach:
This restores the safe default list in
`apps/cli/src/runtime/tool-policies.ts`, which is the current
architecture's CLI host policy layer. The hub and core routing stay
untouched. They should receive resolved policy data from the CLI, not
decide which tools are safe by default.

When interactive auto-approve is disabled, the helper now:
- sets the global `"*"` policy to `autoApprove: false`
- keeps known safe tools explicitly auto-approved
- adds missing safe-tool policy entries when the baseline only contains
`"*"`
- preserves explicit per-tool `autoApprove: false` opt-outs for safe
tools
- still restores the original baseline exactly when auto-approve is
toggled back on

Debugging context:
The old backup branch had this behavior in
`apps/cli/src/runtime/tool-policies.ts` via `SAFE_AUTO_APPROVE_TOOLS`.
The current code already has the important approval-controller behavior
where per-tool `autoApprove: true` approves directly and per-tool
`autoApprove: false` can override global auto-approve. The missing part
was simply that the CLI no longer injected explicit safe-tool policies
when auto-approve-all was off.

Testing:
- `bun run vitest run src/runtime/tool-policies.test.ts
src/runtime/interactive/approvals.test.ts` from `apps/cli`
- `bun run typecheck` from `apps/cli`
- `bunx biome check apps/cli/src/runtime/tool-policies.ts
apps/cli/src/runtime/tool-policies.test.ts`
- `git diff --check`
- `rg -n "—|\\*\\*|as any" apps/cli/src/runtime/tool-policies.ts
apps/cli/src/runtime/tool-policies.test.ts`

Note:
The pre-commit hook still runs root `bun run types`, which fails on the
unrelated existing `settingsKey` errors in enterprise/plugin code before
this change's package typecheck can complete. I committed with hooks
skipped after the focused CLI typecheck and tests passed.
2026-04-30 14:05:11 -07:00
Saoud Rizwan 5081125d38 fix(cli): detect npm wrapper for auto updates (#323)
## Summary

This fixes CLI self-update detection for the published npm install path
and adds `clite --update` as a root-level alias for the existing `clite
update` command.

The CLI already had auto-update logic and package-manager handling, but
the published package does not execute the app directly from the npm
wrapper. Users install `@clinebot/cli` globally, run `clite`, then the
Node wrapper launches the compiled Bun binary. Inside that compiled
binary, `process.argv[1]` is a virtual `/$bunfs/...` path instead of
`.../node_modules/@clinebot/cli/bin/clite`, so the existing updater
could not infer the npm install path and reported `Package manager:
unknown` in a real wrapper-style install layout.

## Technical approach

The npm wrapper now resolves its own real path and passes it to the
compiled child process as `CLITE_WRAPPER_PATH`. The updater now prefers
that wrapper path before falling back to `process.argv[1]`, so the
existing install detection logic can continue to map npm, pnpm, yarn,
bun, and npx-style paths to the correct update command.

This keeps the package-manager handling centralized in
`commands/update.ts` and avoids redesigning the update flow. The only
new signal is the wrapper path handoff from `bin/clite` to the compiled
binary.

The root `--update` flag is intentionally simple. It calls the same
`checkForUpdates()` path as `clite update`, forwarding `--verbose` when
present, and returns before loading runtime modules.

## Debugging notes

I reproduced the problem by building the current-platform compiled
package, assembling an isolated npm-global-style layout with the wrapper
package and platform package under a temp prefix, and running `clite
update --verbose`. Before the wrapper path handoff, the CLI printed
`Package manager: unknown`. After the change, the same isolated layout
prints `Package manager: npm`.

The key discovery is that the old `workspace/cline/cli` implementation
worked because that CLI runs directly from an installed JavaScript
entrypoint, so `process.argv[1]` is a real npm package path. This SDK
CLI runs through a Node wrapper into a Bun-compiled binary, so the
installed wrapper path has to be passed explicitly.

## Tests

- `bun -F @clinebot/cli test:unit -- src/commands/update.test.ts
src/commands/bin-wrapper.test.ts src/main.test.ts`
- `bun -F @clinebot/cli typecheck`
- `bun apps/cli/script/build.ts --single --skip-sdk-build`
- isolated npm-global-style `clite update --verbose` simulation with
fake `npm` first on `PATH`
- `bun -F @clinebot/cli build`
- `bun -F @clinebot/cli test:unit`
- `sleep 1 && git diff --check`

Note: the pre-commit hook runs root `bun run types`, which is currently
blocked by unrelated `AgentExtension.settingsKey` type errors in core
and enterprise paths. The commit was created with `--no-verify` after
the targeted CLI checks above passed.
2026-04-30 13:55:05 -07:00
Saoud Rizwan 7b2aecdfa0 fix(cli): improve fork session ux (#321)
## Summary

This PR improves the CLI `/fork` experience as a separate change from
the resume/history metadata fixes.

The main user-facing changes are:

- Forked sessions get a clearer title with a `(fork)` suffix so they are
easier to distinguish in history.
- Fork metadata is built in a dedicated `runtime/interactive/fork`
module instead of being mixed into the main interactive session runtime.
- The fork confirmation and related command copy now explains that
`/history` can be used to switch between sessions.
- The slash command description, welcome copy, help text, and chat
command parser wording now describe fork behavior more clearly.
- The history dialog can surface fork provenance so a forked session is
visually distinct from its source session.

## Technical approach

I pulled the fork-specific title and metadata behavior out of
`session-runtime.ts` into small helpers under
`apps/cli/src/runtime/interactive/fork/`. The runtime now delegates fork
title generation and fork metadata construction to those helpers, which
keeps session switching and runtime lifecycle code focused on
orchestration instead of UI naming rules.

The title helper keeps existing task titles readable while adding a
consistent `(fork)` marker. The metadata helper carries forward the
existing session metadata while recording fork provenance such as the
source session id, fork time, source, and checkpoint metadata when
present.

The surrounding TUI text was updated so users understand that forking
copies the current session into a new session and that `/history` is the
way to switch back to other sessions.

## Debugging notes

This started because forked sessions were too hard to identify in
`/history`, and the fork copy did not make the session relationship or
switching workflow clear. The first version put more fork-specific logic
directly in the interactive runtime, but that made `session-runtime.ts`
harder to scan and mixed naming policy with session lifecycle. Moving
fork helpers into their own directory makes this easier to review and
gives the fork behavior focused unit coverage.

## Testing

- `bun run --cwd apps/cli typecheck`
- `bun run --cwd apps/cli test:unit --
src/runtime/interactive/fork/metadata.test.ts
src/runtime/interactive/fork/title.test.ts
src/utils/chat-commands.test.ts src/commands/history.test.ts`
- `bun run --cwd apps/cli test:unit`
- Biome check on changed CLI files
- commit hook ran root `bun run types` and Biome on staged files when
the commit was created
2026-04-30 13:29:03 -07:00
Max 544e3aa240 publish sdk migration branch to nightly main channel (#10485)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-04-30 12:41:43 -07:00
Robin Newhouse aa72700794 fix(llms): avoid effort for manual Anthropic thinking [ENG-1827] (#299)
## Summary
- Route Claude models through a centralized Anthropic reasoning policy
instead of scattered adaptive/manual checks.
- Use models.dev-derived capability/family metadata first, with
contained model-id/version parsing only where models.dev does not expose
the Anthropic thinking wire format.
- For manual-thinking Claude models (Sonnet 4.5, Haiku 4.5, Opus 4.5,
lower reasoning models), map reasoning effort to `budgetTokens` /
`max_tokens` instead of emitting `effort`.
- Keep `effort` / adaptive thinking only for adaptive Claude models
(Sonnet 4.6, Opus 4.6+, Opus 4.7+, and future Claude major versions).
- Suppress generic OpenAI-compatible `effort` / adaptive thinking for
Anthropic-compatible manual-thinking models, including
Cline/OpenRouter-routed Claude 4.5 models.

## Context
Sonnet 4.5 requests with `--thinking --reasoning-effort low` were
failing on direct Anthropic with `This model does not support the effort
parameter.` Runtime request logging showed the outbound Anthropic body
had the right manual thinking shape:

```json
{"thinking":{"type":"enabled","budget_tokens":1024}}
```

but also included the invalid field:

```json
{"output_config":{"effort":"low"}}
```

`@ai-sdk/anthropic` maps `providerOptions.anthropic.effort` to
`output_config.effort`. That is valid for adaptive-thinking Claude
models, but invalid for manual-thinking models like Sonnet 4.5 and Haiku
4.5.

## Why this fell through
The routing layer previously treated Anthropic-compatible reasoning
effort as broadly reusable across provider buckets. That works for many
OpenAI-compatible providers, but Claude has multiple thinking request
shapes:

```text
Adaptive models:
  thinking: { type: "adaptive" }
  output_config: { effort: "low" | "medium" | "high" }

Manual models:
  thinking: { type: "enabled", budget_tokens: N }
  no output_config.effort
```

models.dev tells us whether a model has `reasoning` and gives us family
metadata, but it does not currently distinguish Anthropic
manual-vs-adaptive thinking request shape. This PR keeps that inference
centralized in one policy resolver.

## Fix
- Added `resolveAnthropicReasoningRequestPolicy`, returning `none`,
`anthropic-manual`, or `anthropic-adaptive`.
- Derived the policy from catalog capabilities, family metadata, and
contained Claude version parsing.
- For manual Claude thinking, emit token budgets and suppress `effort`
in Anthropic/OpenAI-compatible buckets.
- For adaptive Claude thinking, preserve adaptive `thinking` and
`effort` behavior.
- Removed the previous direct Anthropic fetch sanitizer; request options
are now built correctly up front.

## Test plan
- `bun run test` in `packages/llms`
- `bun run typecheck` in `packages/llms`
- Pre-commit hook ran `bun run types` and Biome on staged files
- Added/updated coverage for:
  - Sonnet 4.5 / Haiku 4.5 / Opus 4.5 manual thinking
  - Sonnet 4.6 / Opus 4.6+ / Opus 4.7 adaptive thinking
  - future Claude major versions defaulting to adaptive thinking
  - date-suffixed Claude IDs not being mistaken for adaptive versions
  - non-reasoning Claude capability gating
- Cline/OpenRouter-routed Claude 4.5 suppressing `effort` and adaptive
thinking

---------

Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>
2026-04-30 10:46:54 -07:00
Dominic Cooney beb3ad78dc Revert "Remove foreground terminal from Cline VSCode extension (#10196)" (#10477)
This reverts commit 1862f15955.
2026-04-30 09:41:52 -07:00
Saoud Rizwan 0c8ec3e9c7 fix(cli): improve account onboarding flow (#319)
This fixes the fresh interactive CLI account onboarding path and
tightens the Cline account dialog behavior. A fresh temp config with the
default Cline provider was starting browser OAuth before the TUI had a
chance to render onboarding. The TUI already has first-run onboarding
logic based on provider readiness, but main.ts was calling
ensureOAuthProviderApiKey first for OAuth-capable providers when no
token was present.

<img width="648" height="459" alt="Google Chrome 2026-04-29 17 59 10"
src="https://github.com/user-attachments/assets/9c396d13-cd75-48dd-947e-10701cfa517f"
/>


- Skip pre-TUI OAuth bootstrap for interactive startup so onboarding can
own first-run auth and provider setup.
- Keep the /account command visible for all users, including logged-out
and non-Cline-provider states.
- Remove the showClineAccountCommand and showAccountCommand plumbing now
that account is always a valid local TUI command.
- Update the logged-out Cline account dialog to show Sign in or create
account and Learn more. Learn more opens https://cline.bot.
- Add Change provider to the loaded account dialog, routed through the
existing provider picker path.
- Fix Cline credit display by consistently treating account balances as
micro-credit units, so values like 500000 render as $0.50 rather than
$500000.

Debugging notes

The issue showed up while testing fresh config with:

```sh
bun run dev -- --data-dir "$(mktemp -d)" -i
```

Instead of onboarding, the CLI printed the Cline auth URL. That traced
back to the bootstrap auth gate in main.ts. The appView onboarding check
in root.tsx was correct, but unreachable because OAuth started before
renderOpenTui ran. The fix is intentionally narrow: headless and
non-interactive flows still avoid TUI, while interactive startup now
reaches the TUI with an empty apiKey and lets onboarding handle setup.

Gotchas

The first commit attempt without --no-verify was blocked by the repo
pre-commit hook running all package typechecks. The failure was
unrelated to this CLI change: @clinebot/menubar typecheck reported
packages/core/src/services/global-settings.ts(116,8) has an unused
settings variable. I committed with --no-verify after running the
CLI-focused checks below.

Testing

```sh
bun -F @clinebot/cli typecheck
bun -F @clinebot/cli test:unit
bun -F @clinebot/cli build
bun biome check apps/cli/src/main.ts apps/cli/src/main.test.ts apps/cli/src/tui/cline-account.ts apps/cli/src/tui/commands/slash-command-registry.ts apps/cli/src/tui/commands/slash-command-registry.test.ts apps/cli/src/tui/components/dialogs/account-dialog.tsx apps/cli/src/tui/components/dialogs/help-dialog.tsx apps/cli/src/tui/hooks/use-account-dialog.tsx apps/cli/src/tui/hooks/use-local-command-actions.tsx apps/cli/src/tui/hooks/use-slash-commands.ts apps/cli/src/tui/root.tsx apps/cli/src/utils/output.ts apps/cli/src/utils/output.test.ts
git diff --check
```

I also smoke-tested the temp-config interactive command from this
non-TTY tool shell. It now reaches the expected TTY preflight instead of
printing the auth URL, which confirms the pre-TUI OAuth path is no
longer firing.
2026-04-30 05:16:22 +02:00
Bee e6b976075b feat: add enable/disable controls for tools and plugins (#318)
<img width="2056" height="1298" alt="image"
src="https://github.com/user-attachments/assets/f5d1b353-c428-4279-9fda-1158f03d7976"
/>

In Code App: Expose sidecar commands to set disabled tools and plugins,
read global
settings when listing configs, and return accurate enabled state for
built-in and plugin tools.

Update the rules UI to toggle tool and plugin availability directly so
users can manage active instruction sources from the configuration view.

Add same feature to clite config view
2026-04-29 17:46:05 -07:00
Saoud Rizwan 1ec60657dc fix(core): observe public run promise during abort (#312)
## Problem

Renee reported that after cancelling a request mid-run in `apps/cli`,
the hub seemed to die. The next request failed with a WebSocket error
like:

```text
WebSocket connection to ws://127.0.0.1:xxxx/hub failed: Failed to connect
```

The short version: cancellation could make the hub daemon think an
expected abort rejection was an unhandled crash, so the daemon exited.
After that, the CLI tried to reconnect to the old hub port and got the
WebSocket failure.

## What was happening

The hub flow looks like this:

```text
1. CLI sends: start this run
2. Hub daemon starts the run and returns a promise for it
3. User hits cancel
4. CLI sends a separate: abort that run
5. Abort makes the run promise reject
6. If Node/Bun thinks nobody is handling that rejection, the daemon's unhandledRejection handler kills the process
7. The next CLI request tries to connect to the old hub port and fails
```

There was already defensive code in `SessionRuntime.abort()` for this:

```ts
void this.activeRunPromise.catch(() => {});
```

That catch is intentionally not application-level error handling. It
just tells the process: this rejection can be expected during
cancellation, so do not classify it as an unhandled crash. Awaiting
callers should still receive the same rejection or result.

The subtle bug was promise identity.

Before this PR, `run()` and `continue()` were `async` wrappers. That
means this shape:

```ts
async run() {
  return this.executeRun();
}
```

Even if `executeRun()` returns promise A, `async run()` returns a
different wrapper promise B that mirrors promise A.

So the runtime was observing promise A with
`activeRunPromise.catch(...)`, but the caller actually held promise B.
During fast cancellation timing, promise B could reject before the
caller awaited it, and the daemon could still see an unhandled
rejection.

## Fix

This PR makes the tracked promise and the returned promise be the same
promise.

`run()` and `continue()` now return directly instead of creating `async`
wrapper promises:

```ts
run() {
  return this.executeRun(...);
}
```

And `executeRun()` stores the same promise it returns:

```text
activeRunPromise === the promise returned to the caller
```

Now when abort attaches the existing catch observer to
`activeRunPromise`, it is observing the exact promise that can reject
during cancellation. That closes the timing gap without adding retry
logic, hiding the abort, or changing what callers receive when they
await the run.

## Regression test

I added a focused test for the failure shape:

1. Start a run.
2. Wait until the fake runtime is active.
3. Abort the run.
4. Let a tick pass before awaiting the returned promise.
5. Assert no `unhandledRejection` was observed.
6. Assert the returned promise still rejects with the original
cancellation error.

That test failed before the fix because the public wrapper promise
triggered `unhandledRejection`. It passes after this change.

## Why this belongs in core

The CLI is where the user sees the broken behavior, but the promise
lifecycle lives in the daemon-side core session runtime. Any hub-backed
caller that aborts an in-flight run could hit the same timing issue, so
the fix belongs in `packages/core` rather than in CLI retry or discovery
code.

I also avoided changing hub retry or stale discovery handling here.
Restarting or rediscovering the hub might mask the symptom, but it would
not address the daemon exit caused by cancellation.

## Verification

Commands run:

```sh
bunx vitest run src/runtime/orchestration/session-runtime-orchestrator.test.ts --config vitest.config.ts
bunx vitest run src/hub/client/index.test.ts src/hub/daemon/index.test.ts src/hub/server/browser-websocket.test.ts --config vitest.config.ts
bun run typecheck
bun run test:unit
```

Additional commit hook verification also ran:

```sh
bun run types
bun biome check --no-errors-on-unmatched --files-ignore-unknown=true
```

The full core unit suite passed with 87 test files, 773 passing tests,
and 3 skipped tests. The focused CLI abort/runtime tests also passed.

## Remaining risk

I did not perform a live provider-backed manual cancellation test. The
regression now covers the promise timing failure that can kill the hub
daemon, but a live run would still be useful for confidence around
provider stream behavior and user-facing CLI recovery.
2026-04-29 17:16:58 -07:00
Saoud Rizwan a9252128b7 fix(cli): style markdown tokens (#316)
## Summary

Adds syntax styles for markdown token scopes in the TUI syntax
highlighter so that rendered markdown in the chat is properly styled
instead of appearing as plain unstyled text.

Covers headings (h1-h6), inline code (`markup.raw`, `markup.raw.inline`,
`markup.raw.block`), bold, italic, blockquotes, list markers, links
(including label and URL sub-scopes), and a few related scopes like
`conceal`, `label`, and `string.special.url`. The colors follow the
existing One Dark-inspired palette already used for code syntax -- green
for inline code, cyan for headings/links/bold, yellow for italic, gray
for quotes/conceal.

Also adds a test file (`syntax-style.test.ts`) that mocks
`@opentui/core`'s `RGBA` and `SyntaxStyle` classes to verify the token
scopes are correctly mapped.

## Test plan

- [ ] Verify markdown in CLI chat renders with styled headings, inline
code, bold/italic, links, and quotes
- [ ] Run `vitest` and confirm `syntax-style.test.ts` passes
2026-04-29 16:55:46 -07:00
Saoud Rizwan bee3d2415a fix(cli): scroll chat to bottom on submit (#315)
## Summary

When the user submits a message, the chat scrollbox now programmatically
scrolls to the bottom. Previously the `stickyScroll` prop handled this
in most cases, but there was a timing issue where new content wouldn't
trigger a scroll if the layout hadn't settled yet.

The fix uses a `userSubmissionScrollKey` derived from checking if the
last entry is `user_submitted`, which triggers a `useEffect` that calls
`scrollbox.scrollTo(scrollbox.scrollHeight)`. It fires the scroll three
ways -- synchronously, via `queueMicrotask`, and via `setTimeout(_, 0)`
-- to cover different layout timing scenarios. The ref is typed as
`ScrollBoxRenderable` from `@opentui/core`.

## Test plan

- [ ] Submit a message in the CLI chat and verify it scrolls to the
bottom
- [ ] Verify scrolling still works correctly when the chat history is
long enough to overflow
- [ ] Verify stickyScroll still works during streaming (assistant
typing)
2026-04-29 16:55:31 -07:00
Saoud Rizwan e2dbfa9dcb chore(cli): release v0.0.6 2026-04-29 15:05:05 -07:00
Saoud Rizwan e613ce6b1b feat(cli): add checkpoint restore via Esc Esc and /undo (#304)
This adds the ability to restore to a previous checkpoint in the CLI
interactive mode. The SDK already had a complete checkpoint system that
snapshots workspace state (via git stash/commit) at the start of each
user turn, but there was no way to trigger a restore from the CLI. The
webview had per-message "Undo" buttons, but the CLI had nothing.

Now users can press Esc twice in quick succession (or type `/undo`) to
open a checkpoint picker, select a previous user message, choose whether
to restore chat only or chat + workspace, and land back in the editor
with that message pre-populated in the input field ready to edit and
re-send.

<img width="1148" height="542" alt="image"
src="https://github.com/user-attachments/assets/93301ba7-425b-4f64-beb3-b44cb88d3750"
/>


## The user flow

1. User presses Esc Esc (300ms window) while the agent is idle, or types
`/undo`
2. A picker dialog opens showing all previous user messages that have
checkpoint data, in chronological order (oldest top, newest bottom),
cursor starting at the bottom. Each entry shows a truncated message
preview and a relative timestamp ("2m ago", "1h ago")
3. User navigates with arrow keys, presses Enter to select
4. A confirmation dialog opens with two options:
- "Restore chat only" -- rewinds conversation history, keeps current
files
- "Restore chat and workspace" -- rewinds conversation AND resets files
via git (shows a warning that this runs `git reset --hard` and `git
clean -fd`)
5. On confirmation, the restore executes. The conversation rewinds to
show everything before the selected message, and the selected message's
full text is placed into the input field so the user can edit and
re-send it. This is the key UX insight: the whole point of undoing is to
say something different, so the message goes straight into the compose
box rather than being displayed as a sent message

## Why the SDK needed changes

The initial implementation attempt just called `ClineCore.restore()`
from the CLI and it "worked" for a single restore. But two problems
surfaced immediately when you tried to restore a second time:

### Problem 1: Checkpoint history was lost across restores

`ClineCore.restore()` internally calls `this.start()` to create a new
forked session with the trimmed messages. But the new session started
with empty checkpoint metadata -- the old session's checkpoint entries
weren't carried forward. So after restoring to message 3 of 5, the new
session had messages 1-3 but zero checkpoint history. Pressing Esc Esc
again showed nothing.

The fix: `ClineCore.restore()` now calls
`createRestoredCheckpointMetadata()` to extract checkpoint entries from
the source session (filtered to `runCount <= target`), and passes them
as `sessionMetadata.checkpoint` to the new session. It also calls
`retainCheckpointRefs()` after starting the new session to re-anchor the
carried-over git stash/commit objects under the new session's ref
namespace (`refs/cline/checkpoints/{newSessionId}/...`). Without this,
the objects would only be reachable via the old session's refs and could
be lost if that session is cleaned up.

### Problem 2: New checkpoints got wrong runCount values

The checkpoint hooks maintain an internal `runCount` counter that starts
at 0 and increments on each root-level run. When a restored session
starts with N user messages from `initialMessages`, the hooks don't know
about them -- the first actual run gets `runCount=1` instead of
`runCount=N+1`. This means the checkpoint entry for the user's new
message doesn't match the message's position in the session, so it never
shows up in the picker.

The fix: `createCheckpointHooks` now accepts an `initialRunCount`
option. The bootstrap layer (`local-runtime-bootstrap.ts`) derives this
automatically by counting user messages in `initialMessages` via
`countSeededRootRuns()`. This uses the same counting logic as
`trimMessagesToCheckpoint` (count user-role messages, skip
`recovery_notice` metadata). The hooks start their counter at this
value, so the first new run gets `runCount=N+1` which correctly maps to
the (N+1)th user message.

This also required `upsertCheckpointHistory()` in the hooks -- when a
restored session creates a checkpoint for run N (which already exists in
the carried-over history), it replaces the existing entry in-place
rather than appending a duplicate.

### Problem 3: The CLI needed the message without storing it

The CLI wants to put the restored user message into the input field for
editing. But `ClineCore.restore()` stores the trimmed messages
(including that user message) as the new session's `initialMessages`. If
the user just hits Enter without editing, the message would be sent
again and the session would have it twice -- once from `initialMessages`
and once from the new send.

The fix: a new `omitCheckpointMessageFromSession` option on
`RestoreOptions`. When set, `ClineCore.restore()` uses
`trimMessagesBeforeCheckpoint()` (which slices to just before the Nth
user message) for `initialMessages`, while still returning the full
trimmed messages (through the user message) in `result.messages`. The
CLI gets the message text for the input field from the result, but the
session's stored history doesn't include it.

`trimMessagesBeforeCheckpoint` shares the index-finding logic with
`trimMessagesToCheckpoint` via an extracted `findCheckpointMessageIndex`
helper.

## How to test

1. Start a CLI session in a git repo, send 2-3 messages that cause file
changes
2. Press Esc Esc -- checkpoint picker should open showing all user
messages
3. Select a message, confirm with "Restore chat and workspace" --
conversation rewinds, files reset, selected message appears in input
field
4. Edit the message and re-send -- agent processes the new version
5. Press Esc Esc again -- should see all messages including the ones
from before the first restore
6. Try `/undo` -- same flow
7. Verify Esc Esc does nothing while agent is running (single Esc still
aborts)
8. Verify "Restore chat only" rewinds conversation but leaves files
untouched
2026-04-29 15:01:56 -07:00
John Choi edc57d4406 fix(cli): fall back to system clipboard for TUI selections (#302)
## Summary
- Add a system-clipboard fallback for Cline TUI text selections when
OpenTUI's `copyToClipboardOSC52` returns `false`.
- Platform support:
- **macOS** — `pbcopy`, with `LANG`/`LC_CTYPE` forced to `en_US.UTF-8`
and `LC_ALL` cleared so non-ASCII selections survive non-UTF-8 parent
locales.
- **Windows / WSL1 / WSL2** — `powershell.exe` (then `pwsh.exe`) running
`Set-Clipboard` with `[Console]::InputEncoding` set to UTF-8 and
`-ExecutionPolicy Bypass` so locked-down hosts still allow the inline
`-Command`. Round-trip tested with emoji + CJK + accented characters.
  - **Linux (non-WSL)** — `wl-copy`, then `xclip -selection clipboard`.
- Keeps the existing OSC52 path as the first attempt for terminal-native
clipboard support (works over SSH).

## Root cause: 1024-byte fixed buffer in OpenTUI's Zig core

OpenTUI builds the entire OSC 52 escape sequence in a **stack-allocated
fixed-size 1024-byte buffer** before writing it to the TTY. From
[`packages/core/src/zig/terminal.zig` in
`anomalyco/opentui@main`](https://github.com/anomalyco/opentui/blob/main/packages/core/src/zig/terminal.zig#L466-L482):

```zig
pub fn writeClipboard(self: *Terminal, tty: anytype, target: ClipboardTarget, payload: []const u8) !void {
    if (!self.canWriteClipboard()) {
        return error.NotSupported;
    }

    var buf: [1024]u8 = undefined;                       // ← THE FIXED CAP
    var stream = std.io.fixedBufferStream(&buf);
    const writer = stream.writer();

    // Build OSC 52 sequence: ESC]52;<target>;<payload>ESC\
    try writer.writeAll("\x1b]52;");      // 5 bytes
    try writer.writeByte(target.toChar());// 1 byte (e.g. 'c')
    try writer.writeByte(';');            // 1 byte
    try writer.writeAll(payload);         // base64-encoded selection
    try writer.writeAll("\x1b\\");        // 2 bytes (string terminator)
    ...
}
```

### What that means in practice

- Frame overhead: `ESC]52;c;` + `ESC\` = **9 bytes**.
- Available payload: `1024 − 9 = 1015 bytes` of base64.
- Base64 expands input by 4/3, so the **maximum copyable selection is
`⌊1015 × 3 / 4⌋ ≈ 761 bytes`** of UTF-8 source text — less for
multi-byte (emoji/CJK) selections, and even tighter inside tmux/screen,
where every `ESC` is doubled by the tmux/screen DCS wrapping (`[2048]u8`
/ `[4096]u8` second-stage buffers).
- When `writer.writeAll(payload)` would overrun the stream, Zig's
`fixedBufferStream` returns `error.NoSpaceLeft`. The `try` propagates
that error up; the FFI surface in
[`lib/clipboard.ts`](https://github.com/anomalyco/opentui/blob/main/packages/core/src/lib/clipboard.ts)
catches it and returns `false` to JS. That is exactly the `false` we now
treat as a fallback signal.

### Why this hits users

A copy of a single moderately long line of code, a stack trace, or a
multi-line selection of agent output in the Cline TUI quickly exceeds
~760 bytes. Without this PR, those selections silently fail OSC 52
inside iTerm (and any other terminal that respects payload size limits)
with a misleading "Unable to copy selection" toast — even though the
system clipboard is fully available.

## Behavior
- **Latest-selection-wins.** Each new TUI selection aborts the previous
fallback via `AbortSignal`, so a slow async copy can't overwrite the
clipboard with stale text after a newer selection. On unmount the
in-flight copy is also aborted.
- **SSH-aware.** When `SSH_CONNECTION` / `SSH_CLIENT` / `SSH_TTY` is
set, the system fallback is skipped — OSC52 is the right path for
remote→local clipboard, and the fallback would otherwise write to the
remote machine's clipboard.
- **Per-command timeout** of 1.5s with `child.kill()` so a hung utility
(`pbcopy`/`xclip`/`wl-copy`/PowerShell) doesn't leak a process or block
the TUI.
- **Defensive against missing utilities** — `spawn` errors and `null`
`child.stdin` are treated as failed and the next fallback is tried.

## Environment variables (opt-in)
- `CLINE_CLIPBOARD_FALLBACK_REMOTE=1` — re-enables the system fallback
inside SSH sessions for users who explicitly want the remote machine's
clipboard.
- `CLINE_DEBUG_CLIPBOARD=1` — emits `console.debug` traces when the SSH
skip kicks in or a command fails. Off by default so the TUI canvas stays
clean; useful for support / triage.

## Test plan
- `bun run test:unit -- src/tui/utils/clipboard.test.ts
src/tui/utils/selection-copy.test.ts` (16 + 8 = 24 cases)
- Coverage includes: empty input, macOS UTF-8 env scrubbing, Windows +
WSL2 + WSL1 PowerShell with Unicode round-trip, `powershell.exe` →
`pwsh.exe` fallback, non-WSL Linux fallback chain, SSH skip + opt-in,
AbortSignal mid-flight, already-aborted signal, timeout-then-fallback,
`spawn` error fallback, null `child.stdin` fallback,
latest-selection-wins, OSC52-aborts-pending-fallback, and
dispose-aborts-in-flight.
- Manual iTerm smoke: selected >750 chars in Cline TUI and verified
paste with `pbpaste`.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-04-29 13:48:56 -07:00
Mikołaj Kondratek ee1d4b4dcf CLINE-1814 typed RipgrepSpawnError + error_reason proto (#10443)
* CLINE-1814 typed RipgrepSpawnError + error_reason proto

* file-search.ts: define RipgrepSpawnError carrying stderr+exitCode; reject
  on non-zero ripgrep exit (with empty results) instead of resolving to []
* file-search.ts: re-throw from searchWorkspaceFiles and
  searchWorkspaceFilesMultiroot so the controller sees the error and can
  attach a structured error_reason to the proto response
* file-search.test.ts: assert spawn-time and exit-time errors both surface
  as RipgrepSpawnError
* file.proto/FileSearchResults: add optional error_reason and error_message
  fields with the closed enumeration of values documented inline

Phase 1 of the visibility patch. No behaviour change for healthy installs;
broken installs now surface a real error instead of an empty list.

Refs: CLINE-1814

* CLINE-1814 surface error_reason in picker UI

Controller (searchFiles.ts):
* classify thrown errors into a closed enumeration of error_reason values
  (workspace_unavailable, ripgrep_spawn_failed, unknown). RipgrepSpawnError
  unwraps the first line of stderr into error_message so the picker can
  surface ENOENT / EACCES / 'Operation not permitted' verbatim.
* the no-workspace-path branch now returns workspace_unavailable instead
  of an empty result list.
* keep using telemetry.captureMentionFailed for aggregate signal but map
  ripgrep_spawn_failed -> 'unknown' to stay within the existing closed enum.

Webview (ChatTextArea + ContextMenu):
* ChatTextArea threads errorReason / errorMessage from each searchFiles
  RPC response (and from RPC-level rejections) into ContextMenu.
* ContextMenu renders a grey, italic, smaller subtitle beneath the
  'No results found' row when an error_reason is present.
* renderErrorSubtitle() carries a short doc-comment for each value so
  reviewers can see at a glance how each error_reason maps to UI copy.

Refs: CLINE-1814

* CLINE-1814 trim verbose CLINE-1814 ticket-reference comments

Pure code-quality pass over the Phase 1 changes: condense the long
narrative comments that referenced the ticket into terser explanatory
comments where they still add value, and remove ones that just
repeated what the (now-stable) code already says. No behaviour change.

* CLINE-1814 fix RipgrepSpawnError override of Error.cause

TS error 'This member must have an override modifier because it
overrides a member in the base class Error' - Error gained an optional
'cause' field in ES2022. Drop the explicit field declaration and pass
the cause via the standard ES2022 ErrorOptions in super(). Behaviour
unchanged: instances still expose .cause via the base-class field.

* CLINE-1814 fix Windows race in executeRipgrepForFiles finalisation

CI hit this on Windows:

  AssertionError: expected [Promise] to be rejected with a message
  matching /ripgrep exited with code 2/, but got 'ripgrep exited with
  code null: rg: /bogus: No such file or directory (os error 2)'

The readline 'close' event and the child-process 'exit' event fire in
non-deterministic order on Windows. The previous code keyed off 'close'
alone, which meant the rejection branch could run with exitCode still
null even when the process eventually exited with a real code.

Fix: gate finalisation on both events with a small barrier (rlClosed +
processExited + finalised flags). Idempotent and safe under any
ordering. Test passes on darwin (where the ordering used to be benign)
and the same path now produces the expected exitCode=2 message on
Windows.

No production-behaviour change on the happy path: results still resolve
exactly when the readline finishes parsing stdout.

* CLINE-1814 address Phase 1 code-review feedback

Three small follow-ups from review on the Phase 1 PR:

1) ContextMenu.tsx: drop a stray trailing semicolon on the
   selectedType prop declaration so the interface style stays
   consistent with the rest of the file (no semicolons on field
   declarations). Cosmetic only, no behaviour change.

2) file-search.ts: in executeRipgrepForFiles' rgProcess.on('error')
   handler, set finalised = true before reject() so that a subsequent
   ('close', 'exit') pair can't pass the finalise() guards. The
   double-reject was already a no-op (Promises swallow further
   reject() calls once settled), but unconditionally maintaining the
   barrier invariant makes the lifecycle of this Promise much easier
   to reason about and matches the symmetry of the other two
   finalise() callers.

3) file-search.test.ts: collapse the awaited-twice rejected-promise
   pattern in 'should reject with RipgrepSpawnError when ripgrep
   exits non-zero with no results'. The previous form
   (await should(p).be.rejectedWith(...); await p.catch(...)) worked
   because settled promises replay their value, but it's subtly
   misleading. The new form awaits once via .catch() and asserts on
   the resulting error directly.

All six File Search unit tests continue to pass.

* CLINE-1814 revert picker error subtitle (UI for impl-detail leak)

Per code-review feedback: surfacing structured error_reason / error_message
from FileSearchResults as a grey-italic subtitle on the 'No results found'
row exposes implementation detail to end-users. The user can't act on
'(ripgrep failed: rg: ENOENT)' or '(internal error: spawn EACCES)' — those
are diagnostic data that belong in logs and aggregate telemetry.

Reverted in this commit:
  - ContextMenu.tsx: errorReason / errorMessage props removed,
    renderErrorSubtitle helper deleted, NoResults row reverts to a plain
    <span>No results found</span>.
  - ChatTextArea.tsx: searchErrorReason / searchErrorMessage state and
    all setSearchErrorReason / setSearchErrorMessage call-sites removed;
    ContextMenu invocation no longer passes the two props.

Kept on purpose:
  - The proto field FileSearchResults.error_reason — it's harmless on
    the wire and the next commit wires it up to telemetry + structured
    logging, which is where this signal actually belongs.
  - The classifyError helper and ERROR_REASON_* constants in the
    searchFiles controller — same reason, they feed telemetry next.

Six file-search unit tests still pass.

* CLINE-1814 telemetry: surface ripgrep_spawn_failed / workspace_unavailable

Until now the searchFiles controller's catch block collapsed every classified
error_reason — workspace_unavailable, ripgrep_spawn_failed, unknown — onto a
two-value telemetry enum (permission_denied | unknown), throwing away the
diagnostic signal we'd worked hard to extract. The Linear ticket explicitly
asks for the structured signal to feed telemetry; this commit delivers on that.

Changes:
  - TelemetryService.captureMentionFailed: extend the errorType enum with
    two new categorical values, ripgrep_spawn_failed and workspace_unavailable.
    Doc-comment updated to call out that those two are picker-search failures
    (vs the existing values which are mention-content retrieval failures).
  - searchFiles.ts:
      * empty-workspace branch now emits errorType=workspace_unavailable
        (previously: not_found).
      * catch-block computes errorType from the classified errorReason —
        RipgrepSpawnError -> ripgrep_spawn_failed, EACCES -> permission_denied,
        otherwise unknown — instead of always permission_denied | unknown.

Net result: ops can now distinguish 'ripgrep is broken on this user's
machine' from 'user is in a one-window-no-folder IntelliJ session' from
genuine code bugs in the search pipeline.

* CLINE-1814 log: include classified errorReason on searchFiles error line

Trivial follow-up to the previous commit. Triagers grepping
~/.cline/cline-core-service.log for searchFiles failures got the raw error
object dumped, but no hint as to which of the structured error_reason
buckets the failure falls into. Now the log line is

    [ERROR] Error in searchFiles (errorReason=ripgrep_spawn_failed): <Error...>

so a single grep for 'errorReason=ripgrep_spawn_failed' surfaces every
ripgrep-side failure across the user's session without having to read the
stack trace. Same field value as the gRPC response and the telemetry event,
so the three sources can be cross-referenced in incident triage.

Also moved the classifyError() call above the Logger.error() call (was
below) so the reason is computed once, used twice.

* CLINE-1814 address Phase 1 review feedback (rename, simplify, trim comments)

Five review comments rolled into one commit:

file-search.ts
  * Rename RipgrepSpawnError -> RipgrepError. The class covers spawn failures
    AND non-zero-exit / stderr-on-empty-stdout paths; the old name only
    described half its job.
  * Drop the unread 'cause' constructor option. We were never reading
    err.cause anywhere downstream, and the only producer was the
    spawn-error path which already encodes the underlying message in the
    string.
  * Replace the rlClosed/processExited/finalised state machine with two
    Promise resolvers awaited via Promise.all. Same ordering guarantees on
    Windows (both 'close' and 'exit' must fire before we settle), zero
    mutable bookkeeping, and the spawn-error path no longer needs to
    pre-flip a 'finalised' flag to be safe against a late close+exit pair.
    Used new Promise() rather than Promise.withResolvers() because TS lib
    is es2022 and withResolvers is es2024; behavior is identical.

searchFiles.ts
  * .trim() the stderr before split() so a leading newline doesn't yield
    an empty first line on the telemetry / log path.
  * Drop the 'this commit' comment (ephemera once 'this commit' is no
    longer the most recent one) and the 'Determine mention type based on
    the search request' comment, both of which restated the obvious code.

file-search.test.ts
  * Update test name and assertion to match the renamed class.

All 6 file-search unit tests pass.

* CLINE-1814 drop unread RipgrepError.exitCode field

Follow-up to the previous review-feedback commit. The 'we're not reading
this anywhere' note was about exitCode, not cause - my mistake. The exit
code is already encoded into the error message string ('ripgrep exited
with code N: <stderr>'), so the dedicated field was carrying no
additional information for any consumer.

Dropped:
  * RipgrepError.exitCode field and constructor option
  * 'exitCode' from the two new RipgrepError(...) call sites
  * 'should(err).have.property(exitCode, 2)' from the unit test

The internal exitCode local in executeRipgrepForFiles stays - it gates
the reject vs resolve decision after both 'close' and 'exit' have fired.
It's just no longer plumbed onto the error.

All 6 file-search unit tests still pass.
2026-04-29 10:42:48 -07:00
Mikołaj Kondratek 71cb451fdc chore(ci): pass NPM_CONFIG_TOKEN to bun publish steps (#305)
**Publish SDK Packages** fails:
https://github.com/cline/sdk-wip/actions/runs/25078244516/job/73478108668

This PR is a fix for that.

---

bun publish reads NPM_CONFIG_TOKEN directly from the environment and
does not expand ${NODE_AUTH_TOKEN} placeholders inside the .npmrc that
actions/setup-node writes. Without this, all four publish steps fail
with 'error: missing authentication (run `bunx npm login`)'.

Hoisting the variable to the publish-sdk job covers shared, llms,
agents, and core in one place. See https://bun.sh/docs/cli/publish.
2026-04-29 18:54:21 +02:00
Saoud Rizwan e870bcde11 fix(cli): restore prompt focus after dialogs close (#303)
## Problem

The CLI prompt could lose focus after opening `/settings`, entering the
provider picker, changing provider or model, and then returning through
the dialog flow. After the final dialog closed, normal typing did not
reach the prompt input, so the TUI looked alive but the user could not
continue entering messages.

The underlying issue is focus ownership. Dialog flows temporarily take
focus, but once the dialog stack is empty the prompt textarea should own
focus again. Relying on each dialog path to remember to refocus is
fragile, especially for nested provider and model flows that open one
dialog from another.

## Technical approach

This PR makes that invariant explicit at the TUI root:

- `TextareaHandle` now includes the real OpenTUI `focus()` method.
- `usePromptInputController` exposes `focusTextarea()` without
remounting the input.
- `root.tsx` focuses the prompt textarea whenever no dialog is open and
the app is not in onboarding.

The existing `refocusTextarea()` remount behavior is left alone for the
call sites that already use it to reset textarea state. The new path is
separate and only restores focus to the current prompt textarea.

## Debugging notes

The provider and model settings flow is a multi-dialog sequence. The app
can go from settings, to provider picker, to auth or existing-provider
choice, to model picker, and then back to settings or out to the main
prompt. That stack makes focus restoration sensitive to which dialog
opened first and which renderable was saved as the previous focus
target.

The first attempted fix treated the symptom with delayed focus attempts
after dialog close. That worked locally, but it was not the right shape:
the real invariant is that the prompt should be focused whenever dialogs
are gone. This version removes the timeout and keypress recovery
behavior and puts the focus rule at the root, where both dialog state
and app view are known.

## Decisions

- Do not modify provider or model dialog sequencing. The dialogs can
remain nested because root focus ownership should handle all dialog
close paths.
- Do not add timeout-based focus restoration. The effect runs from React
state after `isDialogOpen` changes.
- Do not use keypress recovery. Typing should not be required to repair
focus.
- Keep onboarding excluded because onboarding has its own focused
controls and should not have the chat prompt stealing focus.

## Testing

- `bun -F @clinebot/cli typecheck`
- `bun biome check --diagnostic-level=error apps/cli/src/tui/root.tsx
apps/cli/src/tui/hooks/use-prompt-input-controller.ts
apps/cli/src/tui/components/input-bar.tsx`
- `git diff --check`
- Commit hook also ran `bun run types` and Biome through lint-staged.

The local interactive e2e harness was not useful in this container
because the system `script` command rejected the generated arguments
before launching the app.
2026-04-28 23:20:52 -07:00
Saoud Rizwan 35758ad15d chore(cli): release v0.0.5 2026-04-28 22:57:50 -07:00
Renee Huang 512854a3e3 move plugins to a dedicated folder (#274)
move plugins to a dedicated folder

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-04-28 22:42:08 -07:00
Bee 406defe5f5 feat: add hub session restore workflow support (#296)
Add sidecar restore handling for persisted session payloads, including
runtime options, tool policies, checkpoint config, and session creation
events.

Remove legacy checkpoint helper logic from chat-session so restore state
is derived through the shared session flow.
2026-04-28 18:16:02 -07:00
Bee 8d9d2374ad refactor: update folder structures (#289)
for core
2026-04-28 16:54:11 -07:00
TheRealSpencer 89e5cb7f63 security(core): restrict providers.json to owner-only permissions (#290)
### Description
`~/.cline/data/settings/providers.json` — which stores LLM provider API
keys and OAuth tokens — was written with default filesystem permissions
(`0644`), making it readable by any process with group access on the
same system. On developer machines, which regularly execute untrusted
code (npm packages, scripts from repos under review), this is a
credential theft vector.

__Fix__

`ProviderSettingsManager` in `@clinebot/core` now:

- Sets `0600` (owner read/write only) on `providers.json` after every
write
- Sets `0700` on the `settings/` directory when first created
- Applies `0600` retroactively at startup for pre-existing installations

These calls are best-effort and silently ignored on Windows, which does
not use POSIX file permissions.

No API changes. All existing tests pass.
2026-04-28 18:38:39 -05:00
Bee 9a0c4c365e fix: Compress CLI robot animation frames (#292)
## Summary

- Replaced the large inline robot animation frame data with a compact
RLE-backed generated JSON asset.
- Added a lightweight decoder that preserves the existing `FRAMES:
CroppedFrame[]` export used by the CLI TUI.
- Included validation for schema version, palette indexes, and decoded
cell counts to catch malformed generated data early.

## Validation

- Verified decoded output exactly matches the original 192 frames.
- Ran `bun -F @clinebot/cli typecheck` successfully.
- Reduced robot frame source footprint from ~1.48 MB inline to ~124 KB
total across decoder + generated JSON.
2026-04-28 16:14:51 -07:00
Bee 70b1a51153 refactor: clean up message builder (#293) 2026-04-28 15:43:15 -07:00
Bee c6a111e82a Cline SDK 0.0.37 (#294) 2026-04-28 15:42:32 -07:00
Renee Huang fb0767c8bc fix(cli): order TUI slash command menu by priority (#276)
## Summary
- cherry-pick commit 72248aa9 onto a dedicated branch
- keep this CLI ordering change in a separate PR

## Source
- cherry-picked from 72248aa9

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-04-28 15:37:59 -07:00
Bee 8e91cfaa68 fix: prepare messages before provider requests (#291)
Add a prepareMessages hook to apiHandlerToAgentModel and wire
SessionRuntime to run MessageBuilder.buildForApi before invoking legacy
handlers.

This ensures provider requests receive API-safe messages with
session-owned normalization, tool-result truncation, and stale
read-result rewriting. Add tests covering adapter preparation and
MessageBuilder API output behavior.
2026-04-28 15:32:03 -07:00
Robin Newhouse ab2f7db51a CLINE-2019: Keep CLI selected skill prompts compact (#288)
## Summary

Fixes CLINE-2019.

Selecting a skill or workflow from the CLI slash menu used to paste the
entire expanded `<user_command ...>` block into the prompt textarea.
That block contains the full skill/workflow instructions, so the user
saw a large internal prompt dump in the input and again in the submitted
chat line. It was noisy, hard to edit, and forced users to delete
instruction text character-by-character.

This PR keeps the UI representation compact while preserving the
model-facing payload:

- slash autocomplete inserts `[name (skill)]` / `[name (workflow)]`
tokens for skills and workflows
- submit-time expansion converts those compact tokens back to the
existing `<user_command ...>` payload before sending to the model
- the submitted terminal line and prompt history keep the compact
user-facing text instead of the expanded skill body
- Backspace/Delete at or inside a compact token removes the whole token
and moves the cursor to the token start
- manually typed `/skill args` expansion still works as before

## What was wrong

The CLI registry treated skill/workflow autocomplete values as
model-ready prompt text. `formatSlashCommandAutocompleteValue()`
returned `formatUserCommandBlock(...)` for `user-command` entries, and
the input controller used the expanded value for both `onSubmit()` and
the visible `user_submitted` chat entry/history.

That conflated three separate concerns:

1. what the user should see/edit in the prompt box
2. what the terminal should echo after submission
3. what the model should receive

The model needs the full instruction block, but the user-facing UI does
not.

## What changed

The CLI now separates those concerns:

- `formatSlashCommandAutocompleteValue()` returns compact text tokens
for skill/workflow commands
- `expandUserCommandPrompt()` expands compact tokens anywhere in the
prompt before model submission
- `usePromptInputController()` sends the expanded prompt to
`onSubmit()`, but displays and stores the original compact prompt
- `InputBar` delegates token-aware Backspace/Delete handling through
`onTokenDelete`

This mirrors the existing pasted-image marker pattern: the input
contains a friendly marker, while submit-time logic resolves the payload
needed by the runtime/model.

## Testing

- `bun -F @clinebot/cli typecheck`
- `bun --cwd apps/cli vitest run --config vitest.config.ts
src/tui/commands/slash-command-registry.test.ts
src/tui/hooks/use-autocomplete.test.ts`
- `bun -F @clinebot/cli test:unit`

Full CLI unit suite passed: 54 files / 260 tests.

## Before:
<img width="900" height="716" alt="Screenshot 2026-04-28 at 2 40 29 PM"
src="https://github.com/user-attachments/assets/244b5e74-a546-4188-aabf-72bfc8f1e733"
/>


<img width="541" height="220" alt="Screenshot 2026-04-28 at 2 40 08 PM"
src="https://github.com/user-attachments/assets/730d5348-f9c5-421a-9d3f-6a7bd82832ea"
/>

## After

Note: 2 skills active 

<img width="372" height="104" alt="Screenshot 2026-04-28 at 2 42 08 PM"
src="https://github.com/user-attachments/assets/e5b70ed2-1d9f-43ec-af1a-40668f219481"
/>
2026-04-28 15:00:47 -07:00
Renee Huang aea39ae7ae fix(cli): clarify provider row tab hint in model picker (#277)
## Summary
- cherry-pick commit e2ba6d8fb750cda463df53f3fcb5b7f7cabce8cf into a
dedicated branch
- open as a separate PR

## Source
- cherry-picked from e2ba6d8fb750cda463df53f3fcb5b7f7cabce8cf

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 23:45:07 +02:00
Saoud Rizwan 5dac5c4c56 fix(cli): respect cursor boundaries for input history (#281)
## Problem

The CLI prompt was intercepting Up and Down arrow keys for prompt
history whenever the app was idle. That made normal multiline editing
awkward because pressing Up or Down while there was content in the
textarea cycled historical prompts instead of moving through the current
input.

The intended behavior is closer to opencode: let the textarea own
vertical cursor movement while the cursor is inside the content, and
only use history navigation when the cursor is already at the relevant
buffer boundary.

## Technical Approach

The input history hook now computes a small navigation action before
touching history:

- navigate: the cursor is already at the start for Up or at the end for
Down, so cycle history.
- move-to-boundary: the cursor is on the first visual row for Up or last
visual row for Down, so move to the buffer boundary and consume the key.
The next keypress can cycle history.
- ignore: the cursor is in the middle of the content, so the root
keyboard handler does not prevent the event and OpenTUI textarea
movement runs normally.

When recalling older history with Up, the cursor is placed at offset 0
instead of the end. This lets repeated Up presses continue cycling older
entries, which was the specific follow-up behavior requested.

The textarea handle type now derives from OpenTUI TextareaRenderable via
Pick so the history hook can read visualCursor, height, and
virtualLineCount without widening the handle to an untyped shape.

## Debugging Notes

I checked the current CLI path and found history interception in
use-root-keyboard, with the actual history mutation in
use-input-history. I also checked workspace/opencode and found its
prompt behavior gates history on input.cursorOffset boundaries, with an
intermediate boundary jump when the cursor is on the first or last
visual row.

One non-obvious detail is that OpenTUI visualCursor.visualRow is
viewport-relative. For Down, the hook uses the smaller of textarea
height and virtual line count to decide the effective bottom row, so
single-line input in a taller textarea still treats row 0 as the bottom.

The first commit attempt was blocked by Biome on a multiline ternary. I
applied the formatter-compatible shape and recommitted successfully.

## Tests

Ran:

```sh
bun run --cwd apps/cli typecheck
bun run --cwd apps/cli test:unit
bun run --cwd apps/cli test:unit -- src/tui/hooks/use-input-history.test.ts
sleep 1 && git diff --check
```

Added focused unit coverage for the boundary decision helper, including
first-row Up, middle-content Up, end-of-buffer Down, last-row Down, and
the single-line-in-taller-textarea case.
2026-04-28 14:27:00 -07:00
Saoud Rizwan 6454298339 feat: redesign input field with adaptive OKLAB theming and light theme support (#287)
Redesigns the CLI input field and adds comprehensive light terminal
theme support.

<img width="648" height="585" alt="image"
src="https://github.com/user-attachments/assets/7f403616-079d-4bde-957b-2e7cf90882be"
/>

<img width="602" height="458" alt="image"
src="https://github.com/user-attachments/assets/9e7d1af2-6c52-446f-a040-09e2ac044459"
/>


## Input field redesign

Replaced the bordered rounded input field with a chevron-prompt style
inspired by opencode's approach. The new design uses no border, a subtle
filled background, an accent-colored `>` indicator (cyan for act mode,
yellow for plan mode), and generous padding (`paddingX={2}
paddingY={1}`). The result feels more spacious and modern compared to
the previous tight bordered box.

## Adaptive OKLAB color system

The input field colors now adapt to the user's terminal background
rather than using hardcoded hex values. On startup, the terminal's
background color is detected via OSC palette query (150ms timeout,
before React mounts) so there's no visible color flash.

The color derivation uses OKLAB color space because its L (lightness)
channel is perceptually uniform -- the same L delta produces the same
visual "step" whether the base is black or medium gray. An adaptive lift
formula `BASE_LIFT / (1 + distance_from_extreme * DAMPING)` gives a
large lift on very dark/light backgrounds and a smaller lift on
mid-tones, preventing overshoot. On dark themes the input bg is lifted
lighter; on light themes it darkens.

A sub-threshold chromatic nudge (0.003 in OKLAB a/b channels, ~10x below
just-noticeable-difference) gives each mode a barely-perceptible warm or
cool feel without visible tinting. Plan mode nudges warm (+a, +b), act
mode nudges cool (-a, +b).

Three color channels per mode: `inputBackground` (adaptive from terminal
bg), `inputForeground` (bright text), and `inputPlaceholder` (muted gray
with subtle mode tint).

Tested across common terminal themes:

```
Terminal BG        | Act BG  | Plan BG
#000000 (black)    | #1e201e | #211f1e  (lifted)
#282828 (gruvbox)  | #494a48 | #4c4948  (lifted)
#002b36 (solarized)| #254f58 | #2a4e58  (lifted)
#ffffff (white)    | #b0b2af | #b3b0af  (darkened)
#fdf6e3 (sol lite) | #b4af9a | #b7ad9a  (darkened)
```

## Light terminal theme support

OpenTUI defaults text to white, which is invisible on light terminal
backgrounds. Added `getDefaultForeground()` which returns `undefined` on
dark themes (preserving existing white default) and `#1a1a1a` on light
themes. Applied across: status bar, chat messages, robot ASCII art, home
view heading, all onboarding screens, searchable list, and autocomplete
dropdown.

Extended the terminal colors context to carry both the detected
background and foreground.

## User message background

User messages in chat now get a subtle background color (same as the
input field's adaptive palette) with edge-to-edge coverage
(`marginX={-1}` to counteract parent padding) and `paddingY={1}` for
vertical breathing room. This gives user messages a distinct visual
presence, similar to how the reference cline CLI uses
`backgroundColor="blackBright"`.

## Autocomplete dropdown fix

The dropdown was measuring its own box width via `useEffect` after
paint, so the first render used the full terminal width for layout math.
Descriptions were computed to be wide but clipped by the narrower actual
box, making them invisible until an arrow key press triggered re-render.
Replaced the post-render measurement with a `containerWidth` prop passed
from the parent view, giving correct layout on the very first render.

## Layout fixes

- Removed `marginBottom` from InputBar, moved spacing control to parent
views
- Home view: dropdown sits flush against input (no gap), StatusBar gets
`marginTop={1}` inside a wrapper box, total height stays constant via
`DROPDOWN_MAX_HEIGHT + 1`
- Chat view: wrapper box with `marginBottom={1}` separates input from
StatusBar

## Test plan

- [ ] Verify input field appearance on dark terminal (black, gruvbox,
dracula, nord, solarized dark)
- [ ] Verify input field appearance on light terminal (solarized light,
default light)
- [ ] Verify chevron `>` tints cyan in act mode, yellow in plan mode
- [ ] Verify user messages have visible background in chat
- [ ] Verify autocomplete descriptions appear immediately when typing
`/`
- [ ] Verify all text is readable on light terminal themes (onboarding,
chat, status bar)
- [ ] Verify no color flash on startup (palette detection happens before
React mount)
2026-04-28 14:14:11 -07:00
Bee 822435c474 fix: regenerate bun lockfile during version updates (#286)
Remove any stale bun.lock before running a lockfile-only install in the
version script, ensuring dependency metadata is refreshed before model
generation runs.

Fix the broken
https://github.com/cline/sdk-wip/actions/runs/24946769193/job/73049831199
2026-04-28 14:05:13 -07:00
Saoud Rizwan aaed1ec3d7 feat(cli): compact large pasted input (#280)
Adds compact rendering for large pasted text in the CLI TUI prompt. When
a paste has at least five lines, the input field now inserts a short
preview marker such as `[some preview... Pasted +12 lines]` instead of
flooding the textarea with the full content.

<img width="563" height="517" alt="image"
src="https://github.com/user-attachments/assets/1d5cdfc8-18d3-4e15-9cae-8f8a7d2be52c"
/>


## Problem

Large pasted snippets were inserted verbatim into the OpenTUI textarea.
That made the prompt harder to scan, pushed surrounding UI out of view,
and made deletion tedious because users had to backspace through the
entire pasted body.

## Technical approach

The input bar now decodes text paste events, skips binary and image MIME
types, and routes large text pastes through a new paste snippet path.
The visible marker is inserted into the textarea and wrapped in an
OpenTUI virtual extmark so cursor movement and Backspace treat it as one
atomic range.

The prompt controller keeps the original pasted content in memory
alongside the marker. On submit, it expands any active markers back to
their original text before slash command expansion and before sending
the prompt to the runtime. If the marker is removed from the input, the
controller prunes the stale snippet record.

A new `pasted-snippets` helper owns line counting, marker formatting,
duplicate marker suffixing, and expansion. That keeps the OpenTUI event
handling and prompt submission logic small.

## Debugging journey

I first checked the current CLI input path and the referenced workspace
inspiration. The local referenced workspace paths only contained kanban
metadata, so the useful clue came from the installed OpenTUI textarea
implementation. OpenTUI already exposes `TextareaRenderable.extmarks`,
and its virtual extmark deletion behavior matches the desired atomic
backspace behavior.

The main implementation risk was preserving the submitted prompt exactly
while showing only a marker in the input field. The final design avoids
mutating the underlying pasted content by storing the full snippet
separately and expanding markers only at submit time.

## Gotchas

Image paste handling still runs first for immediate image paste data and
pasted image paths. Large text compaction only handles text paste data
after image detection has had a chance to claim the paste.

The compact marker is intentionally plain text in the textarea, but the
virtual extmark makes it atomic. If users delete the whole marker, the
stored snippet is removed from the active snippet list.

## Testing

Ran:

```sh
bun -F @clinebot/cli typecheck
bun biome check apps/cli/src/tui/components/input-bar.tsx apps/cli/src/tui/hooks/use-prompt-input-controller.ts apps/cli/src/tui/root.tsx apps/cli/src/tui/views/chat-view.tsx apps/cli/src/tui/views/home-view.tsx apps/cli/src/tui/utils/pasted-snippets.ts apps/cli/src/tui/utils/pasted-snippets.test.ts
bun -F @clinebot/cli test:unit -- src/tui/utils/pasted-snippets.test.ts
bun -F @clinebot/cli test:unit
```

Full CLI unit suite passed with 55 test files and 265 tests.
2026-04-28 14:03:54 -07:00
Bee 0ed78b2939 fix: avoid hydrating sessions when listing (#285)
List CLI and sidecar history with hydrate disabled to avoid loading full
session details when only summary rows are needed. Update session
helpers and tests to pass the new hydrate option, and replace sidecar
fallback listing with lightweight manifest/store aggregation.
2026-04-28 13:30:04 -07:00
Bee 29e7434f0a fix: accept common command and path input aliases (#282)
Support model-generated aliases for tool inputs, including command/cmd
for run commands and paths for read files. Normalize these shapes before
execution so common requests are handled consistently and add tests to
cover the new aliases.
2026-04-28 12:48:39 -07:00
Bee de175a08ee fix: avoid blocking TUI startup on hub readiness (#284)
Switch interactive sessions to backendMode auto so the CLI can reuse an
available compatible hub or fall back to the local runtime while
prewarming the hub in the background.

Defer resume message hydration until after OpenTUI renders and schedule
runtime readiness checks asynchronously to keep initial TUI paint
responsive.

Document the interactive startup rule that hub startup, polling,
indexing, and resume reads should not block output unless explicitly
required.
2026-04-28 12:37:13 -07:00
Bee fc25d453fd fix: preserve fork start config before stopping session (#278)
Resolve the fork start configuration before stopping the active session
so forks can be created from either the source session or current
config. Reuse the resolved config when starting the fork and store it
for the new session.
2026-04-28 11:31:08 -07:00
Bee a7e00d3683 fix: Resume in-flight team runs by session id (#270)
## Summary

Adds automatic recovery for in-flight team runs when a session is
resumed, without requiring users to provide a `--team-name`.

Previously, restored team state would mark queued/running runs as
interrupted during load, and CLI-generated team names made session-based
recovery unreliable. This change makes the session id the stable team
persistence key, preserves active runs during restore, and requeues
recoverable runs after teammates are restored.

## Changes

- Thread the host-created `sessionId` into local runtime bootstrap
config so team persistence is keyed by session id.
- Stop file and SQLite team stores from marking queued/running runs
interrupted during `loadRuntime`.
- Add `AgentTeamsRuntime.recoverActiveRuns()` to:
  - find restored queued/running runs
  - requeue them after teammate restoration
  - redispatch them automatically
  - mark runs interrupted only when their teammate cannot be restored
- Update recovered run execution to include a safety prefix instructing
teammates to inspect current workspace state and avoid duplicate work.
- Update `awaitRun()` to wait for both queued and running runs.
- Preserve teammate specs during runtime/session lifecycle shutdown so
future resumes can respawn teammates.
- Stop CLI from generating random team names by default; Core now
handles internal display names while persistence follows session id.
- Update tests for team persistence and CLI `/team` behavior.

## Validation

- `bun -F @clinebot/core typecheck`
- `bun -F @clinebot/cli typecheck`
- `bun -F @clinebot/core test:unit --
src/runtime/runtime-builder.team-persistence.test.ts
src/extensions/tools/team/team-tools.test.ts`
- `bun -F @clinebot/cli test:unit -- src/main.test.ts`
2026-04-28 11:04:56 -07:00
Bee af870a572c feat: plugin-driven automation event ingress (#243)
Add plugin automation event contributions and setup context

- add automationEvents plugin capability and event type registration
- expose session, client, user, logger, telemetry, and automation
context to plugin setup
- bridge sandbox plugin automation events and logs back to core
- add local plugin event example and docs
2026-04-28 10:35:54 -07:00
Bee 9b461ed815 feat: Make hub startup self-heal stale and incompatible daemon versions (#269)
This PR makes local hub startup/update behavior resilient to stale or
incompatible hub daemon processes.

Previously, users or developers could end up connected to an old running
hub after updating the CLI or switching builds. That could surface as
confusing errors such as:

```text
Unsupported hub schedule command: session.messages
```

and require manually running:

```sh
bun run cli hub stop
```

This change makes hub compatibility enforcement automatic.

### Changes

- Make detached hub startup build-aware:

- require the probed hub `buildId` to match the current
`resolveHubBuildId()`
  - treat missing/blank `buildId` as incompatible
  - reject old/pre-buildId hub daemons instead of reusing them

- Retire incompatible hubs automatically:

  - request graceful `/shutdown`
  - fall back to `SIGTERM`
  - clear stale discovery
  - spawn/wait for a compatible hub

- Improve CLI update hub restart:

  - use `stopLocalHubServerGracefully()` first
  - fall back to `SIGTERM`
  - fall back to `SIGKILL` if needed
  - clear hub discovery before re-ensuring a fresh hub

- Update compatibility-aware client resolution:

- clear discovery when a discovered hub is missing or mismatching build
metadata

- Add tests for:

  - rejecting healthy hubs from a different build
  - rejecting healthy hubs with missing build metadata
  - clearing discovery on missing/mismatched build metadata
2026-04-28 10:26:00 -07:00
Bee 162b183e4a feat: Event-Driven Automation (#242)
- Keep cron storage/report path handling scope-aware. Default to user
scope (`~/.cline/cron/`)
- Reuse the same durable claim/requeue model as file-based runs.
- Preserve the invariant that retries are explicit policy.
Event-triggered reruns should come from ingress policy or manual retry,
not accidental rematerialization.
2026-04-28 09:10:35 -07:00
Bee e9ae25d6ee fix: remove provider tools from codex provider (#275)
provider tools are only applicable for Codex CLI

```
sdk on  bee/codex-tools [$+] via 🥟 v1.3.13 on ☁️  beatrix@cline.bot took 16s 
❯ bun run cli "Hey tell me about this repo"
$ bun --conditions=development --cwd apps/cli dev "Hey tell me about this repo"
$ CLINE_BUILD_ENV=development bun --conditions=development ./src/index.ts "Hey tell me about this repo"
Plan:
1. Inspect the repository layout and key docs to understand purpose, packages, and workflows.
2. Summarize the architecture, package boundaries, and how the CLI app fits in.
3. Give you a concise orientation with important commands and files to read next.
[run_commands] pwd; ls -la; find .. -maxdepth 2 -type f \( -name 'README.md' -o -name 'CONTRIBUTING.md' -o -name 'ARCHITECTURE.md' -...
   ⎿ /Users/beatrix/dev/sdk/apps/cli (+2 more)
[read_files] {"files":[{"path":"/Users/beatrix/dev/sdk/apps/cli/AGENTS...
   ⎿ ok
Plan update:
1. The app-local `AGENTS.md` doesn’t exist, so I’ll read the CLI package docs and package manifest directly.
2. I’ll inspect the app’s source layout to identify the main entry points and capabilities.
3. Then I’ll give you a practical repo/app overview focused on what this CLI does and how it’s developed.
[read_files] {"files":[{"path":"/Users/beatrix/dev/sdk/apps/cli/README...
   ⎿ 1 | # Cline CLI Lite   2 |    3 | Cline CLI built with Cline SDK.   4 |    5 ... (+2 more)
[run_commands] find /Users/beatrix/dev/sdk/apps/cli/src -maxdepth 2 -type f | sort; find /Users/beatrix/dev/sdk/apps/cli/bin -maxdep...
   ⎿ /Users/beatrix/dev/sdk/apps/cli/src/.DS_Store /Users/beatrix/dev/sdk/apps/cli... (+1 more)
Plan:
1. Inspect the CLI package docs and manifest to understand what this repo section does.
2. Review the source layout to identify main capabilities and architecture.
3. Summarize the repo in practical terms: what it is, how it’s organized, and how to work on it.

This repo is a monorepo for the **Cline SDK ecosystem**, and the directory you dropped me into is specifically **`apps/cli`**, the package for the **`clite`** command-line app.

## What this repo is

At a high level, the monorepo appears to contain:

- **published SDK packages** under `packages/`
- **host applications**^C
```
2026-04-28 09:05:40 -07:00
Bee 484c333c6b fix: code app history (#266)
Fix resuming session chat remove old messages from a session in Code
app.
2026-04-28 09:01:23 -07:00
Saoud Rizwan fc47900c85 Add TypeScript LSP plugin example (#273)
Adds a new example plugin at `apps/examples/typescript-lsp-plugin/` that
demonstrates how to build a tool plugin using the SDK's `AgentExtension`
interface and `createTool()` helper.

The plugin registers a single `goto_definition` tool powered by the
TypeScript Language Service API. Given a file path and line number, it
finds all identifiers on that line and resolves where they're actually
defined -- following through imports, re-exports, type aliases, and
declaration merging. This is the same resolution your IDE uses, so it's
much more precise than grep or text search.

I originally wrote this as a proof-of-concept on Slack to test out the
plugin system, and it turned out to be a good showcase of what plugins
can do. The plugin:

- Uses `createRequire()` to resolve `typescript` from the target
project's own `node_modules` at runtime, so it has zero extra
dependencies and uses the same TS version the project compiles with
- Caches the Language Service instance across calls within a session for
efficiency
- Filters out self-references so you only see where symbols are actually
defined elsewhere
- Includes a standalone demo runner via `import.meta.main` so you can
test it directly with `bun run`

The `apps/examples/README.md` is updated to list the new example
alongside the existing `cline-plugin` and `subagent-plugin` entries.

## Test plan

- [ ] `bun run types` passes (verified by pre-commit hook)
- [ ] Biome check passes (verified by pre-commit hook)
- [ ] Copy the plugin to `~/.cline/plugins/typescript-lsp.ts` and run
`clite -i "Find where createTool is defined"` to verify it works
end-to-end
- [ ] Run the standalone demo: `ANTHROPIC_API_KEY=sk-... bun run
apps/examples/typescript-lsp-plugin/index.ts`
2026-04-28 08:04:47 -07:00
Dominic Cooney 16601a024d chore: Remove scree from model planning in #259. (#271)
These agent debugging notes were committed accidentally. Deleting them.
2026-04-28 21:18:02 +09:00
Saoud Rizwan f4fdf6bb05 chore: remove top-level .claude directory
The opentui skill symlink should only exist in apps/cli/.claude/skills,
not at the repo root.
2026-04-28 03:58:22 -07:00
Saoud Rizwan 971e6617f9 chore(cli): release v0.0.4 2026-04-28 03:18:18 -07:00
Saoud Rizwan 09704e9cec fix(cli): prevent compiled hub daemon recursion (#272)
This PR fixes a production-only issue where the npm-installed `clite`
binary could recursively spawn more `clite` processes when it tried to
start the local hub daemon.

The short version: the CLI worked during development because `bun run
dev` and `bun link` run through real Bun. The published npm package runs
a compiled Bun executable. Those two environments handle daemon startup
differently, and the difference was serious enough that the production
binary could accidentally relaunch the CLI instead of launching the hub
daemon.

In the worst case, a normal command like `clite "say hello"` could start
an expanding process tree. Each child process thought it was just
another CLI invocation, so it also started normal CLI warmup work like
file indexing and plugin setup. That is why the failure looked much
larger than just one stuck daemon.

## The Story

We published the CLI, installed it globally with npm, and ran `clite`.
Very quickly the container became unhealthy. CPU climbed, memory usage
grew, and the process table filled with many `clite` children plus
related worker processes.

At first this was confusing because we had already tested the CLI
through the normal development paths:

- `bun run dev`
- `bun link`
- source-mode e2e tests
- package smoke tests like `clite --version`

Those all looked fine.

The important clue was that the runaway processes were not random. They
had a repeated shape like this:

```text
clite /$bunfs/root/daemon-entry.js --cwd ... --host 127.0.0.1 --port 0 --pathname /hub
```

That command line is supposed to be the detached hub daemon. Instead,
every one of those processes was actually running the normal CLI
entrypoint again.

## What Went Wrong

The hub launcher in core starts the daemon by using `process.execPath`
plus the daemon entry file.

In development, that means something like:

```sh
bun --conditions=development /path/to/daemon-entry.ts --cwd ... --host ... --port ...
```

That works. Real Bun sees the script path and runs `daemon-entry.ts`.

But the npm package does not run from source. The published platform
packages contain a Bun `--compile` binary. In that environment,
`process.execPath` is not the Bun runtime. It is the compiled `clite`
application itself.

So production did this instead:

```sh
clite /$bunfs/root/daemon-entry.js --cwd ... --host 127.0.0.1 --port 0 --pathname /hub
```

That looks reasonable at first glance, but it is not how Bun compiled
binaries work. A compiled binary does not treat the next argument as a
new script to execute. It runs its bundled entrypoint again and passes
the extra values through as normal arguments.

So the intended daemon child did not become the daemon. It became
another CLI process.

That accidental CLI child then:

1. Parsed the daemon path and flags as CLI input.
2. Entered normal agent startup.
3. Created core runtime state.
4. Tried to prewarm the local hub.
5. Spawned another supposed daemon.
6. Repeated the same mistake.

Because the real hub never actually started, the discovery file never
became healthy. Nothing was there to stop the next prewarm attempt. That
is how this became a recursive process spawn.

## Why We Missed It

This was easy to miss because our development flow was exercising a
different execution model than users get from npm.

`bun run dev` worked because it used real Bun and a real script path.

`bun link` worked because the package bin points at `src/index.ts`, so
it also used real Bun.

`--version` smoke tests worked because they do not start the hub daemon.

The broken behavior only appeared when the actual compiled binary tried
to start the detached hub. That is the same shape users get after `npm
install -g @clinebot/cli`, but it was not represented by our source-mode
tests.

## What This PR Changes

This PR changes daemon startup from "try to execute this script path
with whatever `process.execPath` is" to "tell the launched process what
role it should run as."

The new flow is:

1. Core spawns the detached hub process with
`CLINE_RUN_AS_HUB_DAEMON=1`.
2. The CLI entrypoint checks that sentinel before loading normal CLI
code.
3. If the sentinel is set, the process imports
`@clinebot/core/hub/daemon-entry` directly.
4. If the sentinel is not set, the process continues as the normal CLI.
5. Core also refuses to spawn another detached hub if the current
process is already marked as the hub daemon.

That last point is intentional defense in depth. Even if another
entrypoint accidentally reaches core while marked as daemon mode, it
will not recursively spawn another daemon.

The sentinel name and helper live in `@clinebot/shared` so both CLI and
core use the same definition without duplicating string constants or
forcing the CLI entrypoint to import core too early.

## Why This Fix Is Safe

The fix is narrow. It only changes the internal launch contract for the
detached hub daemon.

Normal CLI commands still run through the same CLI path.

Development daemon startup still works because the real Bun path still
receives the same daemon args. The added env var simply makes the
compiled-binary case explicit.

Compiled CLI daemon startup now works because the compiled binary can
choose the daemon entrypoint from inside its own bundled code instead of
relying on Bun to execute a second script path.

The core guard is also conservative. A process already running as the
hub daemon should not be responsible for starting another detached hub
daemon.

## Verification

Focused tests:

```sh
bunx vitest run src/runtime/hub-daemon-env.test.ts src/runtime/build-env.test.ts --config vitest.config.ts
bunx vitest run src/hub/daemon.test.ts src/runtime/host.test.ts --config vitest.config.ts
bunx vitest run src/main.test.ts --config vitest.config.ts
```

Typechecks:

```sh
bun -F @clinebot/shared typecheck
bun -F @clinebot/core typecheck
bun -F @clinebot/cli typecheck
bun run types
```

Compiled binary build:

```sh
bun -F @clinebot/cli build:platforms:single --skip-sdk-build
```

Compiled binary smoke test:

```text
start_status=0
start_output=ws://127.0.0.1:38441/hub
process_count_for_workdir_after_start=1
process_lines_for_workdir_after_start=clite /$bunfs/root/daemon-entry.js --cwd /tmp/clite-clean-work... --host 127.0.0.1 --port 0 --pathname /hub
stop_status=0
stop_output={"stopped":true}
process_count_for_workdir_after_stop=0
```

The important part is the process count. The actual compiled binary
starts exactly one daemon for the test workspace. It does not create a
growing tree of `clite` children. The daemon then stops cleanly and
leaves zero matching processes for that workspace.

## Takeaway

This was not a normal runtime bug. It was a packaging/runtime-shape bug.

The source version and the published compiled binary did not behave the
same way when launching the daemon. Going forward, hub startup needs at
least one test or release check that exercises the actual compiled
binary, not just source-mode `bun` execution.
2026-04-28 03:14:28 -07:00
Saoud Rizwan af36be5e9f chore(cli): release v0.0.3 2026-04-28 01:35:24 -07:00
Saoud Rizwan 90c1897716 fix(ci): disable npm provenance until repo is public
npm sigstore provenance requires public repo visibility. The repo is
currently internal, so provenance signing fails with E422. Commented
out for now; re-enable when the repo goes public.
2026-04-28 01:27:02 -07:00
Saoud Rizwan 26c799bd02 fix(ci): build SDK packages before running tests in publish workflow
The plugin sandbox test spawns a Node.js subprocess that resolves
@clinebot/shared via CJS (jiti), which needs built dist/ files. Without
building first, the dist directory does not exist in CI and the test
fails with MODULE_NOT_FOUND. Also adds --skip-sdk-build to the platform
binary build step to avoid rebuilding SDK twice.
2026-04-28 01:18:56 -07:00
Saoud Rizwan 554b61e502 fix(shared): add default export condition for CJS resolution
The plugin sandbox subprocess uses jiti (CJS require) to load
@clinebot/shared, but the exports map only had import/development/types
conditions. Adding a default fallback fixes ERR_PACKAGE_PATH_NOT_EXPORTED
in the sandbox bootstrap.
2026-04-28 01:11:44 -07:00
Saoud Rizwan 2dfc796b3f feat: rewrite CLI TUI from Ink to OpenTUI (#227)
The CLI's interactive TUI has been rewritten from scratch using
[OpenTUI](https://github.com/anomalyco/opentui), replacing the Ink-based
implementation. OpenTUI is a native terminal rendering engine written in
Zig with a React reconciler, giving us capabilities that were impossible
with Ink: native diff rendering, syntax-highlighted code, streaming
markdown, scrollable content, mouse interaction, and clipboard support.

### Before / After

The old TUI was a single 1,300-line monolith (`interactive-tui.ts`) with
30+ useState hooks, limited rendering (plain text only), and no dialog
system. The new TUI is decomposed into focused components, contexts, and
hooks with rich rendering throughout.

### Architecture

```
run-interactive.ts (runtime bridge)
  |
  |  callbacks: onSubmit, onAbort, onModelChange, onModeChange, ...
  v
index.tsx (OpenTUI renderer)
  |
  v
root.tsx (provider tree + view router + global keyboard)
  |
  +-- DialogProvider          Modal dialogs (model picker, tool approval, settings, etc.)
  +-- SessionProvider         Chat entries, running state, mode, usage tracking
  +-- EventBridgeProvider     Subscribes to SDK agent events, forwards to session
  |
  +-- View Router
        +-- HomeView          Welcome screen with animated robot + centered input
        +-- ChatView          Scrollbox message list + input bar + status bar
        +-- OnboardingView    First-run provider/model setup wizard
        +-- ConfigView        Settings browser (dialog)
        +-- HistoryView       Session history with resume (dialog)
```

The TUI never talks to the SDK directly. All communication flows through
callback props defined in `TuiProps`. The runtime bridge
(`run-interactive.ts`) owns session lifecycle, event wiring, and state
that persists across session restarts.

### What's New

Core rendering:
- Streaming markdown for assistant responses (`<markdown>` element)
- Unified diffs with syntax highlighting for file edits (`<diff>`
element)
- Syntax-highlighted code for file reads (`<code>` element)
- Expandable/collapsible tool output sections
- Scrollable chat with auto-scroll pinning during streaming
- Mouse-tracked animated robot on the home screen

Dialog system (`@opentui-ui/dialog`):
- Model selector with search, thinking level picker, and provider
switching
- Cline-specific model picker with recommended/free tiers
- Tool approval dialog (approve/reject/always-approve per tool)
- Ask question dialog (agent asks user for input mid-run)
- Config/settings browser with interactive toggles
- Session history browser with message preview and resume
- Help overlay with all keyboard shortcuts and commands
- Provider picker with OAuth login and API key entry
- Device code auth flow for Cline provider

Input and navigation:
- Autocomplete dropdown for `/` slash commands and `@` file mentions
- Input history (up/down arrow through previous prompts)
- Message queuing (Enter during a running turn queues the message)
- Steer messages (Ctrl+S sends guidance to a running turn)
- Text selection with copy-to-clipboard (OSC52)

Session management:
- `/history` to browse and resume past sessions
- `/compact` for manual context window compaction
- `/clear` to reset conversation
- `/model` to switch models mid-conversation (preserves chat history)
- `/help` with full keyboard shortcut and command reference
- `/settings` for interactive config browser

Plan/Act mode:
- Tab toggles between plan and act mode with accent color change
(yellow/cyan)
- `switch_to_act_mode` tool lets the agent transition from plan to act
mid-session
- System prompt and tools are rebuilt on mode switch, conversation
history preserved

Onboarding:
- First-run wizard detects if no provider is configured
- Step-by-step provider selection, authentication (OAuth or API key),
model selection
- Thinking level configuration for supported models
- Results applied to runtime config immediately

### Interactive Setup Wizards

Three new top-level CLI commands that walk users through complex setup
flows interactively, so they don't have to construct long flag-heavy
commands by hand:

`clite connect` - Connector setup for messaging platforms (Telegram,
Slack, Discord, Google Chat, WhatsApp, Linear). Walks through bot token
entry, platform-specific options, and launches the bridge.

`clite schedule` - Scheduled run creation. Walks through cron expression
(with presets like "weekdays at 9am"), prompt, workspace, provider/model
selection, iteration limits, and timeout.

`clite mcp` - MCP server management. Lists configured servers, add new
ones (stdio or SSE), edit existing config, remove servers, and test
connectivity.

### What Got Removed

- `interactive-tui.ts` (1,314 lines) and all old Ink components
(ChatMessage, ConfigView, InputBox, MentionMenu, SlashMenu, StatusBar,
WelcomeView)
- `run-interactive-opentui.ts` (merged into `run-interactive.ts`)

The old Ink `HistoryListView` component is preserved at
`commands/history-list-view.ts` because the standalone `clite history`
command still uses Ink for its interactive picker. This is separate from
the main TUI.

### Runtime Changes

- Shebang changed from `#!/usr/bin/env node` to `#!/usr/bin/env bun`
(required because OpenTUI uses `bun:ffi`)
- `package.json` bin entry changed from `dist/index.js` to
`src/index.ts` for `bun link` dev workflow
- Minor SDK changes: `hookPath` added to `RpcSessionRow`, `toolTimeouts`
config support, `resolveSystemPrompt` export

### Documentation

- `DEVELOPMENT.md`: Full development guide covering prerequisites (Bun,
Zig, Node), first-time setup, monorepo structure, tech stack, TUI
architecture walkthrough, and common dev tasks
- `DISTRIBUTION.md`: Plan for publishing compiled binaries to npm
(platform-specific packages, binary resolver, postinstall caching, CI
pipeline). Uses OpenCode's distribution model as reference.

### Testing Locally

```bash
# Install prerequisites
curl -fsSL https://bun.sh/install | bash
brew install zig   # macOS. For Linux: snap install zig --classic

# Clone and checkout
git clone <repo-url>
cd cline-sdk-wip
git checkout saoudrizwan/cli-tui-opentui
bun install

# Build SDK packages (required for workspace package resolution)
bun run build:sdk

# Link globally
cd apps/cli
bun link

# Run from anywhere
clite
```

Or skip the build/link and run directly from source:

```bash
cd apps/cli
bun run dev
```

To test onboarding flow with a fresh config: `clite --config
/tmp/cline-test`

---------

Co-authored-by: abeatrix <beatrix@cline.bot>
2026-04-28 00:25:32 -07:00
Robin Newhouse ec06a9a6aa Fix local provider updates from settings (ENG-1882) (#265)
## Summary
- allow updateLocalProvider to recover providers that exist in settings
but are missing from models.json
- seed a minimal local provider registry entry from saved
settings/request fields before continuing the existing update flow
- add regression coverage for settings-only providers

## Background

Surfaced in Kanban when editing a custom OpenAI-compatible provider
(e.g. litellm, mistral) via the Edit Provider dialog. The error
`provider "litellm" does not exist` was returned to the UI.

The out-of-sync state occurs because Kanban writes provider settings
directly via `saveSdkProviderSettings` /
`ProviderSettingsManager.saveProviderSettings` — for example when the
user selects a provider from the settings panel without going through
the Add Provider flow. That path writes to `providers.json` but never
touches `models.json`. So a provider can legitimately have a settings
entry with no registry entry, and `updateLocalProvider` would hit the
missing-entry guard on the next edit attempt.

The same state can also arise from settings imported or migrated from
another Cline install (e.g. VS Code extension).

## Tests
- bun -F @clinebot/core test:unit --
src/services/providers/local-provider-service.test.ts
- bun -F @clinebot/core typecheck

## Notes
- The normal pre-commit hook runs repo-wide `bun run types` and is
currently blocked by unrelated @clinebot/code type errors involving
`source` properties in provider metadata. The core package test and
typecheck pass for this change.
2026-04-27 22:10:47 -07:00
Bee 9dc8df127a chore: Model List update (#268)
Ran `bun run cli build:models` to update auto-generated model list.

Updated version to 1777348777191
2026-04-27 21:21:45 -07:00
Dominic Cooney 62e0ae4ad6 fix: Forward read_file image bytes through to the model end-to-end (#259)
Fixes ENG-1873.

## Description

Make `read_file` images returned via the `read_files` tool actually
reach the model end-to-end. On `origin/main` the bytes are silently lost
or hallucinated at one of three layers between the tool result and the
wire payload, depending on the provider.

This branch fixes each layer in turn:

| Layer | Symptom on `origin/main` | Fix |
|---|---|---|
| `compat.ts` `Message → AgentMessage` converter | image+text content
arrays were flattened — text was joined into the tool-result string and
the image was emitted as a sibling content block, detached from the
originating tool call | preserve the array shape so
`toAiSdkToolResultOutput` can emit `{type:'content', value:[...]}` |
| SDK runtime (`ai-sdk-format.ts`, agent-config-adapter,
session-runtime-orchestrator) | image bytes dropped between the agent
layer and the provider layer | propagate the multimodal
`ToolResultOutput` through the runtime |
| Content-part naming | mismatched part-type names | use `image-data`
consistently |
| OpenAI-compatible wire format | OpenAI Chat Completions has no slot
for images inside `role:"tool"` messages, so `@ai-sdk/openai-compatible`
was `JSON.stringify`ing the parts array — the model then saw ~50KB of
opaque base64 text and hallucinated | `splitToolImagesMiddleware` (a
`LanguageModelV3Middleware.transformParams` hook). Operates on the typed
`LanguageModelV3Prompt` BEFORE `@ai-sdk/openai-compatible`'s
chat-messages converter runs: replaces image/file parts inside any
`role:"tool"` content-array with `(see following user message for
image)` placeholders and inserts a sibling `role:"user"` message
carrying them as `LanguageModelV3FilePart`. Mirrors the proven wire
pattern from classic Cline
(`src/core/api/transform/openai-format.ts:convertToOpenAiMessages` in
cline/cline). |

The AI SDK message contract (image-data inside `ToolResultOutput`) is
preserved end-to-end; only the synthetic prompt seen by Chat Completions
converters is rewritten.

### Coverage

The middleware is wired at exactly two dispatch points but covers ~30
providers automatically:

1. **`vendors/openai-compatible.ts`** —
`createOpenAICompatibleProviderModule` is the single factory that every
provider with `family: "openai-compatible"` routes through (per
`builtins-runtime.ts` family dispatch). So the wrapper applies
transparently to: `cline`, `deepseek`, `xai`, `together`, `fireworks`,
`groq`, `cerebras`, `sambanova`, `nebius`, `baseten`, `requesty`,
`huggingface`, `vercel-ai-gateway`, `aihubmix`, `hicap`, `nousResearch`,
`huawei-cloud-maas`, `qwen`, `qwen-code`, `doubao`, `zai`,
`zai-coding-plan`, `moonshot`, `wandb`, `openrouter`, `ollama`,
`lmstudio`, `oca`, `asksage`, `sapaicore`.
2. **`vendors/mistral.ts`** — Mistral has its own non-openai-compatible
chat-messages converter but the same string-only `role:"tool"`
constraint, so the wrapper is applied explicitly.

Providers with `protocol: "openai-responses"` (`litellm`, `v0`,
`xiaomi`, `kilo`) are routed to `@ai-sdk/openai`'s Responses API which
supports multimodal tool inputs natively. Anthropic-family providers
render content arrays on tool messages natively.

## Before / after observed with `clite`

Same `clite` invocation, same `test-image.png` (the Cline logo), same
default cline gateway model:

**`origin/main`** — `read_files` tool result is just `ok` (image bytes
never reach the AgentMessage), so the model replies:
> *"I'm unable to view the image — the current model doesn't support
image input, so I can't tell you what's in `test-image.png`."*

**this branch** — `read_files` tool result is `Successfully read image
[image]` with the bytes attached, the middleware splits them into a
follow-up user message, and the model replies:
> *"The Cline logo: a black robot/bot icon next to the word \"cline\" in
lowercase monospace text."*

## Multi-file `read_files`

Verified that a single `read_files` tool call covering text **and**
multiple images works end-to-end:

```bash
clite --act 'Please read /tmp/greeting.txt, /tmp/image.jpg and /tmp/image2.png in one shot using read_files and tell me concisely what each contains.'
```

```
[read_files] {"files":[{"path":"/tmp/greeting.txt"},{"path":"/tmp/image.jpg"},{"path":"/tmp/image2.png"}]}
   ⎿ 1 | Hello, world! 2 | (+2 more)

- /tmp/greeting.txt: The text "Hello, world!"
- /tmp/image.jpg: A photo of a Roman dodecahedron — a small, hollow bronze artifact …
- /tmp/image2.png: A product-style image of a shiny red apple with a green leaf …
```

The original implementation only folded the *first* sibling `image` part
into a tool-result, leaving the second-and-later images orphaned in the
message stream — providers then rejected the request with `Tool result
is missing for tool call …`. The middleware now consumes every
image/file part inside the tool-result content array (it operates on the
structured AI SDK message shape, not the wire JSON). Pinned by
regression tests.

## Test plan

```bash
# Typecheck
bun --parallel -F '*' typecheck

# Unit tests touched by this PR
bun -F @clinebot/llms test
bun -F @clinebot/shared test
bun -F @clinebot/core test
bun -F @clinebot/agents test
```

### End-to-end with `clite`

```bash
# 1. Build
bun run build:sdk
bun -F @clinebot/cli build

# 2. Drop any image into a working dir as `test-image.png`, then:
cd /tmp/clite-apple-test
bun /path/to/sdk-wip/apps/cli/dist/index.js -t 60 --autoapprove true --act \
  "Read the file test-image.png and tell me what's in the image."
```

Expected: model accurately describes the actual image contents.

A pure-text read continues to work unchanged (the middleware is
identity-preserving when the prompt contains no tool-result image/file
parts — no clone, no copy).

### VS Code extension

Manually verified: image reads in the VS Code extension chat now produce
accurate descriptions.

<img width="588" height="309" alt="Screenshot 2026-04-27 at 23 28 11"
src="https://github.com/user-attachments/assets/3881f3ca-12d5-4661-a780-5b4b8e4f9332"
/>

## Notes

- The middleware (`splitToolImagesMiddleware`, in
`packages/llms/src/providers/middleware/split-tool-images.ts`) operates
on the typed `LanguageModelV3Prompt` before the chat-messages converter
runs. No JSON parse/restringify of request bodies, no wire-level fetch
interception.
- Identity is preserved when no rewrite is needed: prompts without
tool-result image/file parts pass through unchanged with no allocation.
- The synthetic `role:"user"` sibling message is typed
(`LanguageModelV3FilePart`), so every downstream converter — Chat
Completions, Mistral, Anthropic, Bedrock, etc. — translates it to its
own native multimodal user-content shape without further help.

---------

Co-authored-by: cline <cline@cline.local>
2026-04-28 12:25:00 +09:00
Bee c581550157 fix: CLI hub commands to use detached daemon (#267)
Fix CLI hub start and ensure so they launch and reuse the detached hub
daemon instead of starting an in-process server that dies when the CLI
exits.

Also make detached hub startup fall back to an ephemeral port when the
default port is unavailable, matching the existing in-process fallback
behavior.
2026-04-27 19:53:55 -07:00
Bee 1cb20b5c59 feat: hub reads and list session messages across clients CLINE-2000 (#264)
What changed:

- Added a new hub command:

  - `session.messages`
  - File: `packages/shared/src/hub.ts`

- Implemented `session.messages` in the hub server:

- Reads messages via the hub-owned `sessionHost.readMessages(sessionId)`
  - Returns `{ sessionId, messages }`
  - File: `packages/core/src/hub/server.ts`

- Updated `HubRuntimeHost.readMessages()`:

- Before: fetched `session.get`, then tried to read
`session.messagesPath` from the client filesystem.
- Now: sends `session.messages` to the hub and returns the hub-provided
messages.
  - File: `packages/core/src/transports/hub.ts`

- Added regression coverage:

- Hub transport test verifies `readMessages()` calls `session.messages`
and does not dereference a local artifact path.

- Hub server boundary test verifies `session.messages` is served by the
hub-owned session host.

  - Files:

    - `packages/core/src/transports/hub.test.ts`
    - `packages/core/src/hub/server.boundary.test.ts`
2026-04-27 17:44:30 -07:00
Renee Huang f2eda58c70 rm hardcoded model lists (#10436)
* rm hardcoded model lists

* Apply suggestion from @greptile-apps[bot]

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-04-27 17:26:30 -07:00
Ara 4afc973f7d feat(openai): add latest native models (#10435) 2026-04-27 16:16:19 -07:00
Bee 630c7f7c62 fix: line range validation for apply_patch (#253)
1. Keep the positive integer validation for individual
start_line/end_line, but move the cross-field start_line <= end_line
handling out of global input validation and into the per-request
execution loop.
2. Improve editor insertion validation and messaging so insert_line is
explicitly a positive one-based boundary line, allows appending at
line_count + 1, and returns accurate range errors.
2026-04-27 15:52:12 -07:00
Bee 987b0d242a feat: dispatch run_end hooks with AgentResult (#254)
Move run_end dispatching from HookBridge runtime hooks to SessionRuntime
so hooks receive the final host-facing AgentResult shape. Add sandbox
support and tests for run_end hooks, export AgentRunResult types, and
add a macOS notification plugin example.
2026-04-27 15:52:03 -07:00
Bee 5aa24b3c2e feat: Add editable queued prompt controls CLINE-1841 (#263)
Add pending prompt mutation support across core, hub, and desktop.

- Introduce action-based pendingPrompts API for list/update/delete
- Wire pending prompt commands and events through hub transports
- Add desktop sidecar handlers for editing, steering, and removing
queued prompts
- Add chat queue UI controls for Edit and Undo
- Cover pending prompt mutation behavior in transport tests
2026-04-27 15:41:59 -07:00
Robin Newhouse b1af38dc1e CLINE-1966 enable web fetch by default in act mode (#251)
## Summary
- enable web fetch by default in the act tool preset
- update runtime parity expectations for the new act-mode default

## Testing
- bunx vitest run src/runtime/runtime-parity.test.ts
src/extensions/tools/presets.test.ts --config vitest.config.ts

Linear: CLINE-1966
2026-04-27 15:26:22 -07:00
Robin Newhouse fdabfaea4b ENG-1869: expand live provider coverage and fix reasoning-disable routes (#252)
## Summary

Adds broad live-provider coverage for ENG-1869 and fixes routed
reasoning-disable behavior for Kimi and DeepSeek.

### What changed

- expanded the live provider matrix across smoke, tools, reasoning-on,
and reasoning-off suites
- split the old GLM mixed reasoning file into dedicated
reasoning-enabled and reasoning-disabled suites
- added reasoning-off coverage for GPT-5.3-Codex across direct and
routed paths
- aligned direct DeepSeek live coverage to the actual available model id
(`deepseek-v4-pro`)
- fixed `cline` gateway/provider-options handling so `reasoning.enabled:
false` is preserved for Moonshot Kimi K2.6
- fixed direct DeepSeek provider-options translation so
`reasoning.enabled` maps to the documented `thinking.type =
enabled/disabled` request shape
- clarified `@clinebot/llms` README guidance for reasoning-disabled
coverage and common live failure classes

## Coverage matrix

### Provider × suite

| Provider | Smoke | Tools | Reasoning on | Reasoning off | Total |
|---|---:|---:|---:|---:|---:|
| `cline` | 3 | 3 | 10 | 13 | 29 |
| `openrouter` | 4 | 3 | 5 | 5 | 17 |
| `anthropic` | 2 | 2 | 2 | 2 | 8 |
| `openai` | 2 | 2 | 2 | 2 | 8 |
| `gemini` | 1 | 1 | 1 | 1 | 4 |
| `vercel-ai-gateway` | 5 | 2 | 2 | 1 | 10 |
| `zai` | 0 | 0 | 3 | 3 | 6 |
| `deepseek` | 1 | 1 | 1 | 1 | 4 |
| `mistral` | 1 | 1 | 0 | 0 | 2 |
| **Total** | **19** | **15** | **26** | **28** | **88** |

### Model × suite

| Model / family | Provider paths covered | Smoke | Tools | Reasoning on
| Reasoning off |
|---|---|:-:|:-:|:-:|:-:|
| `claude-sonnet-4.6` | anthropic, cline, openrouter, vercel |  |  | 
|  |
| `claude-opus-4.7` | anthropic, cline, openrouter, vercel |  |  |  |
 |
| `gpt-5.4` | openai, cline, openrouter, vercel |  |  |  |  |
| `gpt-5.3-codex` | openai, cline, openrouter |  |  |  |  |
| `gemini-3.1-flash-lite-preview` | gemini, cline, vercel |  |  |  |
 |
| `deepseek-v4-pro` | deepseek |  |  |  |  |
| `deepseek/deepseek-v3.2` | cline | — | — | — |  |
| `glm-4.6` | cline, zai, openrouter | — | — |  |  |
| `glm-4.7` | cline, zai, openrouter | — | — |  |  |
| `glm-5` | cline, zai, openrouter | — | — |  |  |
| `kimi-k2-thinking` | cline | — | — |  | — |
| `kimi-k2.6` | cline | — | — |  |  |
| `qwen3-next-80b-a3b-thinking` | cline | — | — |  | — |
| `qwen3-next-80b-a3b-instruct` | cline | — | — | — |  |
| `qwen3.6-plus` | openrouter, vercel |  | — | — | — |
| `mistral-medium-3.1` | mistral, cline |  |  | — |  |
| `grok-4.20-beta`, `grok-4.1-fast` | cline | — | — | — |  |

## Testing

### Local/unit validation

- `bun -F @clinebot/llms test -- --run
src/providers/routing/provider-options.test.ts`
- `bun -F @clinebot/llms test -- --run
src/provider-live-reasoning.test.ts src/provider-live.test.ts
src/provider-live-tools.test.ts`

### Focused live validation

Validated the actual regression/fix paths with live runs:

- `cline-kimi-k2.6-thinking-off` 
- `openai/cline/openrouter gpt-5.3-codex reasoning-off` 
- `deepseek-v4-pro thinking-off` 

### Remaining live failures by class

These remain valuable tests and were kept in the suite:

- `Overloaded` on some Opus paths → provider/model capacity issue
- provider/account-specific availability issues when a model or account
is not usable in the current environment

## Notes

- PR base: `cline-1962-provider-options-refactor`
- Intentional policy: do not remove valuable failing live tests just
because a provider is overloaded or an account/model is unavailable;
instead classify and report those failures explicitly
2026-04-27 14:38:40 -07:00
Saoud Rizwan 17cb07ef1e docs: update README with public-facing SDK documentation (#228)
Replaces the internal workspace README (package list, dev commands,
mermaid diagram) with the public-facing SDK documentation. The new
README covers everything a developer needs to get started and understand
the SDK at a glance:

- Hero banner and nav links (Docs, Quickstart, Examples, Discord,
Reddit, Feature Requests)
- Quick code example showing the Agent API in ~10 lines
- Install instructions
- "What You Can Build" section with a Slack bot example demonstrating
conversation memory
- Custom tools with `createTool` and JSON Schema inputs
- Streaming events via `onEvent`
- Extensions for packaging reusable capabilities
- ClineCore full runtime with session persistence, built-in tools, and
config discovery
- Package table showing the layered stack (`@clinebot/sdk`, `core`,
`agents`, `llms`, `shared`)
- CLI usage examples (interactive, single prompt, scheduled agents,
Telegram connector)
- Provider support table (Anthropic, OpenAI, Google, Bedrock, Mistral,
OpenAI-compatible)
- Links to full documentation site
- Contributing and license sections
2026-04-27 11:27:28 -07:00
Ara 5fe6c9a8ce Add Z AI GLM-5.1 model (#10409) 2026-04-25 11:11:32 -07:00
Bee 12e33f5f8f feat: File-Based Automation (#240)
The File-Based Automation is a feature that works through:

- .cline/cron/*.md — one-off task specs
- .cline/cron/*.cron.md — recurring task specs
- .cline/cron/events/*.event.md — event-driven task specs

These files are parsed and executed by the CronService daemon
automatically, without requiring CLI commands. They're not exposed
through the schedule command—they're managed by writing/editing markdown
files in your workspace's .cline/cron/ directory.
2026-04-24 20:17:43 -07:00
Bee f78c4017cb refactor: overhaul root flags, drop duplicated subcommand, (#249)
Flags & UX:
- Rename -i/--interactive to -i/--tui; replace -T/--taskId with --id
- Replace --sandbox/--sandbox-dir with --data-dir (implicitly enables
sandbox)
- Replace --max-consecutive-mistakes with --retries; drop
--max-iterations and -u/--usage
- Promote --kanban to a `cline kanban` subcommand and remove the
`task`/`t` subcommand
- Hide -y/--yolo from --help while keeping it accepted at parse time
- Honor --data-dir in `cline auth` so credentials land under the chosen
data dir
- launchKanban now returns Promise<number> driven by spawn/error events
- schedule export: write to --to file path (JSON or YAML based on
extension)
- Drop maxIterations from connectors, ACP agent, scheduler, and zen
runtime

Hub defaults:
- Pick CLINE_HUB_DEV_PORT (25466) in dev builds, CLINE_HUB_PORT (25463)
in prod
- Add resolveDefaultCliRpcAddress() and use it across connector adapters
- Export CLINE_HUB_PORT/CLINE_HUB_DEV_PORT from @clinebot/shared
- New defaults.test.ts; pin connect.test.ts and daemon.test.ts to
production env
- resetModules() in client.test.ts so vi.doMock takes effect for dynamic
imports

Docs & tests:
- README: rebrand clite -> cline and update flag/subcommand references
- Update e2e/help/flags tests to match new flag surface
- Remove --taskId-specific error path now that --id replaces it
2026-04-24 18:38:01 -07:00
Robin Newhouse ccd10b09a1 Refactor AI SDK provider-options composition (#248)
## Summary

Refactors AI SDK `providerOptions` composition into an ordered
patch-based flow.

This replaces the previous ad-hoc conditional spread chain in
`ai-sdk.ts` with:
- a small composer (`composeAiSdkProviderOptions`)
- a shared patch merge utility
- explicit precedence between base, codex, provider-id/alias fanout,
gemini, and GLM/Z.AI overlays
- focused helpers for compatible-option concerns

Follow-up fixes after review:
- routed GLM providers no longer leak `thinking: { type: "adaptive" }`
when `reasoning.enabled === true`
- direct `google` no longer picks up generic OpenAI-compatible fanout
fields in the `google` bucket

Behavior is preserved for:
- GLM / Z.AI thinking routing
- OpenAI Codex
- Anthropic prompt cache / anthropic-compatible reasoning
- Cline gateway reasoning
- Google / Gemini thinking config

## What changed

### Moved provider-options composition out of `ai-sdk.ts`
- Removed the large inline `toAiSdkProviderOptions(...)` builder
- `ai-sdk.ts` now delegates to `composeAiSdkProviderOptions(request,
context)`

### Added a dedicated composer
New file:
- `packages/llms/src/providers/routing/provider-options.ts`

Key pieces:
- `mergeProviderOptionPatches(...)`
- `buildProviderAndAliasPatch(...)`
- `buildCompatibleProviderOptions(...)`
- `buildBaseProviderOptionsPatch(...)`
- `buildOpenAICodexProviderOptionsPatch(...)`
- `buildProviderFanoutPatch(...)`
- `buildGeminiProviderOptionsPatch(...)`
- `composeAiSdkProviderOptions(...)`

### Moved provider-specific ownership closer to existing routing modules
- `anthropic-compatible.ts`
  - now owns `buildAnthropicProviderOptions(...)`
- `glm-thinking.ts`
  - now returns a real `ProviderOptionsPatch`
- no longer uses the old custom `{ compatible, provider,
providerOptionsKey, handlesCompatibleThinking }` shape
- `utils.ts`
  - now owns shared `ProviderOptionsPatch`

### Added focused tests
New file:
- `packages/llms/src/providers/routing/provider-options.test.ts`

Coverage includes:
- patch merge semantics
- concrete provider id vs alias fanout
- anthropic direct-provider behavior
- routed GLM precedence, including `reasoning.enabled: true`
- native Z.AI behavior
- OpenAI Codex buckets
- Gemini `google.thinkingConfig`
- direct `google` bucket ownership

## Why this is better

Before, precedence was encoded implicitly inside one overlapping
object-construction chain.

Now, precedence is explicit and centralized:

```text
composeAiSdkProviderOptions(...)
  -> mergeProviderOptionPatches([
       base,
       codex override,
       provider-id + alias fanout,
       gemini,
       glm/zai overlay,
     ])
```

That makes it easier to:
- reason about precedence
- add provider-specific dialects safely
- keep provider-id and alias buckets in sync
- avoid routing drift between providers

## ASCII diagram 1: Before vs after

```text
BEFORE
======

ai-sdk.ts
  |
  '-- toAiSdkProviderOptions(...)
        |
        +-- anthropic bucket
        +-- openaiCompatible bucket
        +-- [providerId] bucket
        +-- [providerOptionsKey] alias bucket
        +-- openai-codex special case
        +-- cline special case
        +-- gemini/google special case
        +-- GLM/Z.AI special case
        +-- prompt cache special case
        +-- openai-native special case
        |
        '-- one overlapping spread chain
             with precedence encoded implicitly
             by line order


AFTER
=====

ai-sdk.ts
  |
  '-- composeAiSdkProviderOptions(...)
        |
        +-- shared values computed once
        |     +-- providerOptionsKey
        |     +-- isAnthropicCompatibleModelId
        |     +-- compatibleOptions
        |     '-- anthropicOptions
        |
        +-- ordered patch builders
        |     +-- buildBaseProviderOptionsPatch(...)
        |     +-- buildOpenAICodexProviderOptionsPatch(...)
        |     +-- buildProviderFanoutPatch(...)
        |     +-- buildGeminiProviderOptionsPatch(...)
        |     '-- buildGlmThinkingProviderOptionsPatch(...)
        |
        '-- mergeProviderOptionPatches([...])
              ^
              |
              '-- precedence is explicit and centralized
```

## ASCII diagram 2: Patch pipeline

```text
composeAiSdkProviderOptions(request, context)
|
|-- Step 0: derive shared inputs
|     |
|     +-- providerOptionsKey = toProviderOptionsKey(providerId)
|     +-- isAnthropicCompatibleModelId = isAnthropicCompatibleModel(...)
|     +-- compatibleOptions = buildCompatibleProviderOptions(...)
|     '-- anthropicOptions = buildAnthropicProviderOptions(...)
|
|-- Step 1: build patches
|     |
|     +-- [base]
|     |      {
|     |        anthropic: anthropicOptions,
|     |        openaiCompatible: compatibleOptions
|     |      }
|     |
|     +-- [codex override]
|     |      if providerId == "openai-codex"
|     |      {
|     |        openai: codexOptions,
|     |        [providerId]: codexOptions,
|     |        [alias]: codexOptions
|     |      }
|     |
|     +-- [provider fanout]
|     |      if providerId != anthropic
|     |         && providerId != openai-codex
|     |         && providerId != google
|     |      {
|     |        [providerId]: compatibleOptions (+ cline reasoning if needed)
|     |        [alias]: compatibleOptions (+ cline reasoning if needed)
|     |      }
|     |
|     +-- [gemini]
|     |      if providerId in {google, gemini} && reasoning.effort
|     |      {
|     |        google: {
|     |          thinkingConfig: { thinkingLevel, includeThoughts }
|     |        }
|     |      }
|     |
|     '-- [glm/zai overlay]
|            if GLM or native Z.AI logic applies
|            {
|              openaiCompatible: ...
|              [providerId]: ...
|              [alias]: ...
|            }
|
'-- Step 2: merge in order
       |
       '-- later patch wins within each bucket
```

## ASCII diagram 3: Provider behavior map

```text
                             +------------------------------+
                             | composeAiSdkProviderOptions  |
                             +--------------+---------------+
                                            |
                                            v
                           +-----------------------------------+
                           | shared compatibleOptions computed |
                           +----------------+------------------+
                                            |
         +--------------------+----------------------+--------------------+----------------------+
         |                    |                      |                    |                      |
         v                    v                      v                    v                      v

+----------------+  +----------------------+  +------------------------+  +----------------+  +----------------------+
| ANTHROPIC      |  | OPENAI-CODEX        |  | NORMAL COMPAT PROVIDER |  | DIRECT GOOGLE  |  | GLM/Z.AI OVERLAY     |
+----------------+  +----------------------+  +------------------------+  +----------------+  +----------------------+
| base:          |  | base:                |  | base:                  |  | base:          |  | applied last         |
| anthropic      |  | anthropic            |  | anthropic              |  | anthropic      |  |                      |
| openaiCompat   |  | openaiCompat         |  | openaiCompat           |  | openaiCompat   |  | native Z.AI GLM:     |
|                |  |                      |  |                        |  |                |  | thinking enabled/disabled
| no fanout      |  | codex adds:          |  | fanout adds:           |  | no generic     |  | into compat/provider/alias
|                |  | openai               |  | [providerId]           |  | provider fanout|  |                      |
| final:         |  | [providerId]         |  | [alias?]               |  |                |  | routed GLM:          |
| anthropic      |  | [alias?]             |  |                        |  | gemini patch   |  | reasoning enabled/exclude
| openaiCompat   |  |                      |  | final:                 |  | owns `google`  |  | into compat/provider/alias
|                |  | final:               |  | anthropic              |  | bucket         |  |                      |
|                |  | anthropic            |  | openaiCompat           |  |                |  | suppresses generic   |
|                |  | openaiCompat         |  | [providerId]           |  | final:         |  | adaptive thinking for|
|                |  | openai               |  | [alias?]               |  | anthropic      |  | routed GLM enabled   |
|                |  | [providerId]         |  |                        |  | openaiCompat   |  |                      |
|                |  | [alias?]             |  |                        |  | google         |  |                      |
+----------------+  +----------------------+  +------------------------+  +----------------+  +----------------------+
```

## Behavior preservation notes

### GLM / Z.AI
- Routed GLM providers still use `reasoning: { enabled | exclude }`
- Routed GLM providers no longer leak generic `thinking: { type:
"adaptive" }` when `reasoning.enabled === true`
- Native Z.AI GLM models still use `thinking: { type: "enabled" |
"disabled" }`
- Native Z.AI non-GLM models still suppress generic adaptive thinking
when `reasoning.enabled` is explicitly set

### OpenAI Codex
- Still emits:
  - `openai`
  - `[providerId]`
  - `[providerOptionsKey]` alias (when distinct)
- Still sets:
  - `instructions`
  - `store: false`
  - `systemMessageMode: "remove"`

### Anthropic / prompt cache
- Anthropic bucket logic is preserved
- anthropic-compatible reasoning remains unchanged
- prompt-cache behavior remains unchanged

### Gemini / Google
- `google.thinkingConfig` still only appears when `reasoning.effort` is
set
- direct `google` no longer accumulates OpenAI-compatible fields in the
`google` bucket

## Testing

Ran in `packages/llms`:

- `bun run typecheck`
- `bun run test`
- `bunx vitest run src/providers/routing/provider-options.test.ts`
- `bunx vitest run src/provider-live-reasoning.test.ts` with
`live-providers.glm-reasoning.example.json`
- `bunx biome format --write ...`
- `bunx biome lint --write ...`

Results:
- `102 passed`
- `3 skipped`
- `0 failed`

Live notes:
- GLM/Z.AI reasoning live suite passed after the routed-GLM suppression
fix
- smoke/cache suite still fails for the sample Gemini config because it
expects `cacheReadTokens > 0`

## Reviewer notes

The main thing to review is whether the new ordered patch composition
preserves the intended precedence:

```text
base
-> codex override
-> provider fanout
-> gemini
-> glm/zai overlay
```

The tests now lock down that precedence for:
- concrete provider id vs alias buckets
- codex buckets
- routed GLM overlays (`enabled: true` and `enabled: false`)
- native Z.AI behavior
- direct `google` bucket ownership
2026-04-24 18:32:20 -07:00
Bee 1224551916 chore: Model list update (#250) 2026-04-24 17:53:11 -07:00
Bee 71164e97c2 feat: Add v0 provider and support provider model overlays (#246)
Add v0 provider and support provider model overlays

Register v0 as a built-in OpenAI-compatible provider with generated
catalog models and V0_API_KEY documentation.

Extend provider metadata with source tracking and register custom
providers from providers.json so non-built-in OpenAI-compatible
providers are available through the runtime registry. Also allow
models.json entries to overlay models onto existing providers without
requiring full provider metadata.

Refresh generated model catalogs and add tests for v0 registration,
built-in model o

---------

Co-authored-by: Copilot <copilot@github.com>
2026-04-24 15:43:14 -07:00
Bee fd355957e5 feat: team tool results and improve CLI error logging (#247)
Add explicit Zod schemas for team tool result payloads and validate
outputs
before returning them from team tools. Normalize runtime timestamps to
ISO
strings so mailbox messages, task lists, run summaries, and outcomes
serialize
consistently through the tool boundary.

Also add CLI process-level error logging for uncaught exceptions,
unhandled rejections, task run failures, and interactive startup/turn
failures.
This ensures fatal and runtime errors are captured in CLI logs while
preserving
stderr output for users.

Update tests to cover serialized team timestamps and CLI process error
logging.
2026-04-24 15:09:26 -07:00
Robin Newhouse 30e99c8dbc CLINE-1955: Fix GLM thinking controls (#236)
## Summary

Fix GLM/Z.AI thinking controls in the SDK for both native Z.AI and
OpenRouter-routed GLM models.

Before this, GLM thinking control was effectively accidental:
- Thinking on worked because GLM defaults to thinking and/or routers
surfaced reasoning anyway.
- Thinking off did not work because the SDK dropped `thinking: false` in
some paths and never sent the provider-specific disable parameter.
- Native Z.AI and OpenRouter need different request shapes, but the SDK
treated them like generic OpenAI-compatible providers.

Now the behavior is explicit:
- Native Z.AI gets `thinking: { type: "enabled" }` or `thinking: { type:
"disabled" }`.
- OpenRouter GLM gets `reasoning/include_reasoning` controls.
- The GLM/Z.AI routing rules live in a focused helper instead of being
embedded directly in the generic AI SDK provider builder.

## Customer context

Requested by Samsung. Tracked in Linear as CLINE-1955.

## Validation

- `bun run typecheck` from `packages/llms`
- `bun run test` from `packages/llms`
- `git diff --check`
- Live GLM reasoning matrix with native Z.AI and OpenRouter keys sourced
from `~/.env`:
- Before fix: thinking-off still emitted reasoning chunks for native
Z.AI and OpenRouter GLM.
- After fix: native Z.AI and OpenRouter GLM thinking-on/off cases
passed.
2026-04-24 14:10:03 -07:00
Bee ece088ac4a feat: log session_start one ClineCore.start (#244)
tools now merge global/per-tool policy, deny disabled tools, and call
requestToolApproval before executing when autoApprove === false. Also
wired approval metadata through the hub in server.ts and bridged
approval.requested events back to the CLI approval callback in hub.ts so
this works for shared-hub sessions too.
2026-04-24 13:39:38 -07:00
Ara cc209ee877 Allow Ollama without an API key (#245)
## Summary
- allow the `ollama` OpenAI-compatible provider to skip API key
validation, matching local-provider behavior already used for LM Studio
- add focused tests covering the no-key exemption and the unchanged
behavior for other providers

## Validation
- `bun x vitest run packages/llms/src/providers/http.test.ts`
- `bun -F @clinebot/llms test`
- `bun x tsc -p packages/llms/tsconfig.json --noEmit`
- local CLI smoke test with `provider=ollama`, `model=ministral-3:3b`,
`baseUrl=http://127.0.0.1:11434/v1`, and `OLLAMA_API_KEY` unset
2026-04-24 18:14:00 +00:00
Dominic Cooney 901d1b5c97 fix(hooks): Use shell escapes on JSON literals in hooks templates (#10382)
* Fix quote escaping in hooks templates.

* Bump timeouts.

* Escaping for CONTEXT_MOD.

* Fix documentation and existing templates to use correct input property names.
2026-04-24 11:11:44 -07:00
tjandy98 c139f7a4d5 Add GPT-5.4 and GPT-5.4-nano (#10394)
Signed-off-by: tjandy98 <3953059+tjandy98@users.noreply.github.com>
2026-04-24 18:02:47 +02:00
Max 07593bb42a version bump and changelog (#10395)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-04-24 08:16:35 -07:00
Ara fd21c314c1 Adding Gpt-5.5 to OpenAI codex subscription provider (#10390) 2026-04-24 07:48:47 -07:00
Tomás Barreiro 32ca1cad9a Use env for github inputs (#10383) 2026-04-24 02:12:27 -07:00
Bee caa8c1c66e feat: OpenAI ChatGPT Subscription provider (#241)
Add OpenAI Codex-specific provider config handling in local runtime
bootstrap by:
- building headers with originator/session metadata
- merging configured and stored headers
- setting ChatGPT-Account-Id from persisted OAuth data or deriving it
from the access token payload

Also update AI SDK provider options for `openai-codex` to send
`instructions`, disable storage, and remove duplicated system messages.

Includes regression tests to verify stored and token-derived Codex
account IDs are correctly applied to request headers.fix(runtime):
populate Codex headers and request options

Add OpenAI Codex-specific provider config handling in local runtime
bootstrap by:
- building headers with originator/session metadata
- merging configured and stored headers
- setting ChatGPT-Account-Id from persisted OAuth data or deriving it
from the access token payload

Also update AI SDK provider options for `openai-codex` to send
`instructions`, disable storage, and remove duplicated system messages.

Includes regression tests to verify stored and token-derived Codex
account IDs are correctly applied to request headers.

demo:
```
cline-packages on  bee/gpt [$+] via 🥟 v1.3.10 on ☁️  beatrix@cline.bot 
❯ bun run cli hub stop
bun run cli hub ensure
bun run cli "hey"
bun run cli "hey" --json

$ bun --conditions=development --cwd apps/cli dev hub stop
$ CLINE_BUILD_ENV=development bun --conditions=development ./src/index.ts hub stop
{"stopped":true}
$ bun --conditions=development --cwd apps/cli dev hub ensure
$ CLINE_BUILD_ENV=development bun --conditions=development ./src/index.ts hub ensure
ws://127.0.0.1:59068/hub
$ bun --conditions=development --cwd apps/cli dev hey
$ CLINE_BUILD_ENV=development bun --conditions=development ./src/index.ts hey
Plan:
- This is a simple greeting with no coding context, so I can answer directly without tools.

Hey! What can I help you with?

$ bun --conditions=development --cwd apps/cli dev hey --json
$ CLINE_BUILD_ENV=development bun --conditions=development ./src/index.ts hey --json
{"ts":"2026-04-24T05:50:42.408Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":"Plan"}}
{"ts":"2026-04-24T05:50:42.426Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":":\n"}}
{"ts":"2026-04-24T05:50:42.440Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":"-"}}
{"ts":"2026-04-24T05:50:42.453Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" This"}}
{"ts":"2026-04-24T05:50:42.469Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" is"}}
{"ts":"2026-04-24T05:50:42.485Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" a"}}
{"ts":"2026-04-24T05:50:42.505Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" simple"}}
{"ts":"2026-04-24T05:50:42.505Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" greeting"}}
{"ts":"2026-04-24T05:50:42.530Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" with"}}
{"ts":"2026-04-24T05:50:42.545Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" no"}}
{"ts":"2026-04-24T05:50:42.569Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" coding"}}
{"ts":"2026-04-24T05:50:42.579Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" context"}}
{"ts":"2026-04-24T05:50:42.620Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":","}}
{"ts":"2026-04-24T05:50:42.620Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" so"}}
{"ts":"2026-04-24T05:50:42.646Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" I"}}
{"ts":"2026-04-24T05:50:42.658Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" can"}}
{"ts":"2026-04-24T05:50:42.763Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" answer"}}
{"ts":"2026-04-24T05:50:42.763Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" directly"}}
{"ts":"2026-04-24T05:50:42.763Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" without"}}
{"ts":"2026-04-24T05:50:42.763Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" tools"}}
{"ts":"2026-04-24T05:50:42.763Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":".\n\n"}}
{"ts":"2026-04-24T05:50:42.763Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":"Hey"}}
{"ts":"2026-04-24T05:50:42.785Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":"!"}}
{"ts":"2026-04-24T05:50:42.796Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" What"}}
{"ts":"2026-04-24T05:50:42.825Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" can"}}
{"ts":"2026-04-24T05:50:42.838Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" I"}}
{"ts":"2026-04-24T05:50:42.857Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" help"}}
{"ts":"2026-04-24T05:50:42.874Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" you"}}
{"ts":"2026-04-24T05:50:42.900Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":" with"}}
{"ts":"2026-04-24T05:50:42.921Z","type":"agent_event","event":{"type":"content_start","contentType":"text","text":"?"}}
{"ts":"2026-04-24T05:50:43.066Z","type":"run_result","finishReason":"completed","iterations":1,"usage":{"inputTokens":1491,"outputTokens":34,"totalCost":0.0042375},"durationMs":1318,"text":"Plan:\n- This is a simple greeting with no coding context, so I can answer directly without tools.\n\nHey! What can I help you with?","model":{"id":"gpt-5.4","provider":"openai-codex","info":{"id":"gpt-5.4","name":"GPT-5.4","contextWindow":1050000,"maxTokens":128000,"capabilities":["images","files","tools","reasoning","structured_output","prompt-cache"],"pricing":{"input":2.5,"output":15,"cacheRead":0.25,"cacheWrite":0},"releaseDate":"2026-03-05","family":"gpt"}}}
```

---------

Co-authored-by: Copilot <copilot@github.com>
2026-04-24 00:50:53 -07:00
Bee 0c7fb537c3 fix: Handle provider-managed tools in runtime and gateway (#239)
This changes tool execution semantics so providers that manage their own
builtin tools can stream tool activity without the SDK trying to inject
or execute custom runtime tools.

Key changes:
- add `provider-tools` as a provider capability and remove the old
negated `!tools` pattern
- restore `oauth` for `openai-codex`
- expose provider capabilities on the gateway provider manifest
- teach the AI SDK provider layer to:
  - skip passing `tools` for `provider-tools` providers
  - annotate streamed tool calls with `toolSource.executionMode`
- teach `AgentRuntime` to skip external tool execution when
`toolSource.executionMode === "provider"`
- simplify tool-call metadata to a minimal stable shape:
  - `providerId`
  - `modelId`
  - `executionMode`

Also included:
- unify `ProviderCapabilitySchema` in `shared` and restore the exported
`ProviderCapability` type alias in `llms/catalog/types`
- update tests for gateway/runtime behavior around provider-managed
tools

Suggested notes for reviewers:
- `openai-codex` now advertises `["reasoning", "oauth",
"provider-tools"]`
- streamed tool calls from provider-managed tool providers are still
preserved in history, but they are treated as non-executable by the
runtime
2026-04-23 22:05:19 -07:00
Bee 85bccdac23 fix: team tools abort task (#238)
- fix issue where teammate shutdown cause tasks to abort
- Changed team_run_task so concurrent sync calls to the same agent now
share the first in-flight promise instead of throwing.
2026-04-23 21:37:03 -07:00
Bee 59d9d29e56 refactor: update default server port number (#237)
4317 to 25463
2026-04-23 20:26:00 -07:00
Bee 3d86101c60 chore: Agent public interface (#234)
AgentPlugin and Agent as public interface
2026-04-23 16:43:36 -07:00
Bee 103026dfa3 fix: message content blocks reconstructure CLINE-1953 (#235)
fix tool results not showing to agents:

```
sdk on  main [$!?] via 🥟 v1.3.10 on ☁️  beatrix@cline.bot
❯ bun run cli "tell me about this repo"
$ bun --conditions=development --cwd apps/cli dev "tell me about this repo"
$ CLINE_BUILD_ENV=development bun --conditions=development ./src/index.ts "tell me about this repo"
[thinking]  I should start by exploring the repository structure to understand what we're working with.
[run_commands] ls /Users/beatrix/dev/sdk; ls /Users/beatrix/dev/sdk/apps/cli
   ⎿ AGENTS.md ARCHITECTURE.md CONTRIBUTING.md DOC.md README.md apps biome.json bu... (+1 more)
[read_files] {"files":[{"path":"/Users/beatrix/dev/sdk/README.md"},{"p...
   ⎿ 1 | # Cline SDK Packages  2 |   3 | _CONFIDENTIAL AND PROPRIETARY_  4 |   5 |... (+2 more)
Here's an overview of this repo.

## Repo: Cline SDK (`/Users/beatrix/dev/sdk`)

A **confidential/proprietary Bun workspace monorepo** containing the packages and host apps that power Cline agent runtimes. Your current working directory (`apps/cli`) is one of the host apps.

### Top-level layout

```
sdk/
├── packages/ # Published SDK packages + internal enterprise package
├── apps/            # Host apps (cli, vscode, examples)
├── scripts/         # Build/version/publish automation
├── AGENTS.md # Development reference (package boundaries, change
routing)
├── ARCHITECTURE.md  # System design & runtime flows
├── CONTRIBUTING.md  # Onboarding + publishing workflow
├── DOC.md           # API reference
├── README.md        # Visitor overview
├── biome.json       # Formatter/linter config
├── bun.lock         # Bun lockfile
├── mise.toml        # Tool-version manager config
└── vitest.workspace.ts
```

### SDK package stack (strict dependency direction)

```
shared → llms → agents → core → apps
                           ↑
                     enterprise (internal only)
```

| Package | Role |
|---|---|
| `@clinebot/shared` | Contracts, schemas, path helpers, hook engine, extension registry, low-level utils |
| `@clinebot/llms` | Provider settings/config, model catalogs, provider manifests, gateway contracts, handler creation |
| `@clinebot/agents` | **Stateless** agent loop, tool orchestration, hook/extension run^C

sdk on  main [$+] via 🥟 v1.3.10 on ☁️  beatrix@cline.bot took 17s
❯ bun run cli "@/Users/beatrix/Pictures/hero-character.png describe this image"
$ bun --conditions=development --cwd apps/cli dev "@/Users/beatrix/Pictures/hero-character.png describe this image"
$ CLINE_BUILD_ENV=development bun --conditions=development ./src/index.ts "@/Users/beatrix/Pictures/hero-character.png describe this image"
[thinking]  The user is asking me to describe an image, so I need to locate it and examine what's shown.
[run_commands] find /Users/beatrix/dev/sdk -name 'hero-character.png' 2>/dev/null | head -5
   ⎿ ok
I can see the image directly in the conversation. Here's a description:

The image shows a cute, cartoon-style chibi character standing atop a chunky, floating rock platform.

**The character:**
- Has a large, square/boxy white head that takes up most of its body proportions
- Two big, oval black eyes dominate the face, giving it an adorable, innocent expression
- No visible mouth or nose — just the simple eyes
- Small pointed ear-like shapes or spikes stick out from the sides of the head
- A small rounded nub or antenna sits on top of the head
- The body is small, white, and simple with stubby arms and legs
- Subtle pink/blush shading accents the cheeks, underside of the head, and body, giving it a soft, plush-toy feel
- Bold dark outlines in a deep purple/maroon color define the shapes

**The platform:**
- A chunky, floating island made of dark navy/indigo-purple rock
- The top surface has a cobblestone-like texture with rounded, lighter-purple stone tiles
- The sides taper down with cracks and crevices, suggesting it's been torn from the ground
- A few small rock fragments or "leaf-like" shapes float off to the left, reinforcing the sense that the platform is suspended in mid-air
- A soft shadow is cast beneath it on an invisible surface

**Overall style:**
The artwork has a hand-drawn, slightly sketchy quality with visible linework and flat-to-soft cel shading. The limited palette — whites/pinks for the character against deep purples for the platform — creates strong contrast and a dreamy, game-asset/indie-platformer vibe (reminiscent of mascot art for games like *Hollow Knight* or mobile platformers). It feels like a hero character splash image, perhaps for a title screen or promotional art.
```
2026-04-23 15:50:05 -07:00
Bee 0d6d07bd00 fix: unset maxIterations limit (#233)
The runtime no longer injects 12 as a fallback. The loop now runs
unbounded when config.maxIterations is undefined, and still respects an
explicit numeric cap when provided. Back to behavior from before.
2026-04-23 15:28:00 -07:00
Ara 8e74e294b2 Read nested raw cache usage before fallback (#231)
## Summary

This PR teaches gateway usage normalization to read prompt cache write
metrics from nested provider usage fields that are already present on
the response payload.

Some providers report cache write information inside `usage.raw` instead
of exposing it on the top-level usage object. When that happens, the
gateway currently normalizes token counts and cost, but it can miss the
cache-write-specific field.

This change makes cache-write extraction more complete by reading the
provider-native nested raw shape before finishing normalization.

## What This PR Does

The normalization path in `packages/llms/src/providers/ai-sdk.ts` now
also checks:

- `usage.raw.cache_creation_input_tokens` -> `cacheWriteTokens`

That value is mapped into the normalized gateway usage object alongside
the existing token and cost fields.

## Why

Prompt cache writes are part of the usage data we surface to the rest of
the SDK.

If a cache write metric is present in the provider response, we should
carry it through normalization the same way we already carry through
input tokens, output tokens, and cost. Otherwise downstream consumers
can receive incomplete usage for the same response.

This PR is only about reading and preserving that cache-write field when
it is already available.

## Example Shape Covered

This PR handles provider usage payloads shaped like:

```json
{
  "usage": {
    "inputTokens": 15997,
    "outputTokens": 4,
    "raw": {
      "cache_creation_input_tokens": 22
    }
  }
}
```

That now normalizes to usage including:

```json
{
  "inputTokens": 15997,
  "outputTokens": 4,
  "cacheWriteTokens": 22
}
```

## Scope

- extend usage normalization in `packages/llms/src/providers/ai-sdk.ts`
- preserve provider-native nested raw cache write values in the
normalized usage object
- add one focused regression test in
`packages/llms/src/providers/gateway.test.ts`

## Validation

- `bun -F @clinebot/llms test src/providers/gateway.test.ts`
- `bun -F @clinebot/llms test src/providers/gateway.test.ts -t "reads
cache write tokens from nested raw usage"`
2026-04-23 21:55:57 +00:00
Bee 94c75df558 feat: add stale code-sidecar process detection and cleanup (#230)
Add support for detecting and killing stale `code-sidecar` processes in
the `doctor` command. This includes:

- New `listStaleSidecarPids()` function using `pgrep` to find stale
sidecar processes by path pattern `/src-tauri/bin/code-sidecar`
- `staleSidecarPids` field added to `DoctorStatus` type
- `sidecarProcesses` kill count included in `--fix` output report
- Sidecar PIDs displayed in human-readable doctor output
- `--fix` flag now kills stale sidecar targets alongside hub/CLI procs
- Updated hint message to mention stale sidecars when applicable
- Full test coverage for the new sidecar detection and kill behavior
2026-04-23 14:35:15 -07:00
Tomás Barreiro 852c65b70c Do not show hardcoded new items (#10374)
* Do not show hardcoded new items

* fix use effect dependencies

* fix tests
2026-04-23 11:52:03 -07:00
Bee 6f9a3b85a2 feat: add --kanban flag to launch kanban app from CLI (#226)
Adds a new `--kanban` CLI option that spawns the kanban process in a
detached background process and exits. If kanban is not installed, a
helpful error message is shown directing users to install it via `npm i
-g kanban`. Includes unit tests covering both the happy path and the
missing-binary error case.
2026-04-23 01:30:28 -07:00
Bee 64c8ddbcce refactor: makes @clinebot/agents stateless (#219)
1. Agents package becomes a thin, **stateless** agentic-loop executor
that exports only `AgentRuntime`, `createAgentRuntime`, `AgentRunInput`,
`AgentEventListener` plus type re-exports from `@clinebot/shared`.
2. Everything stateful (conversation store, session identity,
OAuth/connection refresh, loop-detection counters, consecutive-mistake
tracking, team/delegated-agent orchestration, message-builder caches,
hook-file glue) moves to `@clinebot/core`.
3. Every hook, event, and log message that the old package emits must
still fire after the swap
4. `@clinebot/shared` becomes the single source of truth for every type
both packages need; the `packages/agents/src/types.ts` re-export
indirection is deleted.

---------

Co-authored-by: consumer-migrator <consumer-migrator@cline.bot>
Co-authored-by: cline <cline@bot>
Co-authored-by: migration-lead <lead@team.local>
Co-authored-by: impl-consumer-migrator <impl-consumer-migrator@team.local>
Co-authored-by: impl-core-architect <impl-core-architect@team.local>
Co-authored-by: impl-runtime-porter <impl-runtime-porter@team.local>
Co-authored-by: impl-session-fixer <impl-session-fixer@team.local>
Co-authored-by: Copilot <copilot@github.com>
2026-04-23 01:29:24 -07:00
Mikołaj Kondratek 70f0e8d548 feat(memory-observability): add periodic memory logging to cline-core (#10343)
* feat(memory-observability): add periodic memory logging to cline-core

Introduces a lightweight memory monitor that logs process.memoryUsage()
snapshots to the existing cline-core log every 5 minutes, plus an
immediate baseline at startup and a final snapshot at graceful shutdown.

Each entry is written as a single `[MEMORY] key=valueMB ...` line so it
is trivially greppable and parseable:

    grep '\[MEMORY\]' ~/.cline/cline-core-service.log

The timer is unref()'d so it does not keep the event loop alive on its
own, ensuring the Node process can still exit cleanly.

Also adds an informational log line after process.chdir(__dirname) that
records where V8 will write heap snapshots if --heapsnapshot-near-heap-limit
triggers them, and a best-effort process.on("exit") handler that scans
cwd for .heapsnapshot files on abnormal exit and logs their paths/sizes
so post-mortem investigation starts with the diagnostic data in hand.

This is Part 1 (periodic memory logging) and the Node-side portions of
Part 2 (snapshot directory + exit handler) of the memory observability
implementation plan. The V8 flag itself and the
~/.cline/heapsnapshots/ move-and-cap cleanup live in the Kotlin
CoreProcessManager and are applied separately in the plugin repo.

No business-logic changes; purely additive diagnostics.

* chore(memory-observability): enable --heapsnapshot-near-heap-limit=3 in runclinecore.sh

When cline-core approaches the V8 heap ceiling, V8 will now write up to
3 .heapsnapshot files to the current working directory before giving up
and crashing. These snapshots can be loaded into Chrome DevTools → Memory
tab to identify the objects retaining the most memory.

N=3 is chosen because the last snapshot (written just before the fatal
OOM) shows only live, truly-unreclaimable objects — the earlier ones still
contain garbage the GC hadn't collected yet. Having all three lets us
compare.

This flag is a V8 runtime flag and must be passed on the node command
line; it cannot be enabled from JavaScript at runtime.

Matches the equivalent change on the cline-core launcher in the IntelliJ
plugin repo (CoreProcessManager.kt).

* chore(memory-observability): reduce --heapsnapshot-near-heap-limit from 3 to 1

Reviewer concern: with --max-old-space-size=8192, each heap snapshot
serializes at roughly 4-5x heapUsed on disk, so three snapshots can
burst 24-40 GB to disk in the seconds before an OOM crash — right
when the system is already under memory/CPU pressure. On a laptop
with <40 GB free this can leave partial/corrupted snapshots or
trigger OS pressure on unrelated processes.

The plan doc originally argued 'snapshot 3 of 3 is most valuable
because it contains only live objects'. In practice, by the time V8
triggers the flag it has already run aggressive mark-compact cycles,
so snapshot 1 is nearly-all-live too. Our own Scenario B verification
run confirmed that even the first snapshot contained the retainer
chain — snapshots 2 and 3 added no diagnostic signal.

Trade-off:
  - per-OOM disk burst:      24-40 GB  ->  8-14 GB  (3x reduction)
  - time-to-crash (frozen):  30-60 s   ->  10-20 s  (3x reduction)
  - diagnostic signal:       essentially unchanged

The persistent-directory cap in CoreProcessManager.kt stays at 3, so
we still retain snapshots from the 3 most recent OOM events for
cross-event comparison.

* chore(memory-observability): shorten runclinecore.sh flag comments

The one-line pointer to CoreProcessManager.kt was more noise than
signal given the flags are visible on the same line as the command.
Rationale for the --heapsnapshot-near-heap-limit value lives in the
Kotlin constant's KDoc and in the commit log.
2026-04-23 13:34:51 +09:00
Ara 03540c565f Normalize provider usage costs before gateway fallback (#148)
## Summary

This change adds a provider-specific usage normalization seam for
openai-compatible providers and uses it to normalize nested upstream
cost fields before the generic gateway fallback runs.

## What changed

- add a `normalizeUsage` hook to the provider factory result
- add `packages/llms/src/gateway/usage-normalizers.ts`
- wire `cline`, `openrouter`, and `vercel-ai-gateway` through
provider-specific usage normalizers
- update gateway usage normalization to prefer provider-normalized
totals before generic fallback pricing
- add tests covering nested `usage.raw` cost handling for finish-part
and `stream.usage` paths

## Why

The current gateway cost path only looks at top-level usage cost fields.
For real AI SDK responses from these providers, the cost values often
live under `usage.raw`, so the gateway falls back to local pricing even
when upstream cost data is present.

This PR makes the provider layer responsible for translating
provider-specific raw usage into the canonical gateway shape, while
preserving the generic pricing calculation as a fallback when a provider
cannot supply a trusted total.

## Provider behavior in this patch

- `cline`: prefer nested upstream market-cost style fields
- `vercel-ai-gateway`: prefer nested upstream market-cost style fields
- `openrouter`: normalize nested `cost` plus `upstream_inference_cost`
into a billed total before the gateway fallback path

## Validation

- `bun -F @clinebot/llms test`

## Notes

- repo-wide `bun run types` is currently failing on this branch due to
existing workspace type issues unrelated to this patch, so I used the
package test suite as the relevant validation for this change.
2026-04-22 20:50:12 -07:00
TheRealSpencer 5accd88d73 fix: pin protobufjs to 7.5.5 to address CVE-2026-41242 (#10365) 2026-04-22 20:22:32 -07:00
Robin Newhouse c1c11c186d fix: stamp per-turn usage metrics on assistant messages (CLINE-1923) (#221)
## Problem

Usage attached to each assistant message in was showing the final
session totals instead of per-turn usage.

Each agent loop iteration produces one assistant message, but all of
them were getting the cumulative token sum from the entire run. In a
two-iteration run (e.g. tool call → text reply), the last assistant
message showed (session total) instead of (that turn's actual usage).

## Root Cause

received — the accumulated total across the entire run — and stamped it
on the last new assistant message. Per-turn usage from was available in
the agent loop but never persisted onto the messages.

## Fix

****: Stamp per-turn metrics onto each assistant message immediately
after returns, before appending it to the conversation store. The
metrics object uses the turn's own //etc., with optional fields
conditionally spread to avoid keys.

****: Update to preserve existing per-turn metrics already on messages.
Only falls back to for the terminal message when no metrics are present
— backward-compatible for any path that doesn't go through the agent
loop.

## Tests

- ****: New unit test verifying per-turn metrics are preserved and not
overwritten with session totals. Renamed fallback test to clarify it
covers the legacy/non-agent-loop code path.
- ****: Updated mock to include per-turn metrics on both assistant
messages (matching real agent behavior). Changed assertion from to .
- **** (new): End-to-end test with a VCR cassette covering a two-turn
run (tool call → text reply). Asserts each assistant message carries its
own token counts, not the session total. The cassette uses distinct
values (1000/25 and 1500/40) chosen so the session total (2500/65) can't
be confused with either per-turn value.
- ****: Added note that the CLI build bundles packages from compiled —
rebuilding packages before the CLI is required when testing changes
end-to-end.

## Closes

CLINE-1923

---------

Co-authored-by: abeatrix <beatrix@cline.bot>
2026-04-22 19:14:04 -07:00
Saoud Rizwan 5b29be63b8 docs: remove demo video from README (#10363) 2026-04-22 18:46:17 -07:00
Ara aeb80e5c80 Fix Windows Python hook execution (#224)
## Summary

This PR fixes Python hook invocation on Windows by using the standard
`py -3` launcher instead of assuming `python` is available in PATH.

## Scope

- use `py -3` when inferring Python hook interpreters on Windows
- use `py -3` for `.py` hook files on Windows
- isolate home/cline directory state in the hook file tests
- keep the existing hook behavior unchanged on non-Windows platforms

## Why

Windows environments often provide the Python launcher as `py` instead
of a plain `python` executable. That made Python hook execution and its
tests less reliable on Windows.

## Validation

- `bunx vitest run src/hooks/hook-file-hooks.test.ts --config
vitest.config.ts`
2026-04-22 18:18:21 -07:00
Bee 096db9127f Cline SDK 0.0.36 (#220) 2026-04-22 15:33:31 -07:00
Bee 16372e6176 refactor: fold @clinebot/hub into @clinebot/core (#218)
Absorb @clinebot/hub into @clinebot/core/hub. The hub package was a thin
facade plus two client wrappers that all depended on @clinebot/core, so
   consolidating eliminates a duplicate spawn path and ~1100 lines of
   package overhead.

   Moved into packages/core/src/hub/:
   - defaults.ts (endpoint defaults + env resolution)
- daemon.ts (spawnDetachedHubServer, ensureDetachedHubServer,
prewarmDetachedHubServer) using the real daemon entry file instead of
the weaker node -e bootstrap that client.ts had inlined
- daemon-entry.ts (CLI arg parsing + startHubWebSocketServer), exposed
as the new @clinebot/core/hub/daemon-entry subpath export
- connect.ts (connectToHub, resolveHubUrl, sendHubCommand,
probeHubConnection), renamed from client.ts to avoid colliding with the
existing NodeHubClient module
   - session-client.ts (HubSessionClient)
   - ui-client.ts (HubUIClient)
- start-shared-server.ts (startHubServer, ensureHubServer shared-owner
wrappers around startHubWebSocketServer / ensureHubWebSocketServer)
   - moved tests: daemon.test.ts, connect.test.ts, ui-events.test.ts

   Collapsed the two spawn paths: packages/core/src/hub/client.ts no
longer defines spawnDetachedLocalHub / buildDetachedHubBootstrapCode /
   parseLocalEndpointOverride / isBunExecutable / resolveHubModuleUrl.
   ensureCompatibleLocalHubUrl now spawns via the unified
   spawnDetachedHubServer (real daemon-entry file with --cwd, log file,
   port-0 fallback, bun --conditions=development) while keeping its own
   build-ID-aware waitForCompatibleHubUrl so ClineCore still rejects
   mismatched builds.
2026-04-22 14:58:56 -07:00
Robin Newhouse 4887da4868 Lock messages.json as canonical replay contract for ATIF (#215)
## Purpose
This PR hardens the persisted session messages artifact as a canonical
replay/export contract for downstream ATIF conversion. The intent is
that successful replay/export flows can rely on `messages.json` alone
without requiring `hooks.jsonl` for usage/model correlation.

## Context
A real Harbor trial showed normalized content blocks present in
persisted messages, but replay correctness still depended on combining
artifacts in some paths. Existing tests validated helper logic, but not
the full persisted-file contract end-to-end.

## What changed
### 1) Core runtime contract coverage (new)
- Added a **LocalRuntimeHost e2e** test that runs a real turn and reads
the actual persisted artifact from disk.
- The test asserts persisted replay-critical content and metadata:
- message parts: `thinking`, `tool_use`, `tool_result`, final assistant
`text`
  - assistant `modelInfo` on turn messages
  - assistant `metrics` on terminal turn message with:
    - `inputTokens`
    - `outputTokens`
    - `cacheReadTokens`
    - `cacheWriteTokens`
    - `cost`

File:
- `packages/core/src/transports/local.e2e.test.ts`

### 2) Explicit failure-path contract coverage (new)
- Added a transport test for failure before assistant output.
- Confirms persisted snapshot remains valid and **does not fabricate
synthetic assistant usage/model metadata** when no assistant output
exists.

File:
- `packages/core/src/transports/local.test.ts`

### 3) Retry/recovery success-path metadata assertion (strengthened
existing test)
- Expanded the auth retry test to assert that after forced refresh +
successful retry, persisted assistant message still contains full
`modelInfo` and `metrics` (including cache token fields).

File:
- `packages/core/src/transports/local.test.ts`

### 4) Sidecar metadata extraction completeness
- Extended sidecar usage metadata extraction to include cache token
fields from persisted `metrics`.
- This keeps sidecar/history adapters aligned with the canonical
persisted metrics shape.

File:
- `apps/code/sidecar/session-data/messages.ts`

### 5) Contract docs clarification
- Updated app docs to explicitly state:
- `~/.cline/data/sessions/<sessionId>/<sessionId>.messages.json` is the
canonical replay/export artifact
- `hooks.jsonl` is auxiliary observability/debug data and not required
for normal replay/export

File:
- `apps/code/README.md`

### 6) Temporary live contract script (kept for dev validation)
- Added an opt-in script that runs:
  - `bun run build`
  - headless CLI turn
  - persisted messages artifact validation for canonical fields

File:
- `scripts/tmp/e2e-headless-messages-check.sh`

## Why this is the right scope
- Strengthens contract guarantees where they matter (core persisted
artifact path).
- Adds end-to-end protection without changing canonical writer schema.
- Avoids introducing dual-authoritative artifacts.
- Keeps hooks as observability/debug rather than replay dependency.

## Validation performed
- `bun test packages/core/src/transports/local.e2e.test.ts`
- `bun test packages/core/src/transports/local.test.ts -t "does not
synthesize assistant usage metadata when a turn fails before assistant
output"`
- `bun test packages/core/src/transports/local.test.ts -t "force
refreshes and retries once when turn fails with auth error"`
- `bun run typecheck` (from `apps/code`)
- `scripts/tmp/e2e-headless-messages-check.sh`

## Non-goals
- No Harbor-specific logic.
- No ATIF exporter implementation in sdk-wip.
- No canonical schema migration.

## Follow-up (optional)
If desired, the temporary CLI contract script can be promoted from
`scripts/tmp` into a first-class CI/live check once the team settles on
cadence and environment gating.

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 21:23:05 +00:00
Bee ad6e5cf740 chore: remove type declarations from builds and add macOS quarantine fix (#217)
- Remove TypeScript declaration generation from `enterprise` and `hub`
package builds, dropping `tsc` step and `types` fields from exports
- Add `rm -rf dist` before build to ensure clean output directories
- Add `afterBuildCommand` to strip macOS quarantine attribute from Cline
Hub.app after Tauri build
2026-04-22 13:25:40 -07:00
Bee 710e11722d feat: add custom fetch support for runtime sessions (#216)
Add a `fetch` option to `ClineCoreOptions` that allows consumers to
inject a custom HTTP implementation (e.g. proxies, retries, tracing,
or test doubles) into AI gateway providers used by local sessions.

The custom fetch is threaded through `prepareLocalRuntimeBootstrap`
via a new `defaultFetch` parameter, populating `providerConfig.fetch`
unless overridden by a per-session or per-provider fetch. The option
is forwarded from the host runtime bootstrap path and only applies to
local execution; hub and remote runtimes route HTTP through their own
shared process.

Tests cover three scenarios: defaultFetch is used when supplied,
per-session fetch takes precedence over defaultFetch, and
providerConfig.fetch remains unset when no fetch is provided.

---------

Co-authored-by: Copilot <copilot@github.com>
2026-04-22 13:22:20 -07:00
Bee e24e4a33b7 feat: add zen mode for fire-and-forget background task execution (#214)
Introduces a new `--zen` (`-z`) CLI flag that dispatches a task to the
background hub daemon and exits immediately, enabling fire-and-forget
workflows for long-running tasks.

Key changes:
- Add `-z, --zen` option to CLI program options
- Map `zen` as a new task mode alongside `act`, `plan`, and `yolo`
- Zen mode auto-enables full tool approval (yolo semantics) since no
human is in the loop after CLI exits
- Disable `spawn`/`team` tools by default in zen mode for safety
- Incompatible with `--sandbox` and `--interactive` flags
- Menubar app surfaces a system notification on task completion via hub
`ui.notify` events
- Update README with zen mode documentation, usage examples, and
behavior details
- Fix `--max-consecutive-mistakes` description to be mode-agnostic
- Consolidate `-a, --act` flag; act is now the default mode
2026-04-22 12:48:10 -07:00
Max 9dea336ced update changelog (#10356)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-04-22 11:03:35 -07:00
Tomás Barreiro 697f801937 Use the recommended model list for the onboarding flow (#10355)
* Use the recommended model list for the onboarding flow

* show the welcome view if we fail to load models
2026-04-22 19:32:45 +02:00
Bee cb9e0968b7 fix: update model list generation scripts (#213)
Fix outdated output file path and add doc strings.
2026-04-22 10:32:03 -07:00
Tomás Barreiro 6be35bfcea Remove old hardcoded banners (#10354)
* Remove old hardcoded banners

* Remove tests
2026-04-22 10:21:01 -07:00
Bee 059c958746 fix: avoid blocking auto runtime on hub startup (#212)
Keep CLI auto runtime selection off the hub startup critical path.

- change core auto backend selection to probe only for an
already-running compatible local hub
- fall back immediately to local runtime when no compatible hub is
available
- preserve explicit hub mode behavior, which still requires and waits
for a hub
- gate text hook event printing behind --verbose
- stop CLI startup prewarm from awaiting hub startup for prompt runs
- add regression coverage for runtime host selection and hook output
behavior

This fixes the first-token latency regression introduced by the
hub/spoke runtime routing changes, where normal CLI runs could wait on
detached hub startup before the session began.
2026-04-22 10:20:46 -07:00
Ara 5a91800b6c Add SDK nightly publish workflow (#10344)
* chore: add sdk nightly publish workflow

* chore: publish sdk nightly to prerelease channel
2026-04-22 10:13:56 -07:00
Jose Castelli dacadbaae0 use details instead of catched error message (#10353)
use details instead of caught error message
2026-04-22 16:50:45 +02:00
Bee d7c7cefae2 feat: propagate workspace context through agent, plugins, and hooks (#211)
Pick up from https://github.com/cline/sdk-wip/pull/146

Pass `cwd`, `workspaceRoot`, and `workspaceInfo` from agent config into
the contribution registry setup context, subagent spawning, plugin
sandbox bootstrap, and hook payloads so that all downstream consumers
(plugins, hooks, sandbox subprocess) have consistent workspace metadata
without needing to re-derive it independently.

Key changes:
- Forward workspace env to `createContributionRegistry` via
`setupContext`
- Include workspace fields when spawning subagents
- Add `workspaceRoot` option to `ResolveAndLoadAgentPluginsOptions` and
pass it into the sandbox subprocess
- Expose `SessionWorkspaceEnv` from `@clinebot/shared` in agents types
- Add `PluginSetupCtx` interface and pass it to plugin `setup()` hooks
- Include `workspaceInfo` in base hook payloads for subprocess hooks
- Export `SessionWorkspaceEnv` from shared package
2026-04-22 03:38:38 -07:00
Bee 461d7b848b fix: Scope hub runtime event subscriptions per session (#210)
Add session-scoped hub subscriptions across the runtime stack.

- extend the RuntimeHost subscribe contract with optional session
filters
- make RuntimeHostEventBus enforce session-scoped delivery
- add per-session subscribe/unsubscribe handling to NodeHubClient
- have HubRuntimeHost manage stream subscriptions per active session
- wire one-shot CLI runs to preallocate a session id and subscribe only
to that session
- add coverage for scoped hub subscriptions and teardown behavior

This fixes cross-session event fanout where one CLI process could render
assistant output from another process connected to the same shared hub.
2026-04-22 03:38:27 -07:00
Bee 890530ade3 refactor: update executor to support freeform patch format (#192)
Rewrites the apply-patch executor to natively support the documented
GPT-5 freeform patch grammar without requiring shell wrappers. The
legacy `apply_patch <<"EOF"` shell wrapper form is still tolerated for
backward compatibility with older prompts.

Key changes:
- Replace `stripBashWrapper` with `normalizePatchInput` that detects and
handles both freeform and legacy wrapped patch inputs
- Add `NormalizedPatchInput` interface for cleaner internal typing
- Improve validation to reject incomplete patch sentinels early
- Add comprehensive test coverage for all three input variants: freeform
patch body, legacy shell wrapper, and invalid input
2026-04-22 03:10:51 -07:00
Bee 45715ae5a6 feat: update command for CLI (#171)
add command to update CLI when there is a new version
2026-04-22 02:42:16 -07:00
Bee 90445ccc96 feat: Cline Hub Monitor (#208)
Start Cline Hub in menubar
2026-04-22 02:26:22 -07:00
Bee 70724db55d fix: TUI rendering issue (#209)
The interactive TUI is an ink app that continuously redraws the terminal
in place, so highlighted text will keep get canceled because of the
re-rendering that constantly happens. This PR makes the TUI easier to
copy from by reducing or pausing repaints when idle.
2026-04-22 01:44:39 -07:00
Bee 6a01009d73 feat: add description field to runtime commands and workflow config (#207)
- Add optional `description` field to `WorkflowConfig` and
`AvailableRuntimeCommand` types
- Implement `truncateSplit` utility to truncate strings at a delimiter
(e.g., first sentence)
- Populate command descriptions using `description` or fallback to
truncated `instructions`
- Export `truncateSplit` from shared package (both browser and node
entry points)
- Use actual command description in CLI interactive welcome instead of
generic kind label
- Fix indentation in `parseKeyPairsIntoRecord` utility
2026-04-21 23:38:36 -07:00
Dominic Cooney 8d020e89e6 chore: Publish regular nightlies to Cline (Nightly) *release* channel (#10338)
* Make the nightly publishing script use the stable channel of cline-nightly.

* Address PR review feedback from Greptile and Copilot

- Reject unknown CLI flags with an error message, preventing typos like
  --prerelease from silently publishing to the wrong channel (Greptile)
- Rename 'stable' to 'release' throughout docs, help text, and log
  messages to match VS Code Marketplace terminology (Copilot)
- Rename workflow step from 'Publish Extension as Pre-release' to
  'Publish Nightly Extension' since it now publishes to the release
  channel by default (Greptile)
2026-04-21 21:26:57 -07:00
Tomás Barreiro 20d9f9b0e6 Parse headers with = signs in the values (#204)
We need to support header values with `=` in them. The current logic
splits at the equal signs, instead of simply finding the first one.
This PR refactors it so that we split at the first one instead.
2026-04-21 20:36:52 -07:00
Bee 7a042fe257 refactor: Hub & Spoke (#203)
- Remove rpc and scheduler packages -Add `packages/hub` as a first-class
workspace package
- Move Scheduling Into The Hub
- Rewire schedule-triggered runtime execution so the hub assigns work to
spokes instead of delegating through RPC services
- Replace RPC schedule CRUD and execution APIs with hub-native command
handlers and shared schedule/event types.
- Replace RPC-first runtime selection with `local` / `hub` / `remote`
runtime modes in `@clinebot/core`
- Clients can attach to a running session from the hub
2026-04-21 19:59:54 -07:00
Bee 9670d4b238 refactor: move llms config/runtime ownership into core (#201)
Move the user-facing llms settings/default-resolution layer out of
@clinebot/llms and into @clinebot/core.

What changed:
- move ProviderSettings schema, parsing, and toProviderConfig into core
- move provider default/model-catalog resolution into core
- move LlmsSdk runtime/config loading types and implementation into core
- remove llms runtime config/sdk exports from @clinebot/llms
- keep ProviderConfig and gateway/provider execution contracts in llms
- update core, cli, slack example, and agents call sites to the new
owners
- simplify llms live tests to avoid the removed settings helpers

Result:
- @clinebot/llms is closer to a pure gateway/catalog package
- @clinebot/core now owns stateful config, settings UX, and runtime
selection
- package boundaries better match the architecture
2026-04-21 13:38:15 -07:00
Jose Castelli c6dbc8bcb0 Adding cline quota exceeded cap error message (#10323)
Adding cline quota exceeded cap error message
2026-04-21 11:51:40 +02:00
Bee 8b0acdac98 Cline SDK 0.0.35 (#200) 2026-04-21 00:38:18 -07:00
Bee f4fe3d36ea refactor: add RuntimeHost boundary abstraction to core (#196)
Introduce a `RuntimeHost` boundary in `@clinebot/core` that unifies
local and RPC-backed execution under a single contract.

- Add `RuntimeHost`, `LocalRuntimeHost`, `RpcRuntimeHost`, and
`createRuntimeHost` as primary exports replacing generic session
host/manager types
- Update ARCHITECTURE.md with new section 2a "Runtime Host Boundary"
describing the concrete implementations and design implications
- Renumber "Session Startup Bootstrap" from 2a to 2b
- Update Local In-Process and RPC-Backed runtime flow steps to reflect
the runtime-host factory pattern
- Add runtime boundary notes to DOC.md clarifying ownership of local
execution, RPC translation, and host selection responsibilities
- `ClineCore` now delegates uniformly to `RuntimeHost` without branching
on local vs RPC behavior; transport-specific logic lives inside concrete
host implementations
2026-04-21 00:03:25 -07:00
CandiedUniverse 1862f15955 Remove foreground terminal from Cline VSCode extension (#10196)
* Create implementation plan doc

* Remove foreground terminal UI and default task execution to background mode

* Remove terminal mode UI service endpoint

* Remove foreground terminal mode state and RPC surface

* Add terminal settings UI regression test

* Guard removed foreground terminal state keys

* Test simplified terminal command routing

* Remove dead terminal profile plumbing

* Remove stale terminal mode references

* Add terminal settings verification story

* Remove implementation plan doc once implemented

* fix e2e launch under electron-run-as-node

* address greptile terminal follow-ups

* address greptile proto and vscode terminal notes

* address greptile test follow-ups

* remove dead acp terminal stubs

* Remove VS Code integrated terminal dependencies

* docs: sync integrated terminal removal plan status

* Remove terminal settings UI

* Remove terminal settings plumbing

* Mark terminal settings removal validated

* Remove implementation plan docs once implemented

* Polish shell integration warning UI

* Remove orphaned ACP terminal setters

* Add kanban install flow implementation plan

Start kanban install task from modal

Clarify kanban install task architecture

Verify kanban install task flow

Remove implementation plan doc once implemented

Restore direct terminal install launcher

Make the kanban installer change minimal and squashable

* Changes as per PR feedback

* Further deletions as per PR feedback

* Restore standalone kanban modal copy fallback

---------

Co-authored-by: Arafatkatze <arafat.da.khan@gmail.com>
2026-04-20 18:48:11 -07:00
Mikołaj Kondratek f6a9a02500 fix: set --max-old-space-size=8192 for cline-core node process (#10290)
* fix: set --max-old-space-size=8192 for cline-core node process

The cline-core Node.js process was launched without a V8 heap limit,
defaulting to ~2GB. Long conversations with large file reads cause
GC-thrashing and eventual OOM crashes. Set the limit to 8GB to provide
sufficient headroom for extended sessions.

* fix: set --max-old-space-size=8192 for cline-core node process
2026-04-20 09:42:08 -07:00
Bee fd05d4812c fix: session messages path (#198)
session messages should be stored in .cline/data/sessions sessions.db
should be stored in .cline/data/db
2026-04-18 01:23:59 -07:00
Bee 60d1eb2c74 feat: add CLINE_DB_DATA_DIR env var for database storage path (#197)
Introduce a dedicated `CLINE_DB_DATA_DIR` environment variable to
configure the database storage directory separately from the general
`CLINE_DATA_DIR`. This allows more granular control over where database
files are stored.

- Set `CLINE_DB_DATA_DIR` to `<dataDir>/db` in sandbox environment
configuration
- Propagate `CLINE_DB_DATA_DIR` in CLI e2e test environments
- Update helper tests to capture, restore, and assert the new env var
- Remove unused `resolveDocumentsAgentConfigDirectoryPath` export from
agent config loader
2026-04-17 15:08:05 -07:00
Tony Loehr 10af2439be docs: add prompt storage schema and OpenTelemetry events reference (#10195)
* docs: add prompt storage schema and OpenTelemetry events reference

- Add comprehensive prompt storage documentation (DEVREL-142)
  - Complete enterpriseTelemetry.promptUploading schema
  - Setup guides for AWS S3 and Cloudflare R2
  - Storage architecture and sync worker behavior
  - IAM policies and troubleshooting

- Add OpenTelemetry events catalog (DEVREL-143)
  - Document 80+ events across 8 categories
  - Example payloads and analytics query patterns
  - Integration examples for Datadog, Grafana, New Relic
  - Event schema reference and best practices

- Update monitoring documentation
  - Add cross-references between related pages
  - Update navigation in docs.json
  - Integrate new pages into Enterprise > Monitoring section

* fix: update broken link in telemetry.mdx to point to OTel events page

* docs: address PR review comments

- Fix file contents exclusion claim in prompt-storage.mdx
  - Remove misleading claim about file contents not being stored
  - Add warning that tool inputs (like write_to_file content) are included

- Standardize attribute naming in opentelemetry-events.mdx
  - Change model_id to model in event tables for consistency
  - Match actual emitted event schema shown in example payloads

- Add SQL syntax note in opentelemetry-events.mdx
  - Clarify that attribute access syntax is platform-specific
  - Provide examples for BigQuery and ClickHouse

* adjustments
2026-04-17 17:59:35 -04:00
Bee 3221a22bb6 feat: silent fallback for missing node:sqlite module (#195)
- Add `isNodeSqliteUnavailableError` helper to detect when `node:sqlite`
is unavailable on older runtimes (ERR_UNKNOWN_BUILTIN_MODULE)
- Replace console.warn with telemetry capture on SQLite fallback, and
suppress any warning when the module is simply not available
- Export `isNodeSqliteUnavailableError` from `packages/shared/src/db`
- Add tests for the new error detection helper and update session-host
tests to assert warn/no-warn behavior per fallback scenario
2026-04-17 12:08:08 -07:00
Tony Loehr 9a8fbf9852 docs: update Anthropic docs for Claude Opus 4.7 release (#10295)
* docs: update Anthropic docs for Claude Opus 4.7 release

- Add claude-opus-4-7 and claude-opus-4-7:1m to Anthropic supported models
- Document adaptive thinking replacing classic extended thinking on Opus 4.7
- Document sampling parameter restrictions (temperature, top_p, top_k) on Opus 4.7
- Add claude-opus-4-7 to Claude Code supported models

* Update docs/provider-config/anthropic.mdx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* unblocker

---------

Co-authored-by: Ara <arafat.da.khan@gmail.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-04-17 11:51:45 -07:00
TheRealSpencer c5657a14bb chore: update axios to 1.15.0 across all packages (#10271)
- Update root package.json axios from 1.13.6 to 1.15.0
- Update evals/package.json axios from 1.13.6 to 1.15.0
- Update docs/package.json axios override from 1.13.5 to 1.15.0
- Regenerate all package-lock.json files
2026-04-17 10:52:28 -05:00
Bee ab1501f67a Merge pull request #194 from cline/bee/dirs 2026-04-17 05:04:05 -07:00
abeatrix 579d1f2366 agent shutdown leak 2026-04-17 04:42:49 -07:00
abeatrix d6ece32383 fix 2026-04-17 04:30:21 -07:00
abeatrix a4a7f9280a refactor: directories structures 2026-04-17 04:07:48 -07:00
Bee e77a76a64b chore(cli): remove @clinebot/enterprise workspace dependency (#193) 2026-04-17 03:06:51 -07:00
Bee a5fe7d55ee feat: Claude Opus 4.7 (#191)
- Model catalog updated for Claude Opus 4.7 release.
- Truncation set to auto by default for OpenAI provider.
- Improve Anthropic reasoning effort setting
2026-04-17 00:48:31 -07:00
Bee 154e6f1082 feat: add RPC server debug config and improve tool error display (#190)
- Add "Launch RPC Server" debug configuration and "Launch RPC Server
Debugger" compound to VS Code launch.json for easier RPC debugging
- Simplify tool error output to show compact "failed" marker instead of
exposing full error message text in CLI event handler
- Add test coverage for compact failure marker on tool errors
- Update session and team tools tests to reflect renamed tool
(team_await_run → team_await_runs) and improved field validation
behavior (warn on ignored fields instead of rejecting them)
2026-04-16 22:34:25 -07:00
Robin Newhouse 955ae8df7a feat: wire up remote globalSkills with enterprise UI and architectural fixes [ENG-1774] (#10283)
* feat: wire up globalSkills consumption from remote config

The remote config schema already includes globalSkills (merged in #10236).
The dashboard can save skills to remote config. This PR wires up the
extension to read and use them.

## Changes

### State storage (Layer 1)
- Add remoteGlobalSkills to REMOTE_CONFIG_EXTRA_FIELDS
- Add remoteSkillsToggles to GLOBAL_STATE_FIELDS

### Remote config transform/apply/clear (Layer 2)
- Map globalSkills → remoteGlobalSkills in transformRemoteConfigToStateShape
- Sync remoteSkillsToggles in applyRemoteConfig using frontmatter.name
  as the identity key (not entry.name)
- Clear remoteSkillsToggles in clearRemoteConfig

### Skill discovery (Layer 3)
- discoverSkills accepts optional remoteSkillEntries parameter (pure
  utility, no StateManager coupling)
- getSkillContent accepts optional remoteSkillEntries parameter for
  remote content loading without disk I/O
- Precedence: remote (enterprise) > disk-global (user) > project

### refreshSkills (Layer 3b)
- Reads remote entries from controller.stateManager, parses frontmatter,
  builds SkillInfo entries with alwaysEnabled field

### UseSkillToolHandler (Layer 4)
- Toggle filter checks remoteSkillsToggles for remote: prefixed skills
- Directory note omitted for remote skills
- Passes remoteSkillEntries to both discoverSkills and getSkillContent

### toggleSkill
- Routes remote: prefixed paths to remoteSkillsToggles keyed by name

### Proto + webview
- Added always_enabled field to SkillInfo proto message
- Modal passes isRemote + alwaysEnabled to RuleRow for remote skills
- Uses skill.name as display label for remote skills

## Design decisions
- frontmatter.name is the sole identity for remote skills (entry.name
  is ignored). This matches how local skills work.
- remote: path prefix distinguishes remote from disk skills in toggle
  stores and content loading.
- skills.ts remains a pure utility module with zero StateManager coupling.
  Callers inject remote entries as parameters.
- 42 unit tests covering discovery, precedence, content loading, toggle
  sync, and frontmatter parsing.

* fix: enforce alwaysEnabled in toggle sync to prevent stale false overrides

When applyRemoteConfig syncs skill toggles, synchronizeRemoteRuleToggles
preserves existing toggle values — including false. If an admin later
sets alwaysEnabled: true on a skill that a user had previously disabled,
the stale false toggle would survive the sync. The UI would show the
skill as locked-on (via the alwaysEnabled check in refreshSkills), but
UseSkillToolHandler's filter would see false in the toggle store and
exclude it, causing a 'Skill not found' error for a skill the user can
see is active.

Fix: after synchronizeRemoteRuleToggles, force any alwaysEnabled entry
with a false toggle back to true. This makes the toggle store the single
source of truth — both UI and handler now agree.

Adds 4 tests covering the alwaysEnabled enforcement edge cases.

* fix: deduplicate remote skill parsing, add drift validation, and fix architectural gaps

1. Extract shared parseRemoteSkillEntries utility (skills.ts)
   - Single validation point for remote skill entries, replacing duplicated
     frontmatter parsing in skills.ts, refreshSkills.ts, and remote-config/utils.ts
   - Enforces entry.name === frontmatter.name to catch drift between the
     dashboard and SKILL.md content (rejects with warning on mismatch)

2. Eliminate redundant frontmatter re-parsing in getSkillContent
   - Was re-parsing every entry's frontmatter to find a match by name
   - Now uses entry.name for lookup since drift validation guarantees equality

3. Enforce alwaysEnabled in UseSkillToolHandler
   - The toggle filter was missing the alwaysEnabled check, so a stale false
     toggle could hide an admin-locked skill from the model
   - Now matches the logic in refreshSkills.ts

4. Add remote_skills_toggles to SkillsToggles proto
   - toggleSkill now returns remoteSkillsToggles in the response, matching
     how remote rules/workflows already work

5. Separate Enterprise Skills section in UI
   - Remote skills now render under their own "Enterprise Skills" header,
     consistent with how rules and workflows display remote entries

6. Update tests for new validation behavior
   - Tests now use entry.name matching frontmatter.name (was deliberately
     mismatched before); added drift rejection tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: enable viewing remote skills and fix tooltip text in RuleRow

- openRemoteFile now handles remote://skill/{name} URIs (was only
  rule and workflow), looking up content from remoteGlobalSkills
- RuleRow's handleEditClick builds the correct URI type for skills
  (was falling through to "rule")
- Tooltip text now uses ruleType ("View skill file") instead of
  hardcoded "View rule file" for all remote entries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: soften drift validation to warn-not-reject, fix content lookup fallback

The strict entry.name !== frontmatter.name rejection was silently hiding
org-configured skills when the dashboard's entry.name didn't match the
SKILL.md frontmatter name.

- parseRemoteSkillEntries now warns on drift but uses frontmatter.name as
  the canonical identity instead of rejecting the entry
- getSkillContent falls back to frontmatter match when entry.name lookup
  misses (handles drift for content loading)
- openRemoteFile falls back to frontmatter match for skill view (same
  reason)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: include remote skills in system prompt and fix remote config race

The system prompt generation called discoverSkills() without passing
remoteSkillEntries, so the model never learned about remote skills and
never invoked use_skill for them. This was the actual cause of remote
skills being invisible to the model despite showing in the UI.

Also fixes a race condition in applyRemoteConfig where clearRemoteConfig()
wiped the in-memory cache before repopulating it field-by-field. Any
concurrent reader (e.g., UseSkillToolHandler) during that window would
see an empty cache. Replaced with atomic replaceRemoteConfig() that
builds the new cache and swaps it in a single assignment.

- task/index.ts: pass remoteSkillEntries to discoverSkills, add
  remoteSkillsToggles + alwaysEnabled filtering (matching handler)
- StateManager: add replaceRemoteConfig() for atomic cache swap
- remote-config/utils.ts: use replaceRemoteConfig instead of
  clearRemoteConfig + setRemoteConfigField loop
- Remove debug logging from parseRemoteSkillEntries and handler

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: include remote skills in subagent path

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 18:38:02 -07:00
Bee ef4f0150d3 feat: add input history navigation and session deletion (#188)
- Implement persistent input history navigation using up/down arrow keys
in the interactive TUI.
- Add capability to delete history items in the session list view.
- Refactor hook logging configuration to utilize the
CLINE_HOOKS_LOG_PATH environment variable for improved path management.
- store global hooks audit logs in centralized logs directory
- remove storing per session audit logs

Per-session hook log removed:

SessionArtifacts.sessionHookPath() removed
SessionArtifactPaths.hookPath removed
hookPath removed from StartSessionResult, RootSessionArtifacts,
RpcChatStartSessionArtifacts, ActiveCliSession, SessionRecord output
readHooks() removed from SessionManager interface,
DefaultSessionManager, rpc-session-host, ClineCore
HookSessionContext.hookLogPath removed — hook workers no longer receive
a per-session path hookLogPath removed from HookRuntimeOptions and
createPayloadBase createHookAuditHooks no longer takes hookLogPath
buildEffectiveConfig no longer takes hookPath param registerSession in
rpc-runtime.ts no longer takes hookPath Global log
(~/.cline/data/logs/hooks.jsonl):

ensureHookLogDir default changed from hooks/ → logs/ dir
CLINE_HOOKS_LOG_PATH sandbox env var updated to logs/hooks.jsonl All
audit writes (createHookAuditHooks, appendSubagentHookAudit, stale
session shutdown, appendHookAudit in CLI) now write to
CLINE_HOOKS_LOG_PATH or default logs/hooks.jsonl readSessionHooks in
sidecar reads global log and filters by sessionContext.rootSessionId /
sessionId / taskId Hook events include sessionContext.rootSessionId and
taskId so per-session filtering still works DB backward compat:
hook_path column still written as "" — no migration needed.
2026-04-16 15:08:15 -07:00
Ara 9405419efe v3.79.0 Release Notes (#10292)
* v3.79.0 Release Notes

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/i90f8jfxjc88hit3bs8v2e2k

* chore(cli): bump CLI to v2.15.0

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

🔮 View transcript: https://agentlogs.beatrixwoo.workers.dev/s/i90f8jfxjc88hit3bs8v2e2k

* remove changeset

* remove changeset
2026-04-16 12:26:10 -07:00
Saoud Rizwan f53dcb3096 feat(models): prepare Claude Opus 4.7 provider support (#10286)
* feat(models): prepare Claude Opus 4.7 provider support

* remove deprecated params for opus 4.7

- opus 4.7 doesn't accept params like temperature, top_p, top_k anymore.
This commit removes those params only for opus 4.7

* Agent hill climb fixes

* Anthropic adaptive thinking

* Removing 1m context switcher

* Removing 1m models fully

* Restore Anthropic 1M variants and context switchers

* Adding 1m

* remove changeset

* fix Opus 4.5 adaptive thinking detection

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Arafatkatze <arafat.da.khan@gmail.com>
2026-04-16 12:02:28 -07:00
CandiedUniverse afa32bf801 fix: Stabilize flaky Windows CI test paths (#10291)
* docs: add CI flakiness stabilization plan

* test: harden global hook cwd timeout on windows

* test: stabilize CLI skills panel interactions

* ci: harden vscode test runtime setup

* test: stabilize BannerService timer behavior

* refactor: ignore CLI skills input while loading

* docs: update stabilization plan status

* docs: drop temporary stabilization plan

* Update cli/src/components/SkillsPanelContent.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update .vscode-test.mjs

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* test: expose banner service drain hook

* fix: stabilize CLI skills panel input state

* Change polling interval

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-04-16 11:27:56 -07:00
John Simone 42c7e2026d add confidentiality statement to readme 2026-04-16 09:38:52 -07:00
Bee 7a69bded86 refactor: remove duplicated provider map (#187) 2026-04-16 00:42:28 -07:00
Bee e37df3d57d feat: persisted session messages to remote blob storage (#173)
- add a generic messagesArtifactUploader seam in core session
persistence
- carry per-session metadata through StartSessionInput into persisted
sessions
- port S3/R2/Azure blob storage adapters into @clinebot/enterprise
- resolve promptUploading storage settings from enterprise remote config
- stamp enterprise blob-upload metadata during
prepareEnterpriseCoreIntegration
- upload persisted messages.json files after disk writes when configured
- add azure promptUploading support to the shared remote-config schema
- document and test opt-out promptUploading enablement behavior
- enable the enterprise remote-config upload path in CLI and CLI RPC
runtime
2026-04-15 20:22:46 -07:00
Bee 583938b5d4 fix: code browser import (#186) 2026-04-15 19:28:18 -07:00
Bee e5921d5140 refactor: extract cli interactive home TUI (#183)
extract tui into components
2026-04-15 18:42:54 -07:00
Bee 8e9885e01e fix: show original error message from api (#184)
show original error message from api
2026-04-15 18:09:52 -07:00
Ara 2d2d9d829a fix cache reflection for cline and vercel handlers (#10266) 2026-04-15 17:47:15 -07:00
Bee ba43381200 refactor: ClineCore own runtime routing and session access (#178)
Refactor ClineCore to fully own local-vs-RPC runtime routing and remove
CLI-side session backend resolution.

What changed:
- add a core-owned RPC SessionHost adapter so ClineCore can execute
sessions
  over RPC without exposing RpcSessionClient to callers
- route createSessionHost() to local or RPC hosts behind the same
SessionHost
  interface
- extend RPC runtime session payloads with source and interactive so
remote
  execution matches the SessionHost contract
- add ClineCore session admin APIs for update(...) and
handleHookEvent(...)
- switch CLI session/history/checkpoint/hook helpers to use ClineCore
only
- remove CLI use of RpcCoreSessionService, getCoreSessions(), and
  getCoreSessionBackend()
- move env-based backend selection (CLINE_SESSION_BACKEND_MODE,
  CLINE_RPC_ADDRESS, CLINE_VCR) into core
- expose the resolved runtime address on ClineCore for callers to record

Why:
- keep ClineCore as the only public runtime abstraction
- hide RPC transport details from CLI/runtime callers
- centralize backend/routing policy in core instead of duplicating it in
CLI
- preserve a single API surface for both local and long-running
RPC-backed
  execution

Validation:
- npm run typecheck (packages/core)
- npm run typecheck (packages/rpc)
- npm run typecheck (apps/cli)
- bunx vitest run src/session/session-host.test.ts src/ClineCore.test.ts
  (packages/core)
- bunx vitest run src/session/session.test.ts (apps/cli)
2026-04-15 15:53:14 -07:00
John Choi 27a1b3da8a feat: user-level remote-config discovery with inline value reuse (#10056)
Replace the old client-side per-org scan for remote config with a single
discovery call to GET /api/v1/users/me/remote-config. Reuse the inline
config value when possible, falling back to the org-level endpoint only
when inline parse fails.

Key changes:
- Single discovery call replaces N org-level requests
- Resolve config before switching org to avoid stranding the user
- Transient errors preserve existing config (log-only, no clearing)
- authenticatedRequest() strict null vs undefined validation
- Auth precheck in fetchUserRemoteConfig() with token pass-through
2026-04-15 11:21:47 -07:00
Bee 1086b88a50 feat: account view for code eapp (#181)
- add account view for code app example
- create shared types API endpoint
2026-04-15 11:19:19 -07:00
Bee fc5c0fe14e fix: switch to local backend in CLI (#182)
Switching to local backend instead of rpc for now until the rpc backend
is stabled.

- Removed the "auto" mode that previously tried to auto-start an RPC
sidecar (via ensureCliRpcRuntimeAddress) and fall back to local
  - Now defaults to local backend unless:
    - CLINE_SESSION_BACKEND_MODE=rpc is set, or
- CLINE_RPC_ADDRESS is explicitly set (user already has an RPC server)
  - Removed the now-unused ensureCliRpcRuntimeAddress import

- Added getRpcServerDefaultAddress to the existing @clinebot/rpc import
  - Added ensureCliRpcRuntimeAddress import from ./utils/rpc-runtime
- In the connect command action: before running any adapter, ensure the
RPC server is started (if CLINE_RPC_ADDRESS is not already set) and set
the env var so the connector (and any child processes it spawns) uses it
2026-04-15 11:08:46 -07:00
Tony Loehr f5c8cd4384 docs: fix enterprise license link to point to contact sales (#10164)
Update the Cline Enterprise License link in enterprise onboarding prerequisites from https://cline.bot/enterprise to https://cline.bot/contact-sales
2026-04-15 10:00:26 -07:00
Tony Loehr fddabb6b8d docs: add Kanban remote access documentation (#10274)
* docs: add Kanban remote access documentation

Add comprehensive documentation for accessing Kanban remotely:
- Local network access (--host flag and KANBAN_RUNTIME_HOST env var)
- Tailscale for secure remote access
- Docker deployment
- SSH tunneling
- Ngrok for public URLs
- Cloudflare Tunnels with AWS CDK example

* Update docs/kanban/remote-access.mdx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update docs/kanban/remote-access.mdx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-04-15 09:44:55 -07:00
Bee 442e86eddc feat: harden RPC lock lifecycle and doctor recovery (#180)
Add explicit RPC startup lock states and clear wedged startup artifacts

- store RPC startup lock status as starting/running
- record updatedAt, resolvedAddress, and serverId in lock owner.json
- add markRunning() to the startup lock handle and set it after rpc
start succeeds
- treat running locks with unreachable recorded servers as stale
- extend doctor --fix to clear stuck rpc startup locks and spawn leases
- add tests for lock state transitions, unreachable running locks, and
doctor recovery
- fix slack bot error on channel reply issue
2026-04-15 09:21:41 -07:00
Bee fe4714ea31 fix: unit test case (#179)
expect(output).toMatch(/⎿.*ok/s);
2026-04-14 23:36:15 -07:00
Bee 3389a2d23a feat: registerCommand from plugin + TUI messages (#177)
- Wire registerCommand from plugin and register thems as slash commands.
- Port old Chat Message TUI over
2026-04-14 21:30:12 -07:00
Bee a57518abc2 fix: remove maxIterations as tool options (#175)
Removes maxIterations as subagent and agent team tools options as the
number returned by the main agent are often inaccurate.
2026-04-14 20:05:49 -07:00
Bee 072f0f551b chore: removes desktop app example (#176)
Remove Desktop app example
2026-04-14 20:05:33 -07:00
Bee 2430d5541b feat: support images from read files tool (#174)
support images from read files tool

```
❯ bun run cli "can you tell me what is the image inside apps/code/public/icon.png?"
$ bun --conditions=development --cwd apps/cli dev "can you tell me what is the image inside apps/code/public/icon.png?"
$ CLINE_BUILD_ENV=development bun --watch --conditions=development ./src/index.ts "can you tell me what is the image inside apps/code/public/icon.png?"
[read_files] {"files":[{"path":"/Users/beatrix/dev/clinee/sdk-wip/apps...
  -> Successfully read image [image]

The image at `apps/code/public/icon.png` is the **Cline logo/icon**. It features a stylized robot or AI assistant face rendered in a **teal/cyan color** on a **dark background**. The design has a rounded shape with two prominent "eyes," giving it a friendly, minimalist robot/bot appearance. This is the app icon used for the Cline Code application.

```
2026-04-14 19:26:16 -07:00
Bee 048573e5da feat: /team command & tui components (#172)
- Add /team command for always starting a task as agent team task
- Extracted TUI components for config and history

Wrap /team prompts in user_command tags and render them as slash
commands

- make /team work by default in interactive and non-interactive CLI
flows
- store team prompts as <user_command slash="team">...</user_command>
- strip user_command wrappers before sending user text to the model
- add shared prompt helpers for parsing, normalization, and display
formatting
- render wrapped team prompts as /team ... in CLI, desktop, and code UI
surfaces
- update tests and CLI docs for the new team command behavior
2026-04-14 17:39:21 -07:00
Bee 2976a6fda4 feat: default CLI to rpc-backed sessions (#164)
Changes:

- Change CLI session backend resolution to prefer `backendMode: "auto"`
by default so the core layer can connect to or start the RPC runtime
instead of defaulting to direct local SQLite access.
- Keep `--yolo` and `--sandbox` on the local backend by explicitly
forcing local session mode for those runs.
- Add structured CLI logging for the selected session backend (`rpc` vs
`local`) to make backend choice visible in logs.
- Update focused CLI session tests to cover the new default behavior,
forced-local overrides, and backend selection logging.
- Fixed byte-tracking bug in packages/core/src/input/mention-enricher.ts
2026-04-14 14:23:12 -07:00
Robin Newhouse 071f32ec92 fix: unblock stuck command_output ask when terminal command ends (#10269)
* fix: unblock pending command_output ask on terminal completion

* test: cover timeout and idempotent command_output ask release

* refactor: simplify pending command_output ask release guard
2026-04-14 12:56:55 -07:00
KOlizer 9bdb8a9362 fix(prompts): add use_subagents to GLM, Hermes, and XS TOOL_USE_SECTI… (#10200)
* fix(prompts): add use_subagents to GLM, Hermes, and XS TOOL_USE_SECTION overrides

These variants use hardcoded TOOL_USE_SECTION templates that bypass the
auto-generated tool descriptions. When use_subagents was added as a new tool,
it was registered in each variant's .tools() config but was never added to the
hardcoded override templates — so models using these variants never saw
use_subagents in their system prompt and could not call it.

This adds the use_subagents description block to the TOOL_USE_SECTION override
templates for glm, hermes, and xs variants, and updates the corresponding
test snapshots.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(prompts): gate use_subagents on subagentsEnabled and isSubagentRun context

The previous commit added use_subagents to the GLM, Hermes, and XS
TOOL_USE_SECTION override templates unconditionally. This was incorrect —
the canonical tool spec gates use_subagents with:
  context.subagentsEnabled === true && !context.isSubagentRun

Without this guard, models would advertise use_subagents even when
subagents are disabled by the user, and subagent runs could recursively
spawn further subagents.

This commit:
- Wraps the use_subagents block in all three templates with the same
  subagentsEnabled && !isSubagentRun conditional
- Converts HERMES_TOOL_USE_TEMPLATE from a plain string constant to a
  function so it can access context (matching the pattern used by GLM
  and XS templates)
- Updates snapshots accordingly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(prompts): align use_subagents rendering guard with tool context requirements

---------

Co-authored-by: sunghyun <jjinjukks1227@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 12:41:23 -07:00
Bee 52f281a090 Cline SDK 0.0.34 (#170) 2026-04-14 11:36:24 -07:00
Saoud Rizwan 075c27984d fix: allow disabling MCP settings tool auto-load (#168)
## Summary
This PR adds a runtime config flag, `disableMcpSettingsTools`, so host
apps can opt out of SDK MCP auto-loading from `cline_mcp_settings.json`
when they are already injecting MCP tools themselves.

## Investigation and root cause
We reproduced and traced the duplicate tool-name failure to two MCP
registration paths being active in the same session:

1. SDK runtime auto-load path
- `DefaultRuntimeBuilder` loads MCP tools from settings via
`loadConfiguredMcpTools`
2. Kanban host-injected MCP path
   - Kanban builds MCP tools and passes them in via `extraTools`

When both paths are enabled, tools can collide by name and session
startup fails with duplicate-tool errors.

## Why Kanban cannot fully switch to SDK MCP path today
Kanban currently owns MCP OAuth flows (auth status tracking, callback
listener, token persistence, reconnect behavior) in its MCP runtime
service.

The SDK auto-load MCP path currently does not provide that same
host-integrated OAuth flow end to end. So replacing Kanban MCP injection
with SDK settings auto-load right now would regress OAuth-capable MCP
setups in Kanban.

## What this PR changes
- Adds `disableMcpSettingsTools?: boolean` to runtime config surfaces.
- SDK behavior remains unchanged by default.
  - omitted / false: SDK auto-load stays enabled
  - true: SDK auto-load is skipped
- Threads the flag through core + shared + RPC + CLI runtime mapping.
- Adds/updates test coverage for forwarding and behavior.

## Why disable semantics
Using `disableMcpSettingsTools` as a plain proto bool avoids presence
ambiguity and keeps backward compatibility simple:
- old callers omit the field -> default false -> no behavior change
- callers that need host-owned MCP set true

## Temporary measure and removal plan
This flag is intended as a temporary compatibility bridge.

Once SDK MCP runtime supports the OAuth and session integration needs
that Kanban currently handles, Kanban can stop injecting MCP tools and
rely on the SDK MCP path directly. At that point, we can remove this
flag and related branching.

## Validation
- `bun run --cwd /workspace/cline-sdk-wip types`
- `bun run --cwd /workspace/cline-sdk-wip/packages/core test:unit --
src/runtime/runtime-builder.test.ts`
- `bun run --cwd /workspace/cline-sdk-wip/packages/rpc test`
- `bun run --cwd /workspace/cline-sdk-wip/apps/cli test:unit --
src/commands/rpc-runtime/session-helpers.test.ts`

## Kanban follow-up
When starting Cline sessions that include Kanban-built MCP `extraTools`,
set:
- `disableMcpSettingsTools: true`
2026-04-14 11:30:08 -07:00
Bee f622299d05 fix: thread logger through gateway and surface AI SDK stream errors (#169)
ref https://github.com/cline/sdk-wip/issues/167

Stream errors from the AI SDK were silently swallowed — `onError` was a
no-op and `NoOutputGeneratedError` replaced the underlying cause with a
generic message, making it impossible to diagnose failures.

- Add `logger?: BasicLogger` to `GatewayConfig` and
`GatewayProviderContext` so callers can inject a logger
- Thread `ProviderConfig.logger` through the compat layer into the
gateway
- Log stream-level and provider-level errors via the injected logger in
`createAiSdkProvider` (falls back to silent when no logger is provided)
- Extract the `cause` from `NoOutputGeneratedError` so the actual
provider error (e.g. 429, 500, auth failure) is included in the error
message returned to the caller
2026-04-14 11:03:19 -07:00
Bee 36a495914b refactor: code app to use typescript sidecar (#166)
This PR replaces the Tauri Rust WebSocket bridge and host/ Bun backend with a single TypeScript sidecar process (sidecar/) that imports @clinebot/core directly, serving the Next.js frontend over HTTP+WebSocket. The Rust shell is dramatically simplified to just spawn the sidecar binary/script and proxy its ws_endpoint.
2026-04-14 10:25:05 -07:00
Tomás Barreiro 2d994530fd Add support for Azure Blob Storage (#10264)
* Add support for Azure Blob Storage

* fix comment
2026-04-14 18:42:39 +02:00
Bee 8e0cb07d7a fix: unify run_commands input truncation in CLI (#162)
Problem: formatToolInput for run_commands only handled { commands:
string[] } input. The RunCommandsInputUnionSchema and
StructuredCommandsInputUnionSchema accept six additional shapes — bare
strings, singular { commands: string }, structured { command, args }
objects, and arrays of any of these. Inputs in those shapes either fell
through to a generic JSON.stringify fallback (truncated to 60 with ugly
JSON wrapping) or returned empty string.

Fix:

- Added summarizeRunCommandsInput() that normalizes all accepted input
shapes into a human-readable command string before truncation
- Added formatStructuredCommand() to handle { command, args } structured
entries
- Moved the run_commands case ahead of the typeof input !== "object"
guard so bare strings are handled
- All shapes are consistently truncated at 120 characters
2026-04-13 20:55:14 -07:00
Bee 2fadee2e88 feat: split comma-separated --tool-enable/--tool-disable values (#163)
The CLI was treating --tool-enable and --tool-disable as repeatable
flags only, but not splitting comma-separated values. That caused
commands like --tool-disable run_commands,read_files to register a
single invalid tool name instead of disabling both tools.

This change updates CLI arg parsing to normalize comma-separated tool
lists before building tool policies. It also adds a regression test
covering comma-separated enable/disable flags so the CLI behavior
matches user expectations for both repeated and comma-separated forms.

Tested:

```sh
❯ bun run cli --tool-disable run_commands,read_files "run a bash commands to echo your name"
$ bun --conditions=development --cwd apps/cli dev --tool-disable "run_commands,read_files" "run a bash commands to echo your name"
$ CLINE_BUILD_ENV=development bun --watch --conditions=development ./src/index.ts --tool-disable "run_commands,read_files" "run a bash commands to echo your name"
[run_commands] echo 'Cline'
 error: Tool "run_commands" is disabled by policy
error: 1 tool call(s) failed: run_commands: Tool "run_commands" is disabled by policy

It looks like the `run_commands` tool is **disabled by policy** in this environment, so I'm unable to execute shell commands directly.

However, the bash command to echo my name would be:

echo 'Cline'

**Output:**
Cline

My name is **Cline**! 🤖 If you need me to run other commands or help with a coding task, feel free to ask!
```

## Changes 2

The CLI already parsed --tool-enable and --tool-disable into tool
policies, but disabled tools were still being registered in the runtime
and sent to the model. That meant the model could attempt to call tools
like run_commands, only for the call to fail later with a policy error.

This change fixes both parts of that flow. The CLI now splits
comma-separated --tool-enable and --tool-disable values correctly, and
the runtime builder filters out any tools explicitly marked enabled:
false before handing the tool list to the agent. As a result, disabled
tools are no longer advertised to the model, and comma-separated CLI
flags behave the same as repeated flags.

Tested:

```sh
sdk on  bee/tools-comma [$+] via 🥟 v1.3.10 on ☁️  beatrix@cline.bot 
❯ bun run cli --tool-disable run_commands,read_files "run a bash commands to echo your name"
$ bun --conditions=development --cwd apps/cli dev --tool-disable "run_commands,read_files" "run a bash commands to echo your name"
$ CLINE_BUILD_ENV=development bun --watch --conditions=development ./src/index.ts --tool-disable "run_commands,read_files" "run a bash commands to echo your name"
[thinking] The user wants me to run a bash command to echo my name. I don't have a direct shell execution tool, but I can use the editor or search tools. Actually, I don't have a shell execution tool available. Let me just answer directly.

I don't have a bash execution tool available in my toolset. I can only search code, fetch web content, edit files, or use team/agent tools. I'll let the user know.
I don't have a direct shell/bash execution tool available in my current toolset. However, here's the bash command you would run to echo my name:

echo "Cline"

**Output:**
Cline

My name is **Cline** — an AI coding agent! 🤖 If you need me to perform file edits, code searches, or other supported tasks, I'm happy to help!
```
2026-04-13 20:47:28 -07:00
Bee 7d1d849de9 ci: skip sqlite smoke test on windows (#160)
Reasons:
The smoke test's purpose is to verify the npm packaging (pack → install
→ import) and that Node's built-in SQLite works with the session store.
The npm packaging pipeline is OS-agnostic, and Node's SQLite
implementation is the same native module on both platforms. Windows
already gets coverage from "Run SDK Tests (Windows)" which exercises the
actual packages. The smoke test is expensive (packs 4 tarballs, runs npm
install, spawns Node) and adds minutes to the Windows job.
2026-04-13 19:57:45 -07:00
Bee 54997589a3 fix: plugin initialization resilience and diagnostics (#159)
## Summary

This PR hardens plugin loading in `@clinebot/core` so plugin
initialization failures no longer disable all plugins.

## What changed

- Isolated plugin initialization failures so only the failing plugin is
skipped
- Added structured plugin load diagnostics for failures and duplicate
overrides
- Changed duplicate plugin resolution to last-one-wins instead of
failing the whole load
- Added startup warnings when some plugins fail to initialize
- Sent detailed plugin failure diagnostics to verbose/debug logging
- Exposed a diagnostic loader API for development and debugging

## Behavior changes

Before:
- One bad plugin could make all plugins unavailable
- Plugin initialization failures were effectively silent
- Duplicate plugins could break the entire plugin set

After:
- Valid plugins still load when another plugin fails
- Duplicate plugin names are resolved by keeping the later plugin
- Startup logs warn when some plugins failed and point users to
`--verbose`
- Verbose/debug logs include per-plugin failure details

## Tests

Added coverage for:
- Partial plugin load success when one plugin fails
- Duplicate plugin override behavior
- Sandboxed plugin setup failures
- Diagnostics returned from plugin loading paths
2026-04-13 19:48:54 -07:00
Bee 9b1ce15521 fix: separate tool and team events onto distinct lines (#158)
Ensure CLI transcript output stays line-oriented during parallel tool
and team activity.

terminate tool-start lines explicitly so adjacent tool calls do not run
together flush active inline reasoning/text before team events are
printed add regression tests for adjacent tool output and team-event
line boundaries
2026-04-13 19:04:32 -07:00
Bee 2ea1daf983 refactor: simplify runtime internals and reduce duplication (#155)
- Inline TeamRuntimeRegistry into DefaultRuntimeBuilder as a plain Map,
removing the single-use wrapper class
- Extract ConfiguredSkill type and rename listConfiguredSkills to
getConfiguredSkills, carrying the full SkillConfig through to avoid
re-reading snapshots in resolveSkillRecord
- Add processLabel getter and clearPendingRequest helper in
SubprocessSandbox to deduplicate repeated label strings and cleanup
logic
- Extract unlinkIfPresent in tool-approval.ts and parallelize file
cleanup with Promise.all
2026-04-13 18:52:14 -07:00
Bee 15cd25a8c1 fix: support day/month name ranges in cron expressions (#156)
- normalizeToken() returned after replacing the first matching name, so
expressions like "MON-FRI" became "1-FRI" and failed to parse.
- Replace all name mappings in a single pass instead of
short-circuiting.
- Add unit test
2026-04-13 17:45:48 -07:00
Igor Tceglevskii 48d524aa1e fix(teams): deduplicate concurrent sync team_run_task calls to same agent (#136)
# Fix: deduplicate concurrent sync `team_run_task` calls

## What's the problem?

Claude occasionally generates duplicate tool calls in a single response.
It emits 2-3 identical `team_run_task` blocks (same agent, same task
text) but with different IDs.
The SDK treats each one as a separate call and runs them all in
parallel.

The first call works fine.
The rest fail immediately because the agent is already busy:
> "Cannot start a new run while another run is already in progress"

The coordinator then spends tokens retrying and trying to recover from
errors that shouldn't have happened in the first place.

## How does the fix work?

We track which agents already have a sync call in progress using a
simple Map.

When a second sync call comes in for the same agent:
- It returns right away with an `IGNORED` message instead of hitting the
runtime
- The Claude API still gets a valid `tool_result` for every `tool_use`
block (required by the protocol)
- No extra LLM calls are made on the teammate side

Once the first call finishes, the agent is unlocked for future calls.

Calls to *different* agents are not affected — they still run in
parallel as expected.
Async calls are not affected either — dedup only applies to sync mode.

## What changed?

**`packages/core/src/team/team-tools.ts`**
- Added `pendingSyncRuns` Map before the `team_run_task` definition (~30
lines)

**`packages/core/src/team/team-tools.test.ts`**
- "deduplicates concurrent sync calls to the same agent" — fires two
calls, checks only one reaches the runtime
- "allows concurrent sync calls to different agents" — confirms
per-agent scoping, no false dedup

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: abeatrix <beatrix@cline.bot>
2026-04-13 16:23:21 -07:00
Bee e8be1860d5 fix: remove clite from core error message (#153)
Removes mention of the clite (CLI) from core error message.
2026-04-13 14:02:46 -07:00
Bee b56d472edd fix: VS Code Example launch task (#150)
The empty webview was caused by a **React version mismatch**:
`react@19.2.5` vs `react-dom@19.2.4`. The `^19.2.4` range in the
webview's `package.json` allowed `react` to resolve to `19.2.5` while
`react-dom` stayed at `19.2.4`.

### Changes Made

1. **`apps/vscode/src/webview/package.json`** — Pinned `react` and
`react-dom` to exact `19.2.4` (removed `^` caret) to prevent version
drift between the two packages.

2. **`apps/vscode/src/webview/vite.config.ts`** — Added `resolve.dedupe:
["react", "react-dom"]` to ensure Vite always resolves to a single copy
of each, even if transitive dependencies try to pull in their own.

3. **`.vscode/launch.json`** — Added `outFiles` pointing to
`${workspaceFolder}/apps/vscode/dist/**/*.js` on both VS Code extension
launch configurations, enabling proper source map resolution when
debugging from the monorepo root.

4. **`.vscode/tasks.json`** — Added a `build-sdk` task that runs `bun
run build:sdk` at the workspace root, and made `build-vscode-extension`
and `watch-vscode-extension` depend on it via `dependsOn:
["build-sdk"]`. This ensures SDK packages are built before the
extension, which is necessary when launching from the root workspace
(the inner `apps/vscode/.vscode/tasks.json` didn't need this because it
assumed SDK was already built).

5. Add new lunch task to root .vscode/launch.json
2026-04-13 13:59:44 -07:00
Bee 85d95a7846 fix: prevent duplicate streaming text output in RPC session (#152)
Fix the RPC session manager's post-stream reconciliation logic that
re-emitted the entire response text when result.text diverged from what
was already streamed via deltas, causing the output to appear twice in
the terminal The fallback branch now only emits result.text when no text
was streamed at all, rather than whenever the final text doesn't
prefix-match the accumulated stream Add 4 unit tests covering the
streaming text deduplication logic: exact match, divergence, no-stream
fallback, and remainder emission
2026-04-13 13:52:35 -07:00
Bee fd83f8db25 fix: clear instruction for team_run_task (#139) 2026-04-13 13:52:02 -07:00
Bee 442d7bcd42 dev: fix release script to publish packages as public (#149)
The --access restricted flag on line 294 makes packages private. To
publish them publicly, it should be --access public.
2026-04-13 13:45:11 -07:00
Bee c6d4ac0aea fix: flaky test caused by EBUSY errors on Windows CI (#151)
The flaky test was caused by `EBUSY` errors on Windows CI when `rm()`
tried to remove the temp directory while the spawned hook child process
still held file handles. The fix adds `maxRetries: 3` and `retryDelay:
250` to all 7 `rm()` calls in the test file. Node's `fs.rm` with these
options will automatically retry on `EBUSY`, `EMFILE`, `ENFILE`,
`ENOTEMPTY`, and `EPERM` errors — exactly the transient lock conditions
Windows encounters when a child process hasn't fully released its
handles yet.
2026-04-13 13:44:53 -07:00
Tomás Barreiro 1974d7ad46 Publish 0.0.33 (#147) 2026-04-13 12:46:16 -07:00
Bee 4d63910560 feat: add onServerListening/onServerClose hooks to OAuth callback server (#140) 2026-04-13 12:08:23 -07:00
tjandy98 e477f8fa04 Add gpt-5.2 (#10024) 2026-04-13 19:36:22 +02:00
Tomás Barreiro 36b0baec81 Add globalSkills to remote config (#10236) 2026-04-13 19:36:09 +02:00
Tomás Barreiro 7c25a74cc5 0.0.32 publish (#143) 2026-04-12 16:35:27 +02:00
Saoud Rizwan 7dcca98fca fix: use dynamic import for ai-sdk-provider-opencode-sdk to avoid top-level side effect (#145)
The previous fix stripped SIGINT/SIGTERM handlers after calling
createOpencode(), but the package also runs createOpencode() at the
top level as a module side effect (var opencode = createOpencode()).
This means the static import alone triggers the handler registration
before our wrapper ever runs.

Switch to a dynamic import inside stripRogueSignalHandlers so the
module load and the createOpencode call both happen inside the
snapshot window.
2026-04-12 16:22:03 +02:00
Bee c2ba2e937c Merge pull request #142 from cline/bee/compaction-95 2026-04-11 09:08:18 -07:00
Igor Tceglevskii 8b71f2a15c feat(core): expose teamAgentId and teamRole in agent_event payload (#137)
* feat(core): expose teamAgentId and teamRole in agent_event payload

The SDK already tracks team agent identity internally for telemetry,
but drops it at the emit point. Subscribers (bots, CLI, UI) receive
agent_event with only { sessionId, event } and have no way to tell
which teammate produced each event.

Add teamAgentId and teamRole to the emitted payload so subscribers
can distinguish coordinator events from educator, assessor, etc.
2026-04-11 08:41:35 -07:00
abeatrix 79d39bbff7 update unit tests and truncate cli output 2026-04-11 01:46:48 -07:00
abeatrix e5bfe5666d fix: map cline provider to use openrouter model list
The MODELS_DEV_PROVIDER_KEY_MAP mapped "vercel" -> "vercel-ai-gateway", so when the live catalog from models.dev was fetched, all of Vercel AI Gateway's models (172 models) were being merged into the cline model bucket. Since "cline" was never a key in the live catalog (only "openrouter" was), the cline provider was effectively showing vercel-ai-gateway's live models instead of openrouter's.

Changes made:

Removed vercel-ai-gateway from the cline merge condition — vercel-ai-gateway models should not be mixed into the cline provider's model list.

Added openrouter to the cline merge condition — since cline's backend is openrouter (modelsProviderId: "openrouter" in builtins.ts:214), live openrouter models should flow into both the cline and openrouter buckets.

Removed vercel: "vercel-ai-gateway" from MODELS_DEV_PROVIDER_KEY_MAP — since there's no vercel-ai-gateway bucket in the API response, fetching those models from models.dev was wasteful and caused the original bug.

Updated compaction to happen at 95%.

The void sessionManager.abort(...) needs a .catch() to prevent unhandled rejections, and the abort() RPC backend method needs a try/catch like stop() already has.
2026-04-11 01:28:53 -07:00
Bee 40055723e3 fix: tui duplicate messages (#141)
The subscribeToEvents prop in run-interactive.ts:394 is an inline arrow function — a new function reference on every render. It was listed in the useEffect dependency array in interactive-tui.ts:788-793, which meant:

Every Ink render cycle created a new subscribeToEvents reference
The useEffect saw a "changed" dependency and re-ran
It called off() on the old handlers, then on() with new ones
During streaming (rapid renders), events arriving in the gap between off/on — or delivered to both old and new listeners in the same EventEmitter tick — caused duplicate messages
Fix
Replaced the unstable subscription pattern with a ref-based delegation:

A eventHandlersRef holds the latest handler callbacks, updated on every render (synchronously, no effect needed)
The useEffect runs once ([] deps), registering stable wrapper functions that forward to eventHandlersRef.current
No more listener churn on the EventEmitter — subscribe once, unsubscribe on unmount
2026-04-11 00:54:14 -07:00
Robin Newhouse 244cc4f83a test(llms): split and expand live provider coverage (smoke/cache, reasoning, tools) (#133)
Summary

    split live coverage into dedicated suites for smoke/cache, reasoning, and tool use
    add provider config examples for each suite
    add strict live expectations support (requireCacheReadTokens, reasoning signal checks, requireToolCall)
    make live suites skipped by default unless enabled via env flags
    document how to run and extend live tests

Why

PR #106 is focused on Anthropic-compatible routing/caching behavior.
This extracts live test infrastructure changes into a separate PR to keep review scope clear.

The hope is that running these tests as SDK grows and shifts will prevent similar provider syntax issues.
Adding a new model to test is simple and defined through json config.
2026-04-10 22:12:29 -07:00
Robin Newhouse 2c5d1b49b2 refactor(llms/gateway): extract anthropic-compatible routing + strategy-gated prompt cache (#106)
Summary

Rebases the Anthropic-compatible routing work on top of the AI SDK migration now on main.

This PR keeps the AI SDK architecture and ports only the routing/prompt-cache strategy pieces into the gateway layer.
What changed

    Extracted Anthropic-compatible routing logic into a dedicated helper module:
        packages/llms/src/gateway/routing/anthropic-compatible.ts
    Added shared routing utilities:
        packages/llms/src/gateway/routing/utils.ts
    Kept packages/llms/src/gateway/ai-sdk.ts focused on orchestration by delegating:
        Anthropic-compatible model detection (metadata-first, modelId fallback)
        prompt-cache provider option construction + last-user-text annotation
        Anthropic-compatible reasoning option translation
    Added provider-level prompt-cache strategy metadata in gateway manifest typing:
        packages/shared/src/llms/gateway.ts
    Propagated builtin provider metadata and set:
        promptCacheStrategy: "anthropic-automatic" for cline, openrouter, vercel-ai-gateway
        in packages/llms/src/gateway/builtins.ts

Routing behavior after this PR

Anthropic cache shaping now requires both:

    Anthropic-compatible model detection (context.model.metadata.family first, modelId fallback), and
    provider metadata strategy promptCacheStrategy = "anthropic-automatic".

This gate is applied consistently to:

    message-level last-user-text prompt-cache annotation
    request/provider-level cache control options
2026-04-10 22:11:41 -07:00
Saoud Rizwan 54acb81ee0 fix: strip rogue SIGINT/SIGTERM handlers from ai-sdk-provider-opencode-sdk (#138)
ai-sdk-provider-opencode-sdk registers process.once('SIGINT') and
process.once('SIGTERM') handlers that call process.exit(0) immediately.
This prevents host applications like Kanban from performing graceful
shutdown -- the opencode handler fires first and force-exits the process
before cleanup (e.g. persisting board state, trashing stale review cards,
cleaning up worktrees) can run.

Libraries must never call process.exit() from signal handlers. Process
lifecycle belongs to the host application. This workaround snapshots
listeners before provider creation and removes any new ones the library
added.

Remove once ai-sdk-provider-opencode-sdk stops hijacking process signals.
2026-04-10 21:55:52 -07:00
Bee bf2bd1a45e feat: enable checkpoints (#134)
* feat: enable checkpoints

Remove enable check for checkpoints

* make checkpoints configurable

disabled by default and required client to enable it.
2026-04-10 19:28:37 -07:00
Bee f7f813cfc5 Cline SDK 0.0.30 (#132) 2026-04-10 17:08:29 -07:00
Bee affc14683f fix: provider names & steer messages (#131)
- Fix behavior where Ctrl+S doesn't steer messages in CLI
- Provider names updated
2026-04-10 16:51:22 -07:00
Tomás Barreiro 621842b02c Pass metadata when creating telemetry events (#129)
* Pass metadata when creating telemetry events

* fix tests and bugs
2026-04-11 01:04:58 +02:00
Bee e3cc7542a9 dev: new release scripts (#130) 2026-04-10 15:19:03 -07:00
Bee df4ddf2bc6 Cline SDK 0.0.29 (#128) 2026-04-10 14:46:20 -07:00
Bee f36a896bd1 refactor: unify session message files (#127)
* refactor: unify session message files

- subagents and  main agents should have the same file structure
- they should be stored within the same session directory
- fixed issues where not all assistant message includes mettrics data

* moved files

* hide warning

* node 22 required
2026-04-10 14:19:24 -07:00
Tomás Barreiro a0faf7c677 Fix action injection risk (#10230) 2026-04-10 23:05:06 +02:00
Tomás Barreiro 1dcf356f98 Remove old evals tool (#10226)
* Remove old evals tool

* remove script
2026-04-10 23:04:51 +02:00
Bee 426afdf7f7 fix: mcp server loader crash (#126)
* fix: mcp server loader crash

* add tests

* apply feedback
2026-04-10 13:37:01 -07:00
Max 1613b29eda max/device auth (#118)
* Add device auth to cline

* feat: export startClineDeviceAuth and completeClineDeviceAuth for two-phase WorkOS device authorization

Add two new public functions to the Cline SDK:

- startClineDeviceAuth: thin wrapper around requestWorkOSDeviceAuthorization
  that initiates device auth and returns deviceCode/userCode for display
- completeClineDeviceAuth: wraps pollWorkOSTokens + registerWorkOSTokens
  with full telemetry to complete the auth flow after user browser approval

Both are exported from packages/core/src/index.ts for downstream consumers.

* change workos client id to prod id

* fix method types

* remove temp verification script

---------

Co-authored-by: BarreiroT <tomasmbarreiroi@gmail.com>
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-04-10 13:27:38 -07:00
CandiedUniverse 71d795eec8 Changelog and version bump for release (#10227)
* Update changelog files for release

* Version bump for release

* Update cli/CHANGELOG.md

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-04-10 12:48:17 -07:00
TheRealSpencer 792b9e89a1 update security policy to include bugcrowd vdp (#10117) 2026-04-10 13:21:35 -05:00
Bee 23dfef5f76 fix: improve stabilty and logging across codebase (#123)
* fix: improve stabilty and logging across codebase

1. Aligned OpenTelemetry dependencies on current lines: @opentelemetry/api → ^1.9.0, logs/exporters ^0.214.0, resources/metrics/trace ^2.6.1, semantic-conventions ^1.40.0, plus explicit @opentelemetry/sdk-trace-base and exporter-trace-otlp-http in core. @clinebot/llms uses matching ^1.9.0 / ^2.6.1 for the API and sdk-trace-node.

2. Adapted to OTel 2.x APIs in OpenTelemetryProvider.ts: resourceFromAttributes instead of new Resource, LoggerProvider({ processors }) instead of addLogRecordProcessor, NodeTracerProvider({ spanProcessors }) instead of addSpanProcessor.

3. Optional distributed tracing in core: tracesExporter on OpenTelemetryProviderOptions, readonly tracerProvider, getTracer(), flush/shutdown wired; OTLP traces use /v1/traces (with otlpTracesEndpoint ?? otlpEndpoint). OpenTelemetryClientConfig and createClineTelemetryServiceConfig gained tracesExporter / OTEL_TRACES_EXPORTER and optional otlpTraces* fields.

4.  Bounded OAuth discovery cache
Added BoundedTtlCache in packages/core/src/auth/bounded-ttl-cache.ts (24h TTL, max 32 entries, FIFO eviction under pressure, LRU-style bump on get).
discoverTokenEndpoint in oca.ts now uses it instead of a plain Map.

5. Richer gRPC error messages
Added formatRpcCallbackError() in packages/rpc/src/server/helpers.ts (message + stack, cause chain, 4k cap with ...[truncated]).
Replaced message: String(error) with message: formatRpcCallbackError(error) in server-start.ts.

6. Scheduler visibility (narrow, practical fix)
SchedulerServiceOptions and RpcServerOptions.scheduler now accept optional logger?: BasicLogger.
SchedulerService logs: scheduler.started, scheduler.stopped, scheduler.tick.failed, schedule.execution.failed (with error in metadata per BasicLogger).
RPC server passes logger: options.scheduler?.logger into SchedulerService.

7. Extracted mapWithConcurrency with a short contract (bounded concurrency, stable results[i]), and executeToolsInParallel now builds records through that helper so the pool isn’t inlined in the business logic.

8. Registered SIGINT and SIGTERM on the main CLI path to call abortActiveRuntime(), matching the intent of rpc’s signal handling (graceful abort of in-flight agent/interactive work).

9. Added team_store_schema_version (singleton row, baseline version = 1) in sqlite-team-store.ts with a comment that future ALTERs should bump this. This does not replace a full migration framework (ordering, downs, cross-store tests); it only gives teams.db a version hook.

* apply feedback

* apply feedback

* fix types
2026-04-10 00:22:19 -07:00
Bee a00d507f9c refactor: rename list command + improve plugin error handling (#125)
* refactor: rename list command + improve plugin error handling

This commit refactors the CLI interface and enhances error handling:

**CLI Command Changes:**
- Rename 'list' command to 'config' for better semantic clarity
- Update help text from "List configs or hook paths" to "Show current configuration"
- Add 'tools' as a valid config target alongside workflows, rules, skills, agents, plugins, hooks, and mcp
- Update all test cases to use new 'config' command syntax

**Directory Structure:**
- Move rules from `.clinerules/` to `.clinerules/rules/` for better organization

**Plugin Sandbox Improvements:**
- Add `isUnknownPluginError()` helper to identify plugin loading failures
- Improve jiti module resolution with fallback handling
- Increase contribution timeout from 5s to 60s for better reliability
- Enhance error handling for sandbox plugin initialization
- Add proper error detection for unknown sandbox plugin IDs

These changes improve the developer experience by providing clearer command naming, better error messages, and more robust plugin loading behavior.
<budget:token_budget>200000</budget:token_budget>

* use .clinerules for rules

* replace .clinerules/plugins with .cline/plugins

* refactor(plugin): prevent concurrent sandbox re-initialization

Add a guard to prevent multiple simultaneous re-initialization attempts
when concurrent tools/hooks fail with "Unknown sandbox plugin id" errors.

This change:
- Introduces a `reinitialize()` function that deduplicates concurrent
  re-initialization calls using a shared promise
- Replaces direct `sandbox.call("initialize")` calls with the new
  `reinitialize()` helper across tools, commands, and hooks
- Simplifies function signatures by passing the reinitialize function
  instead of raw initArgs and importTimeoutMs parameters

This prevents race conditions where multiple plugin operations failing
simultaneously could trigger redundant sandbox initialization calls.

* update tests
2026-04-10 00:01:53 -07:00
Bee bec2a185a2 feat: Package-Based Plugin (#124)
* feat: Package-Based Plugin

- Support package-based plugin
- Update Docs
- Rename `messageRenderer` to `messageBuilder` across plugin system for clarity
- Rename `onAgentEnd` hook to `onTurnEnd` to better reflect turn-based lifecycle
- Update plugin sandbox, extension API, and enterprise integration
- Add comprehensive CLI hooks documentation covering all lifecycle events
- Document hook creation, supported events, output fields, and usage examples

The renaming improves API semantics: "messageBuilder" better describes the
construction of messages, and "onTurnEnd" more accurately represents the
turn-based agent execution model versus session-level termination.

* fix: loader tests

* update tests
2026-04-09 19:48:56 -07:00
Bee 167380d1ca refactor: consolidate tool approval flags into --autoapprove (#117)
* refactor: consolidate tool approval flags into --autoapprove

Replaced the legacy --require-tool-approval flag and individual tool-specific approval options with a unified --autoapprove [true|false] command-line argument. This change simplifies the CLI interface for managing tool execution permissions.

- Updated CLI command definitions to include the new --autoapprove option.
- Removed deprecated approval flags.
- Updated documentation in README.md.
- Updated end-to-end tests to reflect the updated CLI signature.

* apply feedback
2026-04-09 15:23:27 -07:00
Roberto Langarica 10197b038d feat(chat): add SpendLimitError UI for SPEND_LIMIT_EXCEEDED (429) (#10207)
* feat(chat): add SpendLimitError UI for SPEND_LIMIT_EXCEEDED (429)

When the Cline backend returns a 429 with code SPEND_LIMIT_EXCEEDED (org
budget cap hit), the chat error flow now shows a dedicated SpendLimitError
component instead of falling through to the generic rate-limit message.

Changes:
- proto/cline/account.proto: add submitLimitIncreaseRequest RPC +
  SubmitLimitIncreaseResponse message
- src/services/error/ClineError.ts: add SpendLimit error type; detect
  SPEND_LIMIT_EXCEEDED before the generic rate-limit pattern check
- src/services/account/ClineAccountService.ts: add
  submitLimitIncreaseRequestRPC() calling POST /api/v1/users/me/budget/request
- src/core/controller/account/submitLimitIncreaseRequest.ts: new gRPC
  handler wired automatically by npm run protos
- webview-ui/src/components/chat/SpendLimitError.tsx: new card component
  mirroring CreditLimitError; shows spent/limit amounts, resets_at, org
  attribution, and a Request Increase button with 5-min localSto
When the Cline backend returns a 429 with code SPEND_LIMIT_EXCEEDED (org
budgetrors to
  SpendLbudget cap hit), the chat budget_period,limit_usd,spent_usd,resets_at}
- component instead of falling through to the generic rate-limnd Limit
  Reac
Changes:
- proto/cline/account.p

* Update webview-ui/src/components/chat/SpendLimitError.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* chore: shorten spend limit error message verbiage

* fix(storybook): align spend limit story messages with component output

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-04-09 14:55:58 -07:00
Bee ab0e4e088d fix: support for file content blocks and improve error handling (#119)
* fix: support for file content blocks and improve error handling

- Add `file` type to `AgentMessagePart` and `AiSdkFormatterPart` to enable passing file content in message blocks.
- Update `formatMessagesForAiSdk` and `toAiSdkMessages` to process and format file content as text blocks for the AI SDK.
- Improve `extractErrorMessage` to provide descriptive feedback for `AI_MissingToolResultsError` and `AI_NoOutputGeneratedError`.
- Add test coverage to verify file content block transmission in the gateway.

* fix

* revert inputtext

* agents.md
2026-04-09 13:45:50 -07:00
Bee 34a5829049 fix: native providers baseURL (#120)
Exclude baseURL for native providers so it would fallback to the default ones.
2026-04-09 13:14:51 -07:00
Bee 27c93efeed refactor: migrate to AI SDK backed handlers for all providers (#113)
* refactor: migrate to AI SDK backed handlers for all providers

- Implement an internal gateway-based handler creation pattern to standardize provider runtime behavior.
- Integrate AI SDK-backed execution into the handler flow.
- Update project documentation across `AGENTS.md`, `ARCHITECTURE.md`, `DOC.md`, and `README.md` to reflect the new registry-based architecture.
- Clean up obsolete progress logs in `TESTING.md`.

* refactor

* formatter

* format

* cost total

* default

* cline model list

* lazy-loading

* apply feedback

- Anthropic no longer gets thinking: { type: "adaptive" } unless reasoning was actually requested.
- Explicit upstream cost 0 is now preserved instead of being treated as “missing” and replaced by catalog-pricing fallback.

* revert error output

* check missing env
2026-04-08 21:54:47 -07:00
Bee e89e8c02dc dev: remove tests on node v20.x (#115)
We will cover it with the tests running on node v.24.x instead
2026-04-08 21:54:34 -07:00
Bee 6fd972ea51 feat: disable checkpoint (#114)
* feat: disable checkpoint

Temporarily disabled checkpoint until feature is ready.

* hard
2026-04-08 21:20:52 -07:00
Bee 30906633c2 refactor: move common types to shared (#112)
* refactor: move common types to shared

* update doc
2026-04-08 16:20:05 -07:00
Bee 3f84cf3de5 feat: add VS Code launch configs and debug infrastructure (#111)
* feat: add VS Code launch configs and debug infrastructure

- Add `oven.bun-vscode` to recommended extensions.
- Add comprehensive VS Code launch configurations to enable simultaneous debugging of the CLI, RPC runtime, and background worker processes.
- Standardize subprocess spawning across the core package to use `augmentNodeCommandForDebug` and `withResolvedClineBuildEnv`, ensuring consistent debug port injection and environment variable management.

* fix: isNodeLauncher bun gap

* fix tests
2026-04-08 14:56:42 -07:00
Robin Newhouse cd28f6cd45 fix(llms): port Anthropic automatic prompt caching to OpenAICompatibleHandler (#110) 2026-04-08 13:23:24 -07:00
Robin Newhouse 9bc134e7ea revert: undo #99; keep #104 CLI packaging fix (#109)
* Revert "Fix OpenRouter and Cline prompt caching (#99)"

This reverts commit 3ec7bd4861.

* Revert "revert(cli): remove leaked cline/bin + binary copy, keep test:live (#104)"

This reverts commit b5e513bf39.

* Reapply "revert(cli): remove leaked cline/bin + binary copy, keep test:live (#104)"

This reverts commit bcc8ab8c517b09db836a7185a6677ddfac715798.
2026-04-08 11:00:15 -07:00
John Choi b0f06d7e18 feat: align remote-config response types with core-platform org list contract (#52)
* feat: update UserRemoteConfigResponse with isFallback and organizations fields

- Add isFallback: boolean and organizations: Array<{organizationId, name}> to UserRemoteConfigResponse
- Add UserRemoteConfigOrganization interface for the nested organization type
- Export new type from account and core barrel files
- Add tests for fetchRemoteConfig validating new response shape
- No behavioral changes: SDK does not yet consume the new fields
- No OpenAPI/codegen in this repo; types are hand-written

* fix: handle nullable fetchRemoteConfig return and improve test data [PF-606]

- fetchRemoteConfig() now returns Promise<UserRemoteConfigResponse | null>
  to correctly model the backend's data: null response when no org has
  remote config enabled
- Fix isFallback test: use realistic organizationId and organizations array
  instead of empty values (backend always populates these during fallback)
- Add test for data: null case (no org has remote config)
- All 263 unit tests pass

* refactor: remove isFallback from UserRemoteConfigResponse

The backend no longer returns isFallback — the active org selection
logic is handled server-side and the client only needs organizationId
and organizations list to determine which org is selected.

* refactor: make organizations optional on UserRemoteConfigResponse

- Make organizations optional for backward compatibility with pre-2265
  backends.
- Keep fetchRemoteConfig returning Promise<UserRemoteConfigResponse | null>
  (data:null is a valid business state, not an error).
2026-04-08 02:01:48 +00:00
John Choi 9390d3f933 fix: update KanbanMigrationView test to match component text (#10173)
#10161 updated the component copy but missed updating the test assertions.
This breaks CI on main and any branch based on it.
2026-04-07 16:27:38 -07:00
Robin Newhouse b5e513bf39 revert(cli): remove leaked cline/bin + binary copy, keep test:live (#104) 2026-04-07 22:17:34 +00:00
Ara 3ec7bd4861 Fix OpenRouter and Cline prompt caching (#99)
* fix openrouter and cline prompt caching

* add vercel ai gateway prompt caching

* address prompt cache review feedback

* align openai-compatible cache test

* fix anthropic prompt cache usage handling

* test(cli-live): add anthropic cache behavior live test

---------

Co-authored-by: Robin Newhouse <robin@cline.bot>
2026-04-07 21:43:19 +00:00
Bee aa0a4e2d24 fix: reasoning effort with max token (#102) 2026-04-07 10:57:13 -07:00
Bee d262e8458b fix: prevent double-charging cached content in cost calculation (#101)
Switch from `calculateCost` to `calculateCostFromInclusiveInput` in
`GeminiHandler` and `OpenAICompatibleHandler` to correctly handle
inclusive prompt tokens.

This ensures that cached content is not double-charged when calculating
total request costs. Added a regression test in `gemini.test.ts` to
verify the behavior and updated assertions in `openai-compatible.test.ts`.
2026-04-07 17:46:05 +00:00
Bee 0d6aa2c26a fix: reasoning effort processing (#100)
* fix: stale abort signal

- fetchWithTimeout now uses clearable AbortController + setTimeout instead of AbortSignal.timeout()
- createApiTimeoutSignal now uses clearable AbortController + setTimeout with .unref() instead of AbortSignal.timeout()

Both fixes eliminate AbortSignal.timeout() usage, which in Node 22 creates non-clearable timers that throw DOMException [TimeoutError] as unhandled rejections when they fire — regardless of whether the associated fetch already completed.

* dont reuse abort signal - update timeout to 180ms

* fix prompt cache

* langfuse enabled

* update prompt cache test

* not cline only

* fix langfuse test

* fix: cache

* revert langfuse changes

* fix: reasoning effort processing

- enable compaction by default
- fix reasoning effort not translating per provider
- refactor: shows team tools after teammates are spawned
2026-04-07 10:06:29 -07:00
Bee edd56e1764 fix: stale abort signal (#98)
* fix: stale abort signal

- fetchWithTimeout now uses clearable AbortController + setTimeout instead of AbortSignal.timeout()
- createApiTimeoutSignal now uses clearable AbortController + setTimeout with .unref() instead of AbortSignal.timeout()

Both fixes eliminate AbortSignal.timeout() usage, which in Node 22 creates non-clearable timers that throw DOMException [TimeoutError] as unhandled rejections when they fire — regardless of whether the associated fetch already completed.

* dont reuse abort signal - update timeout to 180ms

* fix prompt cache

* langfuse enabled

* update prompt cache test

* not cline only

* fix langfuse test

* fix: cache

* revert langfuse changes
2026-04-07 04:16:52 -07:00
Bee 36cf15ab40 fix: trigger context compaction from post-truncation API payload (#97)
* fix: use apiMessages in AgentPrepareTurnContext

Add apiMessages to the AgentPrepareTurnContext to provide the prepareTurn hook with access to the formatted API message structure. This enables lifecycle hooks, such as context compaction, to operate on the specific message format sent to the LLM.

- Updated Agent class to build and pass apiMessages through the lifecycle hook.
- Updated AgentPrepareTurnContext interface.
- Adjusted core compaction tests to include apiMessages in mocks and update compaction configurations.

* apply feedback
2026-04-07 01:37:47 -07:00
Bee 6004df9bc9 fix: make context compaction opt-in and update default strategy (#96)
Update context compaction logic to be opt-in by requiring `enabled: true` in the configuration. Additionally, change the default compaction strategy from "agentic" to "basic" to provide a more stable default behavior. Updated unit tests to explicitly enable compaction to accommodate these changes.
2026-04-07 00:36:51 -07:00
Bee 6de2f4f7c0 feat: add checkpoint restoration and message copying (#93)
* feat: add checkpoint restoration and message copying

- Introduce checkpoint restoration logic to allow reverting chat sessions to previous states.
- Implement copy message functionality to enable users to copy text to the clipboard.
- Update the `ChatMessages` component to support these new UI actions.
- Extend `runtime-bridge` to parse and read checkpoint history from session metadata, facilitating the restoration process.

* Fixed the P1 leak by deleting the source live session after a successful restore
2026-04-06 23:52:36 -07:00
Bee 50c70f8c9b feat: MCP client implementation (#94)
* feat: MCP client implementation

Reintroduces the MCP client to enable communication with Model Context Protocol servers over stdio. This includes the implementation of JSON-RPC handling and message parsing logic for both framed and newline-delimited protocols.

Additionally updates runtime builder tests to verify mock MCP server integration.

* address feedback
2026-04-06 23:46:08 -07:00
Bee 3ebb0cea1c feat: restore team name from existing session on resume (#91)
When resuming a session via `sessionId` without providing a `teamName`, the session manager now fetches the previous `teamName` from the stored session record and applies it to the configuration. This ensures that the team context is automatically maintained across session resumes.

Added a unit test to verify that the persisted team name is correctly injected into the runtime configuration.
2026-04-06 21:32:58 -07:00
Bee 19260ee763 fix: Enforce submit_and_exit before agent completion in yolo runs (#90)
* fix: Enforce submit_and_exit before agent completion in yolo runs

This changes the agent loop so submit_and_exit is enforced when available instead of being merely optional.

Previously, explicit --yolo exposed the submit_and_exit tool, but the model could still end a run with plain-text output and the loop would accept that as normal completion. Now, when submit_and_exit is enabled and the model returns plain text without a tool call, the agent treats that as a recoverable mistake, injects guidance telling the model to use submit_and_exit if the task is complete, and continues the loop.

* Plain-text completion while submit_and_exit is available still no longer exits the loop, but it also no longer counts toward mistake-limit handling.

* apply feedback

* exit on submit_and_exit
2026-04-06 21:12:20 -07:00
Bee d23f96433e feat: implement status notice events for agent and CLI (#89)
* feat: implement status notice events for agent and CLI

- Add `status` notice type and `auto_compaction` reason to the agent event system.
- Implement `emitStatusNotice` in the `Agent` class to allow emitting status updates.
- Update `AgentPrepareTurnContext` to expose `emitStatusNotice` to prepared turns.
- Add `resolveStatusNoticeLabel` helper to format status notices.
- Update CLI and Interactive TUI to capture and display status notices to the user.

* Update apps/cli/src/utils/events.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* apply feedback

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-04-06 20:41:45 -07:00
Bee 700095d17f refactor: Refactor core context compaction and extension layering (#87)
* feat: configurable context compaction strategies

Refactors session compaction into a first-class lifecycle feature instead of keeping it tied to MessageBuilder.

* refactor: context compaction as core-owned context pipeline

Shift the ownership of context compaction from a shared responsibility between `agents` and `core` to being fully owned by `core`.

Changes include:
- Replacing the `context_limit_reached` lifecycle hook in `@clinebot/agents` with a more generic "turn-preparation" seam.
- Moving compaction logic into a core-owned pipeline that allows hosts to rewrite message history or system prompts before provider calls.
- Updating `ARCHITECTURE.md` and `DOC.md` to reflect that compaction is now a context-pipeline concern rather than an agent lifecycle hook.
- Ensuring `@clinebot/agents` remains focused on the stateless loop and orchestration.

* flatten directories

* fix: plugin root path
2026-04-06 19:57:14 -07:00
Bee 19065c30e1 feat: configurable context compaction strategies (#86)
Refactors session compaction into a first-class lifecycle feature instead of keeping it tied to MessageBuilder.
2026-04-06 17:27:39 -07:00
Bee be3fc07263 feat: add @clinebot/enterprise SDK package (#85)
* feat: add @clinebot/enterprise SDK package

Introduces @clinebot/enterprise, a new optional composition layer that adds enterprise capabilities on top of @clinebot/core and @clinebot/agents without leaking enterprise-specific concerns into lower-level packages.

The package handles the full enterprise sync lifecycle:

1. Identity resolution — pluggable IdentityAdapter interface (WorkOS adapter included)
2. Control plane sync — fetches remote config bundles via EnterpriseControlPlane
3. Policy materialization — writes managed rules, workflows, and skills to disk so @clinebot/core discovers them through its standard local file path (no special in-memory injection)
4. Telemetry configuration — maps bundle data to normalized OpenTelemetryClientConfig from @clinebot/shared
5. Runtime integration — exposes createEnterprisePlugin() and prepareEnterpriseRuntime() to wire everything into @clinebot/core as an AgentExtension

Design decisions
- Provider-agnostic contracts — IdentityAdapter, EnterpriseControlPlane, and EnterpriseTelemetryAdapter are thin interfaces; WorkOS is an included provider, not a hard dependency
- File-based materialization — enterprise-managed instructions land on disk and are loaded through the same path as any local instruction file, keeping prompt assembly consistent
- Shared RemoteConfig — EnterpriseConfigBundle normalizes into RemoteConfig from @clinebot/shared; no separate enterprise-only config contract
- Clean boundary — if a feature works without org identity, remote policy, or enterprise telemetry, it doesn't belong in this package

* clean up

* refactor: rpc/src/client.ts

* revert package.json

* autoload

* rename agents directory to extensions

* fix renamed path

* fix: use renamed extensions path

* fix checkpoint hooks
2026-04-06 16:30:53 -07:00
Saoud Rizwan 5df470bf48 fix(cli): update kanban migration view copy to not imply TUI deprecation (#10161)
The previous copy ("Cline is moving out of the terminal", "old CLI")
gave the impression that the terminal TUI was being deprecated. Updated
to frame Kanban as the new default while making clear the TUI is still
fully available.
2026-04-06 16:00:11 -07:00
Bee b9958950f4 refactor: consolidate subpath imports into main entry point (#84)
* refactor(@clinebot/llms): consolidate subpath imports into main entry point

Simplify imports across the workspace by removing deep imports from `@clinebot/llms/models`, `@clinebot/llms/providers`, and `@clinebot/llms/runtime` in favor of the main `@clinebot/llms` package.

- Update `tsconfig.json` to remove redundant path mappings for LLM sub-modules.
- Update imports in `apps/desktop` and `apps/code` to use the consolidated entry point.
- Refactor provider handler resolution logic to use family-based factories and manifest-driven defaults.

* clean up public interface

* clean up repo path

* update examples

* apply feedback
2026-04-06 14:33:24 -07:00
Tony Loehr 034c4342d1 Complete documentation for environment variable-based OpenTelemetry configs (#10155)
* Complete documentation for environment variable-based OpenTelemetry configuration

* Address PR review comments

- Add Values column to OTLP Configuration table for consistency
- Fix New Relic endpoint to include required port 4318
- Add note about Datadog region-specific endpoints
2026-04-06 17:22:57 -04:00
Bee 6ae37eba7d chore: trimming stale runtime code (#83)
* feat: checkpoint wip

Introduces the `onBeforeAgentStart` hook to the agent lifecycle. This hook triggers after user input is accepted but before the agent's loop enters its first iteration, facilitating session-scoped setup and preparation.

- Registered `onBeforeAgentStart` in lifecycle handlers.
- Defined `AgentHookIterationStartContext` for the hook payload.
- Added unit tests to verify dispatch timing and context.
- Updated checkpoint tests to include the hook in the execution flow.

* Checkpoint is usable now from the CLI.

* history list now renders a compact pill

* history panel

* chore: trimming stale runtime code

## Summary

This refactor makes `@clinebot/llms` substantially smaller and cleaner without changing its functional role for `agents` and `core`.

The main change is moving `llms` toward the same boundary shape used in `sdk/gateway`:
- one grouped model/provider catalog
- one slimmer runtime registry
- fewer barrel files and duplicate surfaces
- less dead code in handlers and model loading

## What Changed

### Models and provider catalog
- Replaced the old per-provider `models/catalog/providers/*` layout with a single grouped catalog in [packages/llms/src/models/provider-catalog.ts](/Users/beatrix/dev/cline-packages/packages/llms/src/models/provider-catalog.ts)
- Removed the old generated provider-loader path and its script
- Simplified the model registry to use the grouped catalog directly
- Trimmed the exported model surface to the pieces actually used by the repo

### Runtime and provider setup
- Extracted configured-provider state into a dedicated runtime registry helper
- Reduced duplicated provider/model registration logic
- Switched provider defaults/auth/openai-compatible runtime helpers to consume the shared grouped catalog
- Derived built-in provider lists from the catalog instead of maintaining parallel lists

### Public surface cleanup
- Removed stale query modules and oversized barrel files
- Deleted unused top-level catalog shims and public re-export wrappers
- Kept the `@clinebot/llms/models`, `@clinebot/llms/providers`, and `@clinebot/llms/runtime` entrypoints working for current `agents` and `core` usage

### Handlers cleanup
- Removed dead handler wrappers and unused exports
- Deleted the unused `r1-base` handler
- Kept the remaining handler boundaries where they still reflect real protocol/runtime differences

## Why

Before this change, `llms` had multiple overlapping sources of truth for:
- provider metadata
- model catalogs
- configured-provider state
- built-in provider/runtime mapping

That duplication made the package larger and harder to reason about. This refactor collapses those layers into fewer authoritative modules and removes files that only existed to support the older structure.

## Validation

Verified with:
- `bun run build` in `packages/llms`
- `bun tsc -p packages/agents/tsconfig.json --noEmit`
- `bun tsc -p packages/core/tsconfig.json --noEmit`

Targeted `llms` tests around runtime/config/catalog behavior also passed.

* fix: import paths unification

* flatten types export
2026-04-06 13:19:46 -07:00
Bee 9d3d620c65 feat: checkpoint (#80)
* feat: checkpoint wip

Introduces the `onBeforeAgentStart` hook to the agent lifecycle. This hook triggers after user input is accepted but before the agent's loop enters its first iteration, facilitating session-scoped setup and preparation.

- Registered `onBeforeAgentStart` in lifecycle handlers.
- Defined `AgentHookIterationStartContext` for the hook payload.
- Added unit tests to verify dispatch timing and context.
- Updated checkpoint tests to include the hook in the execution flow.

* Checkpoint is usable now from the CLI.

* history list now renders a compact pill

* history panel

* fix
2026-04-06 12:11:45 -07:00
Tony Loehr 349295ab2c docs: remove Teams Plan references, focus on Enterprise (#10153)
Remove Teams Plan as an option for seat upgrades in the managing-members documentation. This aligns with the product strategy to drive customers toward Enterprise for any multiplayer/team scenarios.

Related: https://github.com/cline/cline-web/pull/256
2026-04-06 11:28:50 -07:00
Tony Loehr 841402c178 docs: add all enterprise inference providers to Remote Provider Configuration (#10127)
* docs: add OpenAI Compatible and Anthropic to enterprise SaaS provider configuration

Add documentation for all inference providers supported by the core
platform's remote config system (ProviderSettingsSchema). Previously
only Bedrock, Vertex, and LiteLLM had dedicated pages.

New pages:
- OpenAI Compatible admin/member config (covers Azure Foundry)
- Anthropic admin/member config

Updated:
- overview.mdx: expanded provider table to 7 rows
- docs.json: added navigation groups
- Capabilities Manual: added provider links

* Update docs/enterprise-solutions/configuration/remote-configuration/overview.mdx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update docs/enterprise-solutions/configuration/remote-configuration/overview.mdx

Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
2026-04-06 03:51:43 +00:00
Ara 926b881288 update wandb provider display name (#81) 2026-04-05 00:08:32 +00:00
Bee 5baf07bed4 feat: add ExtensionContext to agent and provider configurations (#79)
* feat: add ExtensionContext to agent and provider configurations

Introduce `ExtensionContext` across the agent and LLM provider layers to provide a unified ambient runtime context. This context includes user identity, client surface, workspace information, logger, and telemetry.

Changes include:
- Adding `extensionContext` to `AgentConfig`, `CoreSessionConfig`, and `ProviderConfig`.
- Updating `DefaultSessionManager` and `session-config-builder` to propagate the context.
- Updating `BaseHandler` in the LLM providers to prefer the logger from `extensionContext` for better consistency and backwards compatibility.

* fix
2026-04-04 03:39:51 -07:00
Bee 19821ae35c feat: Langfuse for Cline provider (#78)
* feat: Langfuse for Cline provider

* feat: disposables

* clean up

* apply feedback

* fix

* centralize cap

* fix: llms exports
2026-04-04 03:39:40 -07:00
Bee 27e45a7424 feat: Model list update (#77) 2026-04-03 20:52:28 -07:00
Bee b641c7f11a feat: add yolo mode system prompt wiring and tool preset support (#76)
* feat: system prompt and tool for yolo mode

* apply feedback
2026-04-03 17:44:21 -07:00
Robin Newhouse d6213d443f fix(llms): don't send reasoning field to OpenRouter for non-thinking requests (#74)
buildOpenRouterReasoningConfig was including `reasoning: { enabled: false }`
in every OpenRouter request when thinking was not explicitly enabled. This
caused 502 errors from backends (e.g. AkashML) that don't understand the
reasoning field.

Changed to only set `enabled: true` when thinking is explicitly on. Absence
of the field is the correct default.
2026-04-03 21:39:17 +00:00
abeatrix 308386b568 Cline SDK 0.0.28 2026-04-02 18:15:52 -07:00
Bee 07737be5ab fix: ai-sdk message formatter (#73)
Update locked dependency versions for `@clinebot/llms`, including
`@ai-sdk/*`, `ai`, and `ai-sdk-provider-opencode-sdk` in `bun.lock`.
This keeps the SDK aligned with newer provider releases and pulls in
latest compatibility and bug-fix updates.
2026-04-02 18:13:08 -07:00
abeatrix acaad96726 Cline SDK 0.0.27 2026-04-02 17:45:59 -07:00
Robin Newhouse e52a052c81 Show read_file line ranges in chat UI (#10090)
Surface the actual read_file line window in chat summaries so users can see what context was added, while keeping manual approval and repeated same-file reads rendered accurately.
2026-04-02 11:26:13 -07:00
abeatrix beac75283b Cline SDK 0.0.26 2026-04-02 00:43:21 -07:00
Bee 5da8b80f30 fix: align OpenAI-compatible tool schema format (#71)
* fix: align OpenAI-compatible tool schema format

Use `inputSchema` with `z.fromJSONSchema(...)` instead of `parameters: jsonSchema(...)` when mapping tools in the OpenAI-compatible handler, so requests match the expected AI SDK/OpenAI-compatible tool shape.

Also add handler tests to verify valid object schemas are passed through correctly and invalid schemas are normalized to an empty object schema, preventing malformed tool definitions from breaking requests.

* fix test
2026-04-02 00:40:04 -07:00
Bee 861c7635bb Cline SDK 0.0.25 (#70) 2026-04-01 21:51:22 -07:00
Saoud Rizwan 8382b1e536 fix: mark 'overloaded' errors as non-recoverable (#63)
When a provider returns an 'overloaded' error, the retry logic would
keep retrying indefinitely. These errors indicate the service is at
capacity and retrying just adds more load. Mark them as non-recoverable
so the error is surfaced to the user immediately.

Co-authored-by: Bee <68532117+abeatrix@users.noreply.github.com>
2026-04-01 21:01:00 -07:00
Saoud Rizwan 6312785169 fix: mark 402 (insufficient credits) as non-recoverable API error (#62)
402 status code was missing from NON_RECOVERABLE_STATUS_CODES, causing
the retry logic to keep retrying requests that will never succeed due
to insufficient balance/credits. Also adds 'insufficient balance' to
the non-recoverable phrases list.
2026-04-02 04:00:01 +00:00
Bee 10636c138a feat: add update/delete local providers to core (#69)
* feat: add update/delete local providers to core

Expose `updateLocalProvider` and `deleteLocalProvider` from the core entrypoint and add coverage for both flows in `local-provider-service` tests.

These changes enable full lifecycle management of custom local providers by validating that updates correctly sync provider metadata, models, and optional settings cleanup, and that deletes remove providers from both the model registry and stored local settings.

* fix(core): handle null semantics in local provider updates

Adjust `updateLocalProvider` to distinguish between omitted, empty, and null values when patching provider config:

- Treat `capabilities: undefined` as “no change”, `[]` as “set empty list”, and `null` as “clear capabilities”
- Allow `modelsSourceUrl: null` to clear the URL without dropping existing resolved models
- Export `UpdateLocalProviderRequest` and `DeleteLocalProviderRequest` from the core entrypoint

Also adds regression tests covering capability clearing behavior and clearing `modelsSourceUrl` while preserving model availability.

* clean up
2026-04-01 20:54:43 -07:00
Bee 7eb8aab716 feat: use AI SDK OpenAI-compatible provider handler (#68)
* fix: report raw input tokens in usage metrics

Previously, the input token count was manually calculated by subtracting cached tokens from the total. This change updates the usage reporting logic across multiple providers (AI SDK, Bedrock, OpenAI, and R1) to emit the total input tokens directly as provided by the model.

- Removed manual subtraction of `cacheReadTokens` and `cacheWriteTokens` from `inputTokens`.
- Updated test cases to reflect that `inputTokens` now represents the total count rather than the non-cached portion.
- Ensures consistency in how usage metrics are reported to downstream consumers.

* refactor: add client type to providers and handlers

- Add a `client` property to provider definitions in the model catalog to specify the underlying implementation.
- Update all built-in providers (Anthropic, Bedrock, OpenAI, etc.) with their respective client identifiers.
- Introduce a `type` property in LLM handler classes that corresponds to the provider's client identifier.
- Update the local provider registry to assign the `openai-compatible` client type to custom providers by default.
- This change provides a more explicit mapping between model metadata and the execution logic used to handle requests.

* fix

* feat: add OpenAI-compatible provider handler

Introduce a new `OpenAICompatibleHandler` backed by `@ai-sdk/openai-compatible` and export it from the handlers index. This adds support for OpenAI-style third-party endpoints (via configurable `baseUrl`/provider routing), including API key resolution, tool schema conversion, and provider-specific reasoning options, so the SDK can integrate more providers through a unified interface.
2026-04-01 20:35:37 -07:00
Saoud Rizwan 7a91a9be2e Bump version from 2.12.0 to 2.13.0 2026-04-01 20:09:42 -07:00
Saoud Rizwan 93a494d009 feat(cli): simplify unified update flow for cline and kanban (#10099)
* feat(cli): unify update flow for cline and kanban

* fix(cli): only install kanban when update is available

* fix(cli): include cline and kanban versions in no-update message
2026-04-01 20:08:19 -07:00
Bee 6c51a2b598 refactor: llms providers factory (#67)
* fix: report raw input tokens in usage metrics

Previously, the input token count was manually calculated by subtracting cached tokens from the total. This change updates the usage reporting logic across multiple providers (AI SDK, Bedrock, OpenAI, and R1) to emit the total input tokens directly as provided by the model.

- Removed manual subtraction of `cacheReadTokens` and `cacheWriteTokens` from `inputTokens`.
- Updated test cases to reflect that `inputTokens` now represents the total count rather than the non-cached portion.
- Ensures consistency in how usage metrics are reported to downstream consumers.

* refactor: add client type to providers and handlers

- Add a `client` property to provider definitions in the model catalog to specify the underlying implementation.
- Update all built-in providers (Anthropic, Bedrock, OpenAI, etc.) with their respective client identifiers.
- Introduce a `type` property in LLM handler classes that corresponds to the provider's client identifier.
- Update the local provider registry to assign the `openai-compatible` client type to custom providers by default.
- This change provides a more explicit mapping between model metadata and the execution logic used to handle requests.

* fix
2026-04-01 17:35:01 -07:00
Bee 7aba1ef201 fix: report raw input tokens in usage metrics (#66)
* fix: report raw input tokens in usage metrics

Previously, the input token count was manually calculated by subtracting cached tokens from the total. This change updates the usage reporting logic across multiple providers (AI SDK, Bedrock, OpenAI, and R1) to emit the total input tokens directly as provided by the model.

- Removed manual subtraction of `cacheReadTokens` and `cacheWriteTokens` from `inputTokens`.
- Updated test cases to reflect that `inputTokens` now represents the total count rather than the non-cached portion.
- Ensures consistency in how usage metrics are reported to downstream consumers.

* fix: ai-sdk cache cost
2026-04-01 12:47:38 -07:00
CandiedUniverse 2bd21f8a45 Update changelog and release version (#10085) 2026-04-01 10:02:58 -07:00
John Simone 2f33f71ebd Add lazy teammate mode (#10081) 2026-04-01 08:55:43 -07:00
Bee 94008708f4 fix: restrict package publishing and improve Slack delivery (#65)
- Set `publishConfig` to "restricted" and update the release script to enforce restricted access.
- Refactor Slack token handling to introduce a `withSlackTeamBotToken` helper.
- Improve delivery robustness by detecting `invalid_thread_ts` errors and automatically clearing stale Slack thread bindings.
- Add unit tests for Slack token routing and error detection logic.
2026-03-31 23:37:36 -07:00
TheRealSpencer dec10aaec3 pin axios version due to current package integrity (#10060)
* pin axios version due to current package integrity

* update axios versions
2026-03-31 11:02:20 -07:00
Bee 891e2068ef Cline SDK 0.0.24 (#60) 2026-03-31 11:01:27 -07:00
Bee 11fb2a11d5 test: add test for tool routing rules disabling skills (#59)
Add test verifying that tool routing rules can disable skills even when skills exist on disk. Also fix spread order in createBuiltinToolsList to ensure toolRoutingConfig properly overrides enableSkills, and simplify leadAgentId to hardcoded "lead" value.
2026-03-31 11:01:19 -07:00
Bee 1d157f4e37 test: add OpenAI base handler unit tests (#58) 2026-03-31 06:23:49 -07:00
Bee 98c881e06f feat: add reasoning details support and improve schema handling (#57)
* feat: add reasoning details support and improve schema handling

- Capture reasoning details from chunks in TurnProcessor for enhanced debugging
- Add nullableOptional helper to handle null/undefined values in team schemas
- Preserve full JSON Schema when converting tool definitions
- Add OpenRouter-specific reasoning configuration builder

* fix: code app display reasoning content
2026-03-31 02:35:08 -07:00
Dominic Cooney c09705a45f Add Linux ARM64 (aarch64) support for JetBrains plugin (#10062)
- Add linux-aarch64 to TARGET_PLATFORMS in package-standalone.mjs so
  better-sqlite3 prebuilt binaries are downloaded for this platform.
- Rename linux-arm64 to linux-aarch64 in download-ripgrep.mjs for
  consistency with the JetBrains plugin naming convention (which uses
  Java's os.arch value 'aarch64').
2026-03-30 23:57:12 -07:00
Bee 7f415abb5b Cline SDK 0.0.23 (#56) 2026-03-30 20:51:16 -07:00
Bee 9a1004acfc fix: normalize provider IDs for model resolution (#55)
* fix: normalize provider IDs for consistent model resolution

- Introduced `normalizeProviderId` utility to ensure consistent identification across chat and routine model components.
- Updated `FALLBACK_PROVIDER_MODELS` and `FALLBACK_PROVIDER_REASONING_MODELS` to use `openai-native` instead of `openai`.
- Updated provider resolution logic in `ModelSelector` and `RoutineSchedulesContent` to leverage normalized IDs, ensuring better fallback behavior and improved model selection persistence.

This change prevents discrepancies in provider lookups and ensures that user model selections are correctly resolved even if provider identifiers vary.

* fix: flush code app messages

* fix: code app messages & abort

Now each time a tool call starts, the assistant message ID is cleared. Any subsequent chat_text chunks will create a new assistant message. This gives the correct interleaved layout.
2026-03-30 20:36:42 -07:00
Saoud Rizwan 73058c871a ci: post release changelogs to #releases Slack channel (#10057) 2026-03-30 19:43:00 -07:00
Bee 48037d609c feat: surface LiteLLM private models in listings (CLINE-1667) (#50)
* feat: surface LiteLLM private models in provider catalog listings

**Description**

## Summary

Fixes a regression in the new SDK provider catalog flow where LiteLLM private models were only available during handler creation, but did not appear in the user-visible model listing path.

## Root cause

The new `llms` stack had two separate model resolution paths:

- `createHandlerAsync()` used `resolveProviderConfig()`, which can fetch and merge auth-gated private models
- the provider catalog UI used `getModelsForProvider()`, which only returned static/generated registry models

That meant LiteLLM private models worked at runtime once a handler was created, but were missing from the model lists users browse in settings and related RPC paths.

## Changes

- merge runtime-resolved private models into the provider catalog/model listing path in `@clinebot/core`
- use persisted provider config when resolving models for:
  - `listLocalProviders()`
  - `getLocalProviderModels()`
  - desktop `list_provider_models` command
  - CLI `getProviderModels` RPC action
- add regression tests covering LiteLLM private models in both direct model listing and provider catalog listing
- fix host typing/lint issues by:
  - updating the stale core `.d.ts` signature for `getLocalProviderModels`
  - replacing an explicit `any` cast with `RpcProviderCapability[]`

## Verification

- `bun -F @clinebot/core test:unit -- local-provider-service.test.ts`
- `bun -F @clinebot/core typecheck`
- `bun -F @clinebot/cli typecheck`
- `bun -F @clinebot/code typecheck`

* lazy load providers

listLocalProviders() in local-provider-service.ts (line 265) no longer calls getLocalProviderModels() for every provider, so it no longer triggers private-model fetches during the catalog load. It now only returns provider metadata plus a static registry-based model count. The detailed model list, including LiteLLM private models, is still resolved on demand through getLocalProviderModels() when the UI opens a provider detail view, which matches the existing lazy behavior in settings-view.tsx (line 262).
2026-03-30 16:19:07 -07:00
Bee 34eeefbc50 fix: Centralize RPC sidecar ensure logic in core (#49)
* fix: use package versions for build identification

Replace the file modification time (mtime) based approach for build identification with explicit package versions.

- Export `CORE_BUILD_VERSION` in `@clinebot/core`.
- Update CLI RPC logic to use `CORE_BUILD_VERSION` and `RPC_BUILD_VERSION` for `buildId` generation.
- Remove `getEntrypointMtimeMs` and `statSync` as they are no longer required.

This ensures more deterministic and reliable build IDs compared to relying on filesystem timestamps.

* fix: format and test

* address feedback
2026-03-30 16:15:20 -07:00
John Choi f394bf0e2c feat: add fetchFeaturebaseToken SDK method for Featurebase JWT auth (#36)
* fix: refresh featurebase token PR on top of main

* test: cover featurebase token runtime dispatch

---------

Co-authored-by: John Choi <john.choi@cline.bot>
2026-03-30 22:35:29 +00:00
Bee 52f718a34d fix: slack connector (#48)
* fix: slack connector

* remove dead branch
2026-03-30 15:10:28 -07:00
John Choi 03211f1364 feat(sdk): add fetchFeaturebaseToken method [ENG-1673] (#10005)
* feat(sdk): add fetchFeaturebaseToken method

- Add fetchFeaturebaseToken() to ClineAccountService
- Add FeaturebaseTokenResponse type and endpoint constant
- Add E2E mock server handler and unit tests

Ref: ENG-1673

* fix: address review feedback - narrow test glob and add JSDoc

- Fix P2: scope mocharc glob to **/*.test.ts to avoid matching non-test files
- Fix P2: add JSDoc comment to fetchFeaturebaseToken for consistency

---------

Co-authored-by: John Choi <john.choi@cline.bot>
Co-authored-by: John Choi <johnwschoi@users.noreply.github.com>
2026-03-30 14:48:53 -07:00
Robin Newhouse 061aba6225 feat: loop detection as built-in AgentConfig policy (#30)
* feat: loop detection as built-in AgentConfig policy

Repeated tool call loop detection in the agent runtime:
- Soft warning at softThreshold (default 3): injects recovery notice
- Hard escalation at hardThreshold (default 5): triggers mistake limit

Off by default in agent core (loopDetection is optional/undefined).
CLI enables it via CLI_DEFAULT_LOOP_DETECTION constant.

Plumbing: loopDetection flows through CoreSessionConfig and
default-session-manager into the Agent constructor. Config passthrough
verified by session-manager-level integration test.

Live tested: soft warning at call 3 successfully steered the model
to change arguments, avoiding the hard escalation.

* refactor(agents): group loop detection under execution config

- add AgentExecutionConfig in packages/agents/src/types.ts
- move loopDetection, maxConsecutiveMistakes, reminderAfterIterations, and reminderText under AgentConfig.execution
- keep loop detection in the agent runtime and existing mistake escalation path
- thread execution config through core and CLI session startup
- keep CLI loop detection defaults at the host layer
- detect repeated identical tool calls across all tool results in a batch
- add agent tests for repeated-call loop detection, including batched calls

* fix: split lint-staged commands and remove duplicate loop detection import

* fix: isolate lint-staged typecheck from staged file args
2026-03-30 18:56:02 +00:00
Bee ec5b8d9ca8 fix: drop hearbeat from cli output (#45) 2026-03-28 22:19:32 -07:00
Bee 4b1b58b13a feat: support provider specific setting configs (#44)
- Streamlined pure helper functions (e.g., `resolveVisibleApiKey`, `createLetter`) for improved readability.
- Simplified logic flow in `addLocalProvider` by using ternary operators and more concise object assignments.
- Cleaned up type definitions and reduced code verbosity to improve maintainability.
2026-03-28 17:43:00 -07:00
Bee 665cd3cc06 chore: remove js file paths & team tool clean ups (#43)
* refactor: shared delegated-agent layer

Extracted a shared delegated-agent layer in delegated-agent.ts that now owns:

-the common connection/runtime config shape
- a mutable config provider
- shared agent config construction
- shared agent creation

* chore: team tools clean up

* chore: remove js paths

* fix: test js path
2026-03-28 15:47:57 -07:00
abeatrix 6387c9b2a4 fix: oAuth token refresh updates future agent spawn 2026-03-28 02:13:12 -07:00
abeatrix cce50f05e4 feat: divided tool hook/auth phase from execution
- add progress streaming and input override
- fix max concurrent running sequentially
- add discord channel connector
- revert history command title changebun
2026-03-28 00:32:10 -07:00
abeatrix 76aedd7fe8 feat: ClineCore 2026-03-27 23:52:05 -07:00
abeatrix 6c71c12481 fix: improve CLI history UI and telemetry ID resolution
- Update session ID resolution in core to prioritize `node-machine-id` before falling back to a locally stored fallback file.
- Refactor history list display logic:
  - Add `formatHistoryTitle` to clean up, normalize, and truncate session titles.
  - Apply truncation to providers and models to ensure consistent layout.
  - Update UI instruction text for better readability.
- Update `SpawnAgentInputSchema` to use `z.looseObject`.
- Revert version change
2026-03-27 21:44:15 -07:00
BarreiroT 5f21ebdb2b update binary 2026-03-27 23:28:06 -03:00
CandiedUniverse 65e9727c65 Add is_remote_workspace metric (#10019)
* Create implementation plan doc

* feat(telemetry): track remote workspace metadata

* test(workspace): assert telemetry emission during setup

* Remove implementation plan doc

* Improvements pre- code review

* Fix as per Greptile feedback
2026-03-27 19:04:07 -07:00
BarreiroT b44f39ceef feat: publish the cli to the internal tap 2026-03-27 22:51:05 -03:00
John Choi ba06ac5fc9 feat(core): add fetchFeaturebaseToken to ClineAccountService
Add fetchFeaturebaseToken() method to ClineAccountService that calls
GET /api/v1/users/me/featurebase-token via the existing request() helper.
Returns FeaturebaseTokenResponse | undefined (swallows errors gracefully).

Wire the new operation through the full RPC stack:
- shared: add fetchFeaturebaseToken to RpcClineAccountActionRequest
- core: add FeaturebaseTokenResponse type, ClineAccountOperations interface,
  executeRpcClineAccountAction dispatcher, and RpcClineAccountService client
- Export FeaturebaseTokenResponse from account/index.ts and core index.ts

Bump all packages to 0.0.23.
2026-03-27 17:41:25 -07:00
abeatrix bb373d2382 chore: llms package structure clean up
Updated package structure so the model catalog and model types are named by responsibility instead of generic folders:

packages/llms/src/models/providers -> packages/llms/src/models/catalog/providers
packages/llms/src/models/schemas -> packages/llms/src/models/types
I also moved provider settings out of the generic types folder:

packages/llms/src/providers/types/settings.ts -> packages/llms/src/providers/config/provider-settings.ts
test moved to packages/llms/src/providers/config/provider-settings.test.ts

Also fixed session history format issue
2026-03-27 17:00:57 -07:00
abeatrix 462ddca3a3 chore: remove node entry point for core sdk 2026-03-27 15:45:56 -07:00
abeatrix cd14ac9b6e Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-27 14:44:24 -07:00
abeatrix e352feea41 refactor(llms): Runtime-oriented API
Add a smaller runtime-oriented API around the existing SDK. packages/llms/src/sdk.ts now supports:
- getBuiltInProviderIds()
- getBuiltInProviders()
- registerBuiltinProvider(...)

That gives clients an explicit builtin provider list plus a supported path for “my provider ID + my model list + reuse builtin handler family”. The key enabler is the new routingProviderId in packages/llms/src/providers/types/config.ts: a provider can present itself as acme-openrouter while still inheriting the runtime behavior of openrouter, openai-native, anthropic, etc.

Also exposed a focused runtime entrypoint with packages/llms/src/runtime.ts, exported it from packages/llms/src/index.ts and packages/llms/src/index.browser.ts, and added the ./runtime subpath in packages/llms/package.json. The public types for builtin summaries and builtin-backed registration are in packages/llms/src/types.ts.
2026-03-27 14:38:04 -07:00
Robin Newhouse 884fbfb21e feat(read_file): add chunked reading with start_line/end_line parameters (#9711)
* fix(read_file): add stable line labels in act/plan

* prompt: clarify read_file line labels for replace_in_file

* Update prompt snapshots

* feat(read_file): add chunked reading with start_line/end_line parameters

Add optional start_line and end_line parameters to read_file so models
can read files in chunks instead of loading entire files into context.
Default limit is 1000 lines per read, with a continuation hint guiding
the model to paginate when needed.

Made-with: Cursor

* feat: align read_file line format with SDK while keeping superior chunking

- Change line format from 'L1:' to '1 |' to match SDK format
- Add proper NaN validation for start_line/end_line parameters
- Keep 1000-line chunking with continuation hints (superior to SDK)
- Update tool description and tests to reflect new format
- Add directory usage guardrail back to tool description
- Update replace_in_file prompt to reference new line format

This aligns the PR with the newer SDK design patterns while
preserving the superior chunking behavior that prevents context
overflow issues.

* fix: resolve duplicate step numbering in replace_in_file instructions

Fixes the duplicate step 5 issue when NOTEBOOK_INSTRUCTIONS is
concatenated with BASE_DIFF_INSTRUCTIONS for .ipynb files.
Changes notebook instructions to step 6 to avoid ambiguity.

* test: update unit test snapshots

* test: fix gemini3 tools snapshot

* test: regenerate gemini3 tools snapshot

* fix: preserve line ranges on cached file reads

* test: refresh gemini3 tools snapshot

* test: strengthen chunked read assertions

* fix: normalize inverted read file ranges
2026-03-27 12:24:57 -07:00
abeatrix 4dc4cf056a Cline SDK 0.0.22 2026-03-27 12:22:19 -07:00
abeatrix fac5ad6bbb fix: hook worker process 2026-03-27 12:12:54 -07:00
Bee 018b2a6664 docs: simplify CLI build/publish process (#37)
* docs: simplify CLI build/publish process

- Update README to clarify npm publishing uses Bun
- Remove `--production` flag from build script for consistency
- Simplify release script to use `bun publish` directly
- Move workspace dependencies from `dependencies` to `devDependencies`
- Remove deprecated `pack` script and related prepare/restore steps

* dev: fix version scripts
2026-03-27 12:11:57 -07:00
abeatrix fcbde78c25 Cline SDK 0.0.21 2026-03-27 10:40:23 -07:00
abeatrix 0009103441 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-27 04:02:02 -07:00
Dominic Cooney 0c880c7bb1 fix: Serve H.264/MP4 for VSCode and VP9/WebM for JetBrains (#10012)
VSCode's webview doesn't reliably play VP9/WebM, while JetBrains'
JCEF lacks H.264 decoding. Restore the original H.264/MP4 video and
use HTML5 <source> elements with explicit MIME types so each platform
picks the format it supports:

  <source src=... type="video/mp4" />   <!-- VSCode -->
  <source src=... type="video/webm" />  <!-- JetBrains -->

Also:
- Restore .mp4 Git LFS tracking in .gitattributes
- Update CI LFS verification to check both files
- Add webm to JetBrains MIME type map (BrowserRequestHandler)
2026-03-27 03:35:20 -07:00
abeatrix f88a94fce6 dev: use bun for packaging 2026-03-27 03:31:55 -07:00
abeatrix 4088374d1a dev: use bun for packaging 2026-03-27 03:05:18 -07:00
abeatrix ae2d714129 chore: includes production flag for builds 2026-03-27 02:39:15 -07:00
abeatrix 390cde5954 fix: clean up adapter in test 2026-03-27 02:22:30 -07:00
abeatrix 7e1368f091 test: speed up unit tests and bash tool 2026-03-27 00:25:10 -07:00
abeatrix 85d5a2ea77 chore: clean up and consolidate tsconfig 2026-03-26 23:55:36 -07:00
abeatrix eb2b9e65b9 dev: skip apps build in CI 2026-03-26 23:25:08 -07:00
Saoud Rizwan c5a2a640b2 fix(shared): resolve Windows home dir fallback for .cline (#34) 2026-03-26 23:18:12 -07:00
abeatrix c3e0723ba5 test: reenable tests on Windows 2026-03-26 23:15:01 -07:00
abeatrix 3dcef2b49e fear: agent telemetry event metadata 2026-03-26 22:44:48 -07:00
abeatrix 7433bfd10e fix: remove old OpenAI Compatible as provider 2026-03-26 20:37:07 -07:00
abeatrix 20a1116e97 feat: telemetry events with agent id 2026-03-26 19:22:36 -07:00
Dominic Cooney a915122c6d fix: Transcode kanban demo video from H.264/MP4 to VP9/WebM (#9997)
JetBrains IDEs use JCEF (Chromium Embedded Framework) for webviews,
which often does not include H.264 decoding due to licensing
restrictions. This causes the kanban demo video to fail to play.

Transcode the video from H.264/MP4 to VP9/WebM, which is a royalty-free
codec universally supported in Chromium derivatives. This also reduces
the file size from 3.6MB to ~900KB.

Updated all references:
- Source component import (ClineKanbanLaunchModal.tsx)
- Git LFS tracking (.gitattributes)
- CI workflow LFS checks (publish.yml, publish-nightly.yml)
2026-03-27 07:39:05 +09:00
abeatrix aa1a29806d fix: OpenAI Compatible provider migration 2026-03-26 15:21:00 -07:00
BarreiroT 8f7db4a3c3 Expose LoggerTelemetryAdapter in the node package 2026-03-26 17:53:54 -03:00
abeatrix f308042330 fix: bash tool for Windows 2026-03-26 13:51:27 -07:00
abeatrix b5e6bf689a chore: update test workflow 2026-03-26 13:49:37 -07:00
abeatrix 46ab106cf4 fix: node smoke test 2026-03-26 13:11:39 -07:00
Robin Newhouse 835ed94736 fix: exclude new_task tool from system prompt in yolo/headless mode (#9958) 2026-03-26 12:10:47 -07:00
Robin Newhouse 00ca3d13fa test(oca): add messages api routing and stream helper tests (#9494) 2026-03-26 12:02:13 -07:00
CandiedUniverse 3f3a87aed9 Hooks: Notification hook polish (#9909)
* Create implementation plan doc

* Implement notification hook helper

* Remove generated proto artifacts from branch

* Remove implementation plan doc

* Update src/core/hooks/__tests__/notification-hook.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update src/core/hooks/notification-hook.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Tony Loehr <turingxo@gmail.com>
2026-03-26 10:10:15 -07:00
John Simone 7a0f11837e Add Kanban docs (#9988)
* add kanban docs

* fix rendering error in overview, drop kanban flags from commands

* fix stray link

---------

Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
2026-03-26 08:12:09 -07:00
Max 03d2d01eed chore: bump version to 3.76.0 (#9986) 2026-03-26 07:52:28 -07:00
Dominic Cooney db1b1c45bd chore: update dependencies flagged by npm audit (#9980) 2026-03-26 21:03:35 +09:00
abeatrix 6fca498c9b test: adapter windows 2026-03-26 00:25:03 -07:00
BarreiroT 51ccb7341b Fix versioning and dependencies 2026-03-26 04:06:48 -03:00
abeatrix d1d7a724f6 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-25 23:57:00 -07:00
abeatrix 3c0f853e16 feat: expose sdk declaration maps 2026-03-25 23:56:51 -07:00
BarreiroT e7c8275364 remove unnecessary comment 2026-03-26 03:48:31 -03:00
BarreiroT 0cb49cdfba Fix fetchOrganization 2026-03-26 03:44:23 -03:00
BarreiroT 2926df687e fix: Expose ClineOrganization in node files as well 2026-03-26 03:41:09 -03:00
BarreiroT 164e3f7807 Add ts-expect-error 2026-03-26 03:39:32 -03:00
BarreiroT b77e1ba1fa fetch and export ClineOrganization 2026-03-26 03:37:38 -03:00
abeatrix e20e35ea0b Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-25 23:21:41 -07:00
abeatrix 14d1be6d2d Cline SDK 0.0.16 2026-03-25 23:21:33 -07:00
BarreiroT c500b8aa9f export repote-config specifically 2026-03-26 03:19:57 -03:00
abeatrix d8d5649b81 test: sqlite on windows 2026-03-25 23:01:42 -07:00
Saoud Rizwan a4131e57d8 fix(cli): replace --tui hint with kanban description in migration view
The migration announcement didn't explain what Kanban is. Swap the
"run cline --tui" line for a one-liner describing the product so users
know what they're opting into. The --tui escape hatch is still
discoverable via the Exit menu item.
2026-03-25 22:52:22 -07:00
abeatrix 5a89f73a7d feat: support powershell hooks 2026-03-25 22:28:43 -07:00
abeatrix d9d234b848 fix: path for windows tests
Why the failure was test-only:

Issues:
The failing assertion expected a hardcoded POSIX string: "/tmp/cline-data/teams".
The implementation returned the Windows-normalized filesystem path.
That mismatch is exactly what a cross-platform test should avoid.
2026-03-25 21:37:15 -07:00
Robin Newhouse 300d6db1f5 Merge pull request #32 from cline/fix/connector-host-test-mocking
fix: resolve vitest mocking hoisting issue in connector-host.test.ts
2026-03-25 20:34:02 -07:00
BarreiroT 5bf95230b4 fix lock file 2026-03-26 00:22:12 -03:00
BarreiroT 8b3c34188e chore: export UserRemoteConfigResponse 2026-03-26 00:17:16 -03:00
Robin Newhouse 9b832636e2 fix: resolve vitest mocking hoisting issue in connector-host.test.ts
Fixed ReferenceError caused by accessing mock variables before initialization
due to vitest's hoisting behavior. Restructured mocking to define functions
inline within vi.mock() factory and use vi.mocked() for typed references.

Resolves GitHub CI test failure in connector-host.test.ts.
2026-03-25 19:42:13 -07:00
Saoud Rizwan 683dd9a741 chore(cli): bump version to 2.11.0 2026-03-25 19:24:48 -07:00
Saoud Rizwan 0d437f71b0 fix(cli): remove noisy install message when launching kanban 2026-03-25 19:24:20 -07:00
abeatrix 80c6060db4 fix: model generate script 2026-03-25 19:12:30 -07:00
Saoud Rizwan fc77c0faea chore(cli): bump version to 2.10.0 2026-03-25 18:38:24 -07:00
Saoud Rizwan 12c85c4233 Update tips about kanban 2026-03-25 18:26:06 -07:00
Saoud Rizwan d6b7a1ab41 fix(cli): launch kanban directly with package manager fallback 2026-03-25 18:26:06 -07:00
abeatrix 78053b8fdc feat: connector auth events 2026-03-25 18:06:20 -07:00
abeatrix 3e83142192 fix: smoke test workflow on windows 2026-03-25 17:40:54 -07:00
abeatrix b7f528c08f fix: smoke test 2026-03-25 17:16:45 -07:00
CandiedUniverse 3926e7b404 Stabilize auth e2e onboarding tests and clean up mock server URL parsing (#9974)
* Fix tests that fail when kanban modal is visible

* Fix deprecation warning

* Improve isVisible() as per Greptile suggestion
2026-03-25 16:35:09 -07:00
abeatrix e21b4399f4 fix: test workflow 2026-03-25 16:33:23 -07:00
abeatrix 73bf47a093 fix: formatter 2026-03-25 15:53:08 -07:00
abeatrix 1811a903a5 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-25 15:48:39 -07:00
BarreiroT 92a1f706e5 chore: add fetching remote config 2026-03-25 19:48:32 -03:00
abeatrix ebc6425672 refactor: agent loop 2026-03-25 15:47:21 -07:00
abeatrix 4d42af5da8 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-25 15:30:51 -07:00
abeatrix ae6c01f357 fix: Introduce mistake limit and improve chat failure handling
This commit introduces a `maxConsecutiveMistakes` option for agents to prevent them from getting stuck in failure loops. If an agent fails to make progress (e.g., due to repeated tool call errors or invalid model output) for more than the specified number of turns, it will stop execution with a new `mistake_limit` finish reason.

Additionally, the error handling for chat turns has been improved across the `code` and `desktop` apps. If a turn fails for any reason, the session's message state is now reverted to the last successfully persisted state. This ensures the UI remains consistent and doesn't lose the context of previous successful turns after an error occurs.
2026-03-25 15:30:44 -07:00
Tony Loehr 5ba4314b9a feat: add toggle to disable feature tips in chat (#9973) 2026-03-25 15:28:09 -07:00
BarreiroT 91c89668c8 Cline SDK 0.0.15 2026-03-25 19:26:04 -03:00
BarreiroT 29a418d1ff chore: export remote config stuff 2026-03-25 19:25:07 -03:00
abeatrix b0b985c813 fix: windows test 2026-03-25 15:17:05 -07:00
abeatrix 4c247142c9 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-25 14:55:15 -07:00
abeatrix d256eca3e6 feat: file based store fallbacks 2026-03-25 14:55:09 -07:00
BarreiroT 621d5891d9 chore: export mcp and auth stuff on node builds 2026-03-25 18:27:07 -03:00
abeatrix 578e821d2a Cline SDK 0.0.14 2026-03-25 14:07:34 -07:00
abeatrix 2fa5cb5d1e fix: set node engine and moved better-sqlite3 to optionalDependencies 2026-03-25 13:56:35 -07:00
BarreiroT 9e0e894cb7 Redirect to the authUrl directly 2026-03-25 17:32:26 -03:00
BarreiroT 460cf60b88 Update 2026-03-25 17:18:56 -03:00
Tomás Barreiro 9ce72e6027 Merge pull request #27 from cline/add-remote-config-to-the-sdk
Add the Remote Config Schema and constants to the shared package
2026-03-25 21:18:09 +01:00
BarreiroT a9a338ad39 Add the Remote Config Schema and constants to the shared package 2026-03-25 17:14:38 -03:00
abeatrix 93b4ee35b9 chore: clean scripts 2026-03-25 12:20:23 -07:00
abeatrix 0f503d7224 Cline SDK 0.0.13 2026-03-25 12:02:11 -07:00
abeatrix 4074e4e517 test: tool errors 2026-03-25 11:44:25 -07:00
abeatrix 50c25008a7 fix: remove hard char limit from tools 2026-03-25 11:44:04 -07:00
abeatrix b87182a216 fix: increase tool input limits 2026-03-25 11:29:39 -07:00
abeatrix 8b82ebd1b0 test: fix connector host tests 2026-03-25 11:17:45 -07:00
abeatrix 1daf3273c4 feat: Linear connecter 2026-03-25 11:12:00 -07:00
abeatrix c5c310c224 feat: Chat Command Registry and refactor connectors 2026-03-25 10:35:22 -07:00
John Choi 9ccad7f764 refactor: replace hand-rolled YAML parser in refreshSkills with shared helper (#9956)
* refactor: replace hand-rolled YAML parser in refreshSkills with shared helper

The refreshSkills controller had its own line-by-line YAML parser that
only handled simple key: value pairs. Replace it with the shared
parseYamlFrontmatter helper already used by the skills discovery path.

Same output for name and description fields. The shared parser handles
edge cases (arrays, nested values, quoted colons) more robustly.

* refactor: inline parseYamlFrontmatter, remove redundant wrapper

Remove the parseFrontmatter wrapper since only `data` is used by
the caller. Inline the call directly at the use site.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: John Choi <johnchoi@MacBook-Pro.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:06:15 -07:00
Saoud Rizwan 3562f54dbf fix(cli): enable shell mode for kanban spawn on Windows
npx.cmd requires shell: true on Windows to resolve correctly.
2026-03-25 07:35:53 -07:00
Octopus d427d5d76a feat: upgrade MiniMax default model to M2.7 (#9886)
* feat: upgrade MiniMax default model to M2.7

- Add MiniMax-M2.7 and MiniMax-M2.7-highspeed to model list
- Set MiniMax-M2.7 as default model
- Keep all previous models as alternatives
- Update provider documentation

* fix: correct M2.7 cache pricing and update prompt caching docs

- Updated cacheReadsPrice from 0.015 to 0.03 for both MiniMax-M2.7 and
  MiniMax-M2.7-highspeed to match M2.5 pricing (same cache read rate
  across M2.x models)
- Updated prompt caching tip to explicitly mention highspeed variants

* fix: correct MiniMax model pricing to match official rates

- M2.7-highspeed/M2.5-highspeed/M2.1-lightning: $0.60/$2.40 (not $0.30/$1.20)
- M2.7 cache: reads $0.06/M (not $0.03), writes $0.375/M (not $0.0375)
- All models: cache writes $0.375/M (not $0.0375)

Ref: https://platform.minimax.io/docs/llms.txt

---------

Co-authored-by: PR Bot <pr-bot@minimaxi.com>
2026-03-25 05:17:13 -07:00
Saoud Rizwan 072e2887b0 fix(cli): add --tui flag to TUI e2e tests to bypass Kanban redirect
The Kanban launch-by-default feature (beb54a4) redirects bare `cline`
invocations to Kanban, which blocks all TUI tests that launch without
`--tui`. Adding the flag ensures tests reach the legacy TUI as expected.
2026-03-25 04:36:12 -07:00
Saoud Rizwan 9c71a6f021 feat(webview): add Cline Kanban launch modal and queue announcements (#9963)
* feat(webview): add dedicated Cline Kanban launch modal

* fix(ci): fetch LFS media assets in publish workflows
2026-03-25 04:32:09 -07:00
Saoud Rizwan 7627a382aa feat(cli): launch kanban by default with migration view (#9914)
* feat(cli): refresh welcome banner and kanban launcher

* feat(cli): launch kanban by default with migration view

* feat(cli): add kanban process lifecycle management

Detach the kanban child process on Unix so it gets its own process
group, forward signals to the group for graceful shutdown, and fall
back to SIGKILL after a 10s timeout. Resolves exit codes from signals
correctly (130 for SIGINT, 143 for SIGTERM).
2026-03-25 02:01:56 -07:00
abeatrix ca47322754 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-25 00:56:33 -07:00
abeatrix b2467a40dc feat: slack connector 2026-03-25 00:56:12 -07:00
abeatrix bd419a8692 fix: add back built-in commands 2026-03-24 19:29:19 -07:00
Tomás Barreiro 978c633c90 Add KanBan to remote config [PF-632] (#9959)
* Add KanBan to remote config

* Update tests
2026-03-25 02:33:52 +01:00
abeatrix c1df0d4e64 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-24 18:28:19 -07:00
abeatrix 7f0ba7cecc refactor: extract shared constants and normalize DB column naming to camelCase
- Move DEFAULT_RPC_ADDRESS to @clinebot/shared as CLINE_DEFAULT_RPC_ADDRESS
- Deduplicate repeated `process.env.CLINE_RPC_ADDRESS || DEFAULT_RPC_ADDRESS` pattern
- Convert snake_case DB column references (status_lock, metadata_json,
  hook_path, transcript_path, messages_path, etc.) to camelCase throughout
  session persistence and storage adapter layers
- Simplify metadata handling by removing intermediate JSON serialization
- Add files.insertFinalNewline to VS Code workspace settings
2026-03-24 18:28:14 -07:00
abeatrix 44b572e7b3 feat: shows skills and workflows as commands 2026-03-24 18:21:40 -07:00
Tony Loehr e807b520e0 feat(cli): add rotating feature tips during thinking/acting phases (#9874)
* feat(cli): add rotating feature tips during thinking/acting phases

Port the FeatureTip component from the VSCode extension (PR #9799) to the
CLI. Shows rotating educational tips below the ThinkingIndicator while Cline
is processing, appearing after a 2-second delay and cycling every 8 seconds.

Includes 17 tips covering:
- Core features: .clinerules, Plan/Act mode, checkpoints, MCP servers
- CLI-specific: /settings, /skills, /history, /compact, /reportbug
- Workflows: kanban, auto-approve toggle, history navigation, @ mentions
- Images via --images flag, browser testing, double-check completion

* Update FeatureTip.tsx

* Apply suggestions from code review

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix: resolve FeatureTip.tsx syntax errors (duplicate export, missing tipIndex state)

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-24 17:57:35 -07:00
abeatrix 022341a104 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-24 16:02:45 -07:00
abeatrix 122b98a414 fix: harden flaky loader test with focused case 2026-03-24 16:02:44 -07:00
abeatrix 394456b0b6 doc: message queue 2026-03-24 15:55:18 -07:00
abeatrix ef653024ce fix: plugin loader test 2026-03-24 15:43:22 -07:00
abeatrix 12a3004ce1 Cline SDK 0.0.12 2026-03-24 15:20:55 -07:00
Max bffb0512c3 Merge pull request #25 from cline/max/tui-test-gh-action
add TUI tests to test gh action
2026-03-24 15:09:38 -07:00
abeatrix 38e621d626 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-24 15:05:08 -07:00
abeatrix c6f76fbf14 feat: list and load plugins 2026-03-24 15:05:06 -07:00
Max Paulus 🥪 9592af2e46 add TUI tests to test gh action
- now that tui tests are all passing, want to add it to PR checks
2026-03-24 15:04:03 -07:00
Max Paulus 🥪 ea29ed93f7 skip remaining tests for now 2026-03-24 14:58:41 -07:00
Max Paulus 🥪 95cc8d84ba fix: add providers.json to claude-sonnet-4.6 test config
The test 'starts interactive mode with custom config directory' expected
anthropic/claude-sonnet-4.6 in the status bar but got the user's real
model because the test config had no providers.json, causing the CLI to
fall back to the real ~/.cline config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 14:45:14 -07:00
Max Paulus 🥪 3d9f10cb39 fix: handle slash completion in /settings test
Type /settings, wait for completion menu, then submit twice —
first Enter selects the completion item, second Enter submits the command.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 14:44:32 -07:00
Max Paulus 🥪 6b7532363f fix: correct Shift+Tab auto-approve toggle test initial state
The test expected 'Auto-approve all disabled' as the initial state,
but the default test config has autoApprovalSettings.enabled: true.
Flip the assertion order to match the actual default.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 14:41:15 -07:00
Max Paulus 🥪 53aa338a3e fix/unskip tests 2026-03-24 14:35:03 -07:00
Max Paulus 🥪 9b299038e4 fix: modernize test config to use providers.json instead of legacy secrets.json
The default test config used legacy secrets.json with expiresAt in seconds,
but isCredentialLikelyExpired() compares against Date.now() (milliseconds),
causing the OAuth token to always appear expired. Replace with a modern
providers.json (expiresAt in ms) which bypasses legacy migration entirely.

Also update env.ts recording helper to set CLINE_PROVIDER_SETTINGS_PATH
(pointing to real ~/.cline/data/settings/providers.json) instead of the
defunct CLINE_SECRETS_FILE env var.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 14:35:03 -07:00
Max Paulus 🥪 f4199b41a6 fix some more broken tui tests 2026-03-24 14:35:03 -07:00
abeatrix ca48885bf5 feat: queue and steer prompts in CLI & code app 2026-03-24 13:41:07 -07:00
John Choi f3a3f30db5 feat: add repeated tool call loop detection (#9933)
* feat: add repeated tool call loop detection

Detect when the LLM calls the same tool with identical arguments
repeatedly, which wastes tokens without making progress. This is
the #1 active complaint in the Cline issue tracker (13+ open issues
including #9923, #9916, #9846, #9816).

Two-stage escalation:
- Stage 1 (3 identical calls): inject a warning nudging the LLM
  to try a different approach
- Stage 2 (5 identical calls): trigger the existing
  consecutiveMistakeCount escalation (asks user or fails in YOLO)

Detection uses JSON.stringify with a sorted key replacer for
deterministic comparison. Metadata params like task_progress
(which change every call even when actual tool arguments are
identical) are stripped from the comparison.

Complementary to fileReadCache, which deduplicates file content but
still allows the tool call to succeed and consume a turn. Loop
detection catches the repeated call pattern itself.

Changes:
- loop-detection.ts: shared helper (toolCallSignature, checkRepeatedToolCall)
- TaskState.ts: add lastToolParams, consecutiveIdenticalToolCount
- ToolExecutor.ts: call checkRepeatedToolCall in handleCompleteBlock
- responses.ts: add formatResponse.repeatedToolCall
- loop-detection.test.ts: 5 tests

Manually verified: CLI test confirms soft warning at call 3,
YOLO mode failure at call 5.

Refs: #9923, #9916, #9846, #9816

* fix: address review feedback on loop detection

- Change hardEscalation threshold from >= to === so it fires exactly
  once at count 5, matching softWarning behavior
- Widen toolCallSignature param type to Partial<Record<string, string>>
  to match actual block.params type from ToolExecutor
- Add negative boundary assertions to verify no false positives at
  calls 0, 1, 3, 4

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: reset loop detection state when user continues after mistake_limit_reached

When hard escalation fired at count === 5 and the user clicked
"continue", consecutiveIdenticalToolCount was never reset. The count
would exceed 5 but === 5 never matched again, silently disabling
loop detection for the rest of the task.

Reset consecutiveIdenticalToolCount, lastToolName, and lastToolParams
alongside the existing consecutiveMistakeCount reset so the detector
fully re-arms after the user continues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: John Choi <johnchoi@MacBook-Pro.local>
2026-03-24 13:40:31 -07:00
abeatrix 4120afc7bb feat: prompt queue 2026-03-24 12:46:22 -07:00
abeatrix 6ee6e51510 chore: clean up 2026-03-24 10:45:39 -07:00
abeatrix ee93b13e27 fix: flaky hook tests 2026-03-24 09:16:00 -07:00
abeatrix 83c6c5ebbe feat: includes skills in tool definitions 2026-03-24 09:00:45 -07:00
abeatrix 30415e3a89 fix: clarify tool description and truncation logic 2026-03-24 08:40:01 -07:00
abeatrix 14c71568eb fix: disable ask question tool in yolo mode 2026-03-24 07:09:47 -07:00
abeatrix 77489ff131 fix: cost calculation 2026-03-24 07:00:24 -07:00
abeatrix f192e9135e refactor: rpc version and message truncation 2026-03-24 04:19:35 -07:00
abeatrix 4ed7f24160 refactor: connector tool output 2026-03-24 03:04:45 -07:00
abeatrix 4b5278ecdb fix: connector args 2026-03-24 03:01:03 -07:00
abeatrix 879ecc8aac fix: improve team tool schema and error parsing 2026-03-24 02:24:37 -07:00
abeatrix 9137fab90b fix: gemini tools schema format 2026-03-23 22:51:09 -07:00
abeatrix 91fbdfdaef feat: update generated model list 2026-03-23 21:53:16 -07:00
abeatrix d4a3dceb5a Cline SDK 0.0.11 2026-03-23 21:45:51 -07:00
abeatrix 991357589d fix: test formatting 2026-03-23 21:36:18 -07:00
abeatrix 405e2cc8ad fix: includes internal metadata per message turn 2026-03-23 21:26:56 -07:00
abeatrix 175bc5f886 fix: prompt caching 2026-03-23 21:14:50 -07:00
abeatrix 0cb8f07fdc Cline SDK 0.0.10 2026-03-23 20:11:04 -07:00
abeatrix 05e84c3a36 doc: update example packages 2026-03-23 20:00:05 -07:00
abeatrix 3e7415e30e build(cli): change build target and shebang to Node.js
Update the Bun build configuration to target Node.js instead of Bun and update the executable shebang to use the Node.js runtime. This allows the CLI to be executed in environments where Bun is not installed.
2026-03-23 19:27:58 -07:00
abeatrix 5f9aff2264 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-23 18:59:05 -07:00
abeatrix 662d26bc7c fix(cli): allow headless execution without persisted API key
Removes the "fail fast" termination in headless modes (JSON output, YOLO mode, or non-TTY stdin) when no API key is found. This allows authentication to be resolved at runtime—for example, via environment variables—instead of requiring a persisted key or triggering a browser-based OAuth flow.

The browser-based OAuth flow is now explicitly skipped in headless scenarios to prevent unexpected interactive prompts in automated environments.

Added integration tests to verify that the CLI correctly proceeds to agent execution in headless modes even when a local API key is not present.
2026-03-23 18:59:00 -07:00
abeatrix d8bee3c5f6 fix: hook file settled 2026-03-23 17:42:23 -07:00
abeatrix 045370ff86 feat: restart server on protocol version mismatch
Summary

-Adds rpc_version field to the gRPC HealthResponse message, populated from @clinebot/rpc's package.json version
- ensureCompatibleRpcAddress now checks the running server's reported version against the local RPC_PROTOCOL_VERSION — if they differ (or the server predates this field), it triggers the same graceful-shutdown-and-restart flow used for servers missing runtime methods
- Ensures users who upgrade the core package get a fresh RPC server without manual intervention

Changes

packages/rpc/src/proto/rpc.proto — added string rpc_version = 5 to HealthResponse
packages/rpc/src/version.ts — new file, exports RPC_PROTOCOL_VERSION from package.json
packages/rpc/src/server/runtime.ts — populates rpcVersion in health() response
packages/rpc/src/index.ts — re-exports RPC_PROTOCOL_VERSION
apps/cli/src/commands/rpc.ts — version check in ensureCompatibleRpcAddress
apps/cli/src/commands/rpc.test.ts — 3 new tests: version match (reuse), version mismatch (restart), missing version/old server (restart)
2026-03-23 17:11:23 -07:00
abeatrix 05278d506f Merge branches 'main' and 'main' of https://github.com/cline/sdk-wip 2026-03-23 16:49:23 -07:00
abeatrix c1086d5321 fix: ensure fresh provider abort controllers and trace abort sources across agent runs
This changeset fixes provider request cancellation isolation and adds enough abort-reason propagation to identify what actually triggered a cancellation.

Each provider request now gets a fresh AbortController in base.ts, which prevents stale abort signals from older requests from cancelling newer loops on the same handler instance. The provider config types were also updated to include an optional logger, matching existing handler usage.

On the agent side, abort reasons are now preserved and logged instead of being dropped during signal merging. agent.ts now logs abort activity for agent_run, agent_config, and api_timeout sources, and forwards provider-layer abort logs through the agent logger. Common cancellation entry points were updated to pass explicit reasons, including session-manager aborts, streaming aborts, RPC/runtime aborts, interactive CLI aborts, run-agent aborts, and team-wide aborts.

Tests were added/updated in base.test.ts to verify stale-signal isolation and fresh-controller-per-request behavior.
2026-03-23 16:46:53 -07:00
Max Paulus 🥪 1ae6ec9bd3 use bun instead of node 2026-03-23 16:29:16 -07:00
Ara 2f67343b6f Merge pull request #23 from cline/ara2/cli-process-exit-stdin-fixes
fix(cli): process exit cleanup and headless stdin hang
2026-03-23 16:04:27 -07:00
Arafatkatze e4dd181b90 fix(cli): avoid task parse fallthrough into runtime 2026-03-23 15:44:35 -07:00
abeatrix 1cde9a9fb3 feat: file read tool to includes optional line range 2026-03-23 15:38:05 -07:00
abeatrix 318e0d53be fix: do not reuse controller for new request
getAbortSignal() always creates a new AbortController per provider request instead of reusing the previous one. That keeps stale request signals from cancelling newer requests. I also clear the current controller reference when that request is aborted.
2026-03-23 15:13:09 -07:00
Robin Newhouse 276a3ce31a fix(cli): prevent hang when spawned without TTY in headless/CI environments
Use fstatSync(0) to check whether stdin is actually a pipe (FIFO) or
file before attempting to read from it. Previously the guard only checked
`!process.stdin.isTTY`, which is false in non-TTY environments even when
nothing is piped, causing the for-await loop on stdin to block forever.

Ports the fix from cline/cline PRs #9073 and #9121.

Made-with: Cursor
2026-03-23 14:44:26 -07:00
Robin Newhouse d63ea54e0c fix: CLI process exit and sonic-boom shutdown
Replace all process.exit() calls in main.ts with process.exitCode + return
so every exit path flows through the index.ts finally block for cleanup.
After cleanup, always call process.exit() to prevent lingering handles
(worker threads, TLS sockets) from keeping the process alive.

Make all pino file destinations synchronous to eliminate the sonic-boom
"not ready yet" error when process.exit() fires before async fd open.

Add e2e regression tests with 10s timeout to catch future exit hangs,
and unit tests for logger shutdown safety and aborted teardown handling.

Made-with: Cursor
2026-03-23 14:44:26 -07:00
Ara 243ad7c07e Merge pull request #12 from cline/arafatkatze/hill-climb-fixes
Hill climb fixes
2026-03-23 14:42:18 -07:00
Arafatkatze 5fb1efac46 fix(cli): avoid blocking cline startup on welcome fetch 2026-03-23 14:39:11 -07:00
Arafatkatze 9d6d6f63cc fix(llms): scope OpenAI cache marker to final user turn 2026-03-23 14:31:36 -07:00
Arafatkatze 1a1ad142fc Agent hill climb fixes 2026-03-23 14:03:32 -07:00
abeatrix 9ab70908de fix: Vercel AI Gateway base URL 2026-03-23 10:50:55 -07:00
abeatrix f25bc749ac fix: improve tool schema 2026-03-23 10:49:29 -07:00
Max Paulus 🥪 7f37d6e0f6 fix flag strings in tests 2026-03-23 10:27:24 -07:00
Max Paulus 🥪 bdccec2328 Add --verbose and --config flags to the update subcommand
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:25:55 -07:00
Max Paulus 🥪 91e1fdbb4a Ensure data directory exists before opening SQLite database
Add mkdirSync(dirname(filePath), { recursive: true }) in loadSqliteDb()
so that missing directories (e.g. ~/.cline/data) are created automatically
instead of crashing with "SQLiteError: disk I/O error".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:03:33 -07:00
Max Paulus 🥪 29fbc3393b Fix --json mode to only output JSONL — suppress plain text in writeln()
writeln() previously only suppressed empty strings in JSON mode, allowing
plain-text warnings (model catalog, provider settings) to leak into stdout
and break JSONL parsing. Now suppresses all writeln() output in JSON mode
so only emitJsonLine() writes to stdout. Updated test expectation to match
the JSON-formatted error output from writeErr().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:01:20 -07:00
Max Paulus 🥪 0ac110cceb Fail fast with "Not authenticated" in headless mode when no credentials
In headless mode (yolo / json / piped stdin), if no API key or OAuth
token is available, print "Not authenticated" to stderr and exit 1
instead of attempting a browser-based OAuth flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:50:46 -07:00
Max Paulus 🥪 4f118dad45 Suppress headless mode chrome, only show LLM output by default
Headless runs (cline -y, cline "prompt", piped stdin) now print only
the LLM response text. Model info, welcome line, and summary are
gated behind --verbose. No global state added — each call site checks
config.verbose directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 09:22:45 -07:00
Max Paulus 🥪 0c9fad623c Fix "sonic boom is not ready yet" error on CLI exit
Wrap SonicBoom destination's flushSync with try-catch so its
internally-registered process exit handler never throws when
the async stream hasn't finished initializing (e.g. --help,
--version, and other quick-exit commands).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 08:55:42 -07:00
Max Paulus 🥪 cefda2bd37 fix some test strings 2026-03-23 08:48:14 -07:00
Max Paulus 🥪 c60553374e Centralize cline config dir resolution with --config flag and CLINE_DIR env var
Add setClineDir/resolveClineDir to paths.ts with precedence:
--config flag -> CLINE_DIR env var -> ~/.cline default.
Remove per-subcommand CLINE_DATA_DIR workarounds in auth/history.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 08:47:02 -07:00
Max Paulus 🥪 ccd1e8e43e Update auth partial-flags tests to expect error+exit instead of interactive screen
Tests for --apikey/--modelid/--baseurl without --provider now expect
an error message and exit code 1, matching the current implementation.
Tests for --verbose/--cwd/--config remain as interactive auth screen tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:02:27 -07:00
Max Paulus 🥪 2cba8eb6cd Add --config flag to history and auth subcommands
- history: add --config <dir> option that sets CLINE_DATA_DIR so history
  loads from the specified config directory
- auth: add --config, --cwd, and --verbose options; --config sets
  CLINE_DATA_DIR before creating the provider settings manager
- Update auth option descriptions (Provider ID, Model ID) for consistency
- Fix flags.test.ts history description to match actual --limit text

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:30:45 -07:00
Max Paulus 🥪 5ff11f2f86 Add --apiKey as hidden alias for --key flag
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:09:29 -07:00
Max Paulus 🥪 4ddb43c56b Fix cline config --help showing root help instead of config help
Remove prefix-stripping logic that converted `config --help` into
`--help` (which showed root help). Let Commander route the config
subcommand naturally. Add `--config <dir>` option and remove
allowUnknownOption/allowExcessArguments so the config subcommand
produces its own help page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:05:25 -07:00
Max Paulus 🥪 979ef6b641 Make cline task show all root-level flags in --help
Extract option definitions into shared `addRootOptions()` function and
apply it to both the root program and the task subcommand so that
`cline task --help` displays all flags (--act, --plan, --yolo, etc.)
instead of only -h/--help.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:01:08 -07:00
Igor Tceglevskii f1c7934064 fix: prevent OOM crash from globby's eager .gitignore scanning in lis… (#9917)
* fix: prevent OOM crash from globby's eager .gitignore scanning in listFiles

Replace globby's gitignore:true (which reads ALL .gitignore files in the
entire tree upfront, including inside gitignored directories) with
incremental .gitignore reading during BFS traversal.

In projects with large gitignored vendored dependencies containing many
nested repos, globby collects thousands of patterns, builds a massive
regex, and V8 runs out of memory during regex compilation (~488MB).

The fix reads .gitignore files only from directories the BFS actually
enters. Gitignored directories are never entered, so their .gitignore
files are never parsed and the pattern count stays small.

- Set gitignore:false, handle .gitignore ourselves
- Read root .gitignore in buildIgnorePatterns() to seed initial patterns
- Read subdirectory .gitignore lazily during globbyLevelByLevel BFS
- Accumulate patterns in currentIgnore so deeper levels respect them
- Add 4 tests: root patterns, file patterns, subdirectory .gitignore,
  and OOM-prevention (no reading inside gitignored dirs)

* Code review followup

* Potential fix for code scanning alert no. 147: Incomplete string escaping or encoding

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2026-03-22 15:20:57 -07:00
BarreiroT 121aa9385d Add browser exports for the shared package 2026-03-21 22:25:10 -03:00
BarreiroT d9dae00ed4 SDK 0.0.9 2026-03-21 22:09:33 -03:00
BarreiroT 75449c5ea9 Add src to the dist 2026-03-21 22:09:18 -03:00
BarreiroT 12bcb491d5 Remove internal description 2026-03-21 21:59:59 -03:00
BarreiroT 01ca426860 SDK 0.0.8 2026-03-21 21:12:12 -03:00
Tomás Barreiro 4cfbeb0a17 Merge pull request #14 from cline/publish-shared-package
Publish @clinebot/shared
2026-03-22 01:11:45 +01:00
BarreiroT 7b01d45171 Publish @clinebot/shared 2026-03-21 20:47:59 -03:00
Robin Newhouse 1d42da5248 Add tool-specific error messages for replace_in_file and execute_command (#9710)
Replace generic "missing parameter" errors with targeted guidance for
the two tools observed failing most in SWE-bench (6% of failures).
The new messages include the expected format (SEARCH/REPLACE blocks,
XML example) without the 30-line boilerplate reminder.

Made-with: Cursor
2026-03-21 11:57:00 -07:00
abeatrix cd5f0a5cd0 refactor: session manager 2026-03-20 20:45:11 -07:00
abeatrix 2674a1d7e5 fix: code app model selector render 2026-03-20 20:12:38 -07:00
abeatrix bebc59ccb0 feat: add telemetry events for core flows
Integrate telemetry tracking into OAuth and authentication processes. This update introduces specific event captures for authentication lifecycle stages, including when auth starts, succeeds, fails, or when a user logs out.

Key changes:
- Added `captureAuthStarted`, `captureAuthSucceeded`, `captureAuthFailed`, and `captureAuthLoggedOut` helper functions.
- Integrated `ITelemetryService` into `ClineOAuthProviderOptions` and related auth functions.
- Updated `RuntimeOAuthTokenManager` to support telemetry propagation.
- Added user identification (`identifyAccount`) upon successful login to track account-specific metrics.
- Applied these changes across Cline, Codex, and OCA auth providers.
2026-03-20 19:56:13 -07:00
abeatrix 42cb009af3 fix: handle pre-finalized sessions and add debug logging
- In `run-agent.ts`, ensure that if a session is already finalized during the start phase (e.g., local non-interactive runs), the result from `start()` is used instead of calling `send()`. This prevents "session not found" errors when the session manager has already cleaned up the session.
- Added a `moduleLogger` to `rpc-runtime.ts` and `sessionLogger` to `session.ts` to provide better visibility into session lifecycle and RPC calls.
- Improved debug logging for session lookups and RPC runtime handler calls to assist in troubleshooting session management issues.
2026-03-20 19:47:41 -07:00
abeatrix 8bf104d7a1 fix: Code App initialize with last selected provider 2026-03-20 18:53:08 -07:00
abeatrix e1ef1853ca refactor: rename models and providers exports 2026-03-20 18:37:50 -07:00
abeatrix a0380e93de feat: CLI build script with env vars 2026-03-20 18:06:36 -07:00
Max da27979921 max tui tests and use commander.js (#9)
* add vcr.ts

* add tui-tests

* feat: replace manual parseArgs() with Commander.js

Install commander and create src/commands/program.ts with the root
command definition and all global flags. parseArgs() in helpers.ts
now delegates to Commander internally while preserving the existing
ParsedArgs interface and return type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: convert auth command arg parsing to Commander.js

Replace manual parseAuthCommandArgs() loop with a Commander-based
subcommand. The new createAuthCommand() exports a Command that can
be registered on the root program. In the auth context -p means
--provider and -m means --modelid, scoped by Commander per-command.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix some tui tests

* feat: convert command routing in main.ts to Commander.js subcommands

Replace manual rawArgs[0] string matching with Commander .command()
subcommands for all CLI commands (hook, dev, version, update, rpc,
auth, schedule, history, list, config). RPC subcommands are nested
under a 'rpc' command group. The 'h' alias is now handled by
Commander's .alias() instead of the normalizer. A shared ctx object
communicates exit codes and fall-through state from subcommand
actions back to the main flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: replace manual help rendering with Commander.js auto-generated help

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* alphebatize cli subcommands

* feat: convert history command arg parsing to Commander.js

Replace manual rawArgs parsing in history command with Commander.js
subcommands and options. The history command now defines proper
subcommands (delete, update) with typed options (--session-id,
--prompt, --title, --metadata, --limit, --page) instead of manually
indexing into rawArgs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: convert connect command arg parsing to Commander.js

Replace manual rawArgs parsing in runConnectCommand with a Commander
subcommand. The connect command now uses .argument(), .option(--stop),
.allowUnknownOption(), and .passThroughOptions() so connector-specific
flags pass through untouched. Dynamic adapter listing is rendered via
.addHelpText().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: convert schedule command arg parsing to Commander.js

Replace manual arg parsing (resolveRpcAddress, hasFlag, getFlagValue,
parseList, parseJsonObjectFlag) with Commander.js subcommands and typed
options. Export createScheduleCommand() that returns a Command instance
registered on the root program via addCommand(). Update tests to use
the new Commander-based API. Alphabetize subcommands and flags.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: enable positional options on root program for connect passthrough

Commander requires enablePositionalOptions() on the parent command
when a subcommand uses passThroughOptions().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: convert dev command arg parsing to Commander.js

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: convert list command arg parsing to Commander.js

Replace manual rawArgs[1] routing in runListCommand with a Commander
command tree. Each list target (workflows, rules, skills, agents,
hooks, mcp) is now a proper subcommand with its own action handler.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: convert rpc command arg parsing to Commander.js

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix some more tests

* feat: eliminate rawArgs usage in main.ts — use commander-parsed values

Replace all raw process.argv manipulation in main.ts with commander's
parsed output:

- Remove normalizeTopLevelArgs: inline prefix detection for task/t/config
- Remove parseArgs call: use commanderToParsedArgs(program) after the
  single parseAsync() as the source of truth for the default flow
- Auth command: define -p/--provider, -k/--apikey, -m/--modelid,
  -b/--baseurl directly on the auth subcommand instead of delegating
  to parseAuthCommandArgs(rawArgs.slice(1))
- Config command: set launchConfigView flag in action instead of
  checking rawArgs[0]
- Connect command: pass connectCmd.args.slice(1) (passthrough args
  after adapter name) instead of full rawArgs
- History/List commands: read outputMode from program.opts().json
  directly instead of from the removed args variable
- Keep resolveConfigDirArg as a documented two-pass helper since
  setHomeDir() must run before commander parses
- Update ConnectCommandDefinition.run interface and all three adapter
  parse functions to receive pre-sliced passthrough args

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: register 'task' as a proper commander subcommand with 't' alias

Replace the prefix-stripping normalization hack for 'task'/'t' with a
proper commander subcommand registration. The task command uses
passThroughOptions to capture all args, then re-parses them through a
fresh root program so global options (--model, --timeout, etc.) work.

- 'task|t' now appears in --help output
- clite task <prompt> and clite t <prompt> behave identically to clite <prompt>
- Global flags work after task: clite task --model foo fix bug

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix post-merge problems

* feat: convert doctor command arg parsing to Commander.js

Replace manual rawArgs parsing in doctor.ts with Commander.js options.
Export createDoctorCommand() following the same pattern as rpc command.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: pass options object instead of string array to runRpcEnsureCommand in test

The test was passing a string array but the function now expects
{ address, json } options object.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: mock @clinebot/agents in hooks.test.ts to fix createPersistentSubprocessHooks error

The test called createRuntimeHooks which internally imports
createPersistentSubprocessHooks from @clinebot/agents. Without a mock,
the test fails with 'createPersistentSubprocessHooks is not a function'.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix typing issue with resolveHookSessionContext

* fix: resolve 8 failing e2e tests caused by Commander.js migration

- Update help output assertion to match Commander's subcommand format
- Add unknown target handler to list command with proper error message
- Add --json option to list and history commands for positional option propagation
- Change history delete/update from requiredOption to manual validation for custom errors
- Handle non-zero CommanderError exit codes for --taskId missing value

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: update history mock exports in main.test.ts to match renamed imports

The test mocked `runHistoryCommand` but main.ts now imports `runHistoryList`,
`runHistoryDelete`, and `runHistoryUpdate` via dynamic imports.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* update test

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: abeatrix <beatrix@cline.bot>
2026-03-20 17:34:55 -07:00
abeatrix 983ba88c41 Cline SDK 0.0.7 2026-03-20 17:19:32 -07:00
abeatrix 770ad680ab Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-20 16:49:35 -07:00
abeatrix 14ce002916 fix: skip RPC server auto-start when an explicit RPC address is running 2026-03-20 16:49:33 -07:00
Tomás Barreiro 482e8d58b1 Fix the legacy cline auth info migration (#10)
* Fix the legacy cline auth info migration

* Remove clineAccountId

* Add tests
2026-03-20 16:37:48 -07:00
abeatrix 0008b43cf7 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-20 16:34:33 -07:00
abeatrix 024872f039 fix: tool call input stream parsing 2026-03-20 16:33:08 -07:00
Ara 7f3974a827 fix: read cache_write_tokens from OpenRouter API instead of hardcoding 0 (#9871)
* fix: read cache_write_tokens from OpenRouter API instead of hardcoding 0

- Read prompt_tokens_details.cache_write_tokens from OpenRouter stream usage
  chunks instead of hardcoding cacheWriteTokens to 0
- Read native_tokens_cache_write from generation endpoint fallback
- Replace fragile hardcoded model ID switch statement for cache_control blocks
  with prefix-based matching (anthropic/, minimax/) so new models automatically
  get prompt caching enabled
- Add unit test verifying cache_write_tokens are correctly extracted

Co-authored-by: Ara <arafat.da.khan@gmail.com>

* fix: read cache_write_tokens from OpenRouter API instead of hardcoding 0

- Read prompt_tokens_details.cache_write_tokens from OpenRouter stream usage
  chunks instead of hardcoding cacheWriteTokens to 0
- Read native_tokens_cache_write from generation endpoint fallback
- Replace fragile hardcoded model ID switch statement for cache_control blocks
  with prefix-based matching (anthropic/, minimax/) so new models automatically
  get prompt caching enabled
- Add unit test verifying cache_write_tokens are correctly extracted

Co-authored-by: Ara <arafat.da.khan@gmail.com>

* Release v3.74.0 Notes

* Release v3.74.0 Notes

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: alex-lum <alex@cline.bot>
2026-03-20 15:46:29 -07:00
CandiedUniverse d40ab56aff Changelog and version bump for release (#9910) 2026-03-20 15:29:45 -07:00
abeatrix 1bdb43fe0c test: fix flaky tests 2026-03-20 13:49:27 -07:00
abeatrix c7522ab64e feat: store failed tool calls 2026-03-20 13:47:34 -07:00
abeatrix 8fad00f838 test: add tests for rpc and hooks and fix e2e timeout 2026-03-20 13:13:09 -07:00
abeatrix 2ce15f3866 fix(cli): add lease mechanism for detached RPC server spawning
Introduces a file-based locking mechanism (`tryAcquireRpcSpawnLease`) to
prevent race conditions when multiple processes attempt to spawn a
detached RPC server on the same address simultaneously.

The lease ensures that only one spawn operation proceeds at a time. It
automatically expires after a timeout (10 seconds) or when manually
released, and includes checks to handle stale leases from crashed
processes.

- Added `rpc-spawn-lease.ts` in `@clinebot/core`.
- Integrated lease acquisition in `spawnRpcStartDetached`.
- Added unit tests for the lease logic.
2026-03-20 12:23:11 -07:00
abeatrix bb8ffcc21e refactor: doctor command display text 2026-03-20 11:54:08 -07:00
abeatrix fb0fff5890 feat: model list updated 1774031158558 2026-03-20 11:27:43 -07:00
abeatrix 2c0e9fd8bb Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-20 11:25:35 -07:00
abeatrix 2f9f006a20 test: agent turns 2026-03-20 11:23:13 -07:00
abeatrix 90d8b6573d fix: build error 2026-03-20 11:23:01 -07:00
abeatrix a70a8097df Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-20 11:15:51 -07:00
abeatrix ba1cbc7ab8 feat: add support for message notices and display roles
- Implement handling for `notice` event types in the CLI and TUI.
- Introduce `displayRole` and `messageKind` metadata to support specialized message rendering (e.g., recovery notices, system status, errors).
- Update message hydration logic in both TypeScript and Rust cores to preserve and merge notice-related metadata.
- Ensure recovery notices are filtered out when deriving prompts from message history to avoid polluting model context.
2026-03-20 11:15:24 -07:00
CandiedUniverse bbdf445db7 fix: prevent resume asks from unblocking on abort (#9908)
* fix: prevent resume asks from unblocking on abort

* Add test case for resume_completed_task

* Prevent flaky tests
2026-03-20 10:51:16 -07:00
abeatrix f45305b338 feat: doctor includes connector status 2026-03-20 10:35:45 -07:00
abeatrix 6fe2e62d3f feat: add onStopError hook for non-recoverable API errors
Introduces a new `onStopError` hook and `agent_error` event to the agent lifecycle. This allows the system to explicitly track and respond when an agent execution terminates due to a non-recoverable API error (e.g., rate limits or error related to auth).

- Added `agent_error` to session hook types in both code and desktop apps
- Implemented `onStopError` dispatching in the core `Agent` class
- Updated subprocess hook handlers to propagate `agent_error` events
- Added unit tests to verify hook emission during non-recoverable errors
2026-03-20 10:35:24 -07:00
abeatrix 47e571f56f fix: codex tools
Codex now ignores provider-native tool-call stream events instead of converting them into local executable tool_calls, which removes the Unknown tool failure path. The change is in community-sdk.ts.

I also fixed the Codex model catalog so it no longer advertises custom tools capability and actually uses the Codex-adjusted model map in openai-codex.ts. That keeps the provider metadata aligned with the real behavior from the AI SDK docs.

Verification: bun test packages/llms/src/providers/handlers/codex.test.ts packages/agents/src/tools/tools.test.ts passed.
2026-03-20 10:04:38 -07:00
CandiedUniverse d992a3bf21 Stabilize hooks fixture tests with per-scenario isolation (fix flaking tests) (#9868)
* Refactor flaking hooks tests

Fix flaking hooks tests

Strengthen hooks fixture test isolation

* Address Greptile hooks test review

* Harden Windows hook timing tests

* Loosen hook cancellation timing margins

* Fixing more flaking tests
2026-03-20 09:50:14 -07:00
abeatrix 04d54f5c37 feat: team_list_tasks 2026-03-20 09:47:43 -07:00
abeatrix 28c7cc56bd feat: clean up workspace indexes 2026-03-20 09:46:36 -07:00
Max ace95988f8 fix: resolve deadlock when clicking New Task/Exit after task completion (#9905)
The presentation scheduler introduced in ff05ec3bb awaits in-flight
flushes during dispose(), but ask() blocks indefinitely on pWaitFor
waiting for user input. When abortTask() sets abort=true and then
awaits scheduler.dispose(), the in-flight flush (blocked on
ask("completion_result")) never resolves, deadlocking the UI.

Add abort flag check to ask()'s pWaitFor condition so blocked asks
unblock immediately when the task is aborted.

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 08:53:21 -07:00
abeatrix 7d26c6f052 Cline SDK 0.0.6 2026-03-19 22:56:01 -07:00
abeatrix a0c974f8b3 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-19 22:41:47 -07:00
abeatrix e84d61bcda fix: ensure rpc data path 2026-03-19 22:15:24 -07:00
Max 200d58e2c6 Merge pull request #6 from cline/max/fix-switching-provider-mid-session
fix(acp): recreate session manager when provider changes mid-session
2026-03-19 21:05:52 -07:00
Tony Loehr 54726f1677 docs: fix SDK documentation accuracy and completeness (#9856)
* docs: fix SDK documentation accuracy and completeness

- Fix setPermissionHandler API Reference to use correct async/return signature
  (was showing old callback pattern with (request, resolve) => void)
- Remove non-existent PermissionResolver type from Exported Types table
- Fix PermissionHandler type description to match actual signature
- Add missing hooksDir option to ClineAgentOptions documentation
- Fix newSession() example to use real model IDs
- Replace developer personal path in Full Example with generic path
- Use placeholder for version in initialize() example to avoid staleness
- Expand Stop Reasons table and add note about current implementation
- Add missing key exported types: AcpSessionStatus, AcpSessionState,
  RequestPermissionRequest/Response, PermissionOption, SessionUpdatePayload,
  SessionModelState, ModelInfo, TextContent/ImageContent/AudioContent,
  SetSessionMode/Model request/response types, TranslatedMessage

* docs: improve SDK visibility and disambiguate from API code examples

- Move SDK page higher in Cline CLI nav (after Installation, before Interactive Mode)
- Add sidebarTitle 'SDK (Programmatic Use)' for clearer nav label
- Rename api/sdk-examples to 'Code Examples' to avoid naming confusion with the Cline SDK
- Update API overview card title to match

* Apply suggestions from code review

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* docs: add ClientCapabilities, Error Handling, and BYO API key docs to SDK

- Document clientCapabilities object and its effect on agent behavior
- Add Error Handling section with all throwable errors per method
- Expand BYO API key setup with concrete CLI auth examples

* docs: fix duplicated Stop Reasons table rows from code review

* docs: fix 3 accuracy issues found in source code audit

- Fix protocolVersion: was '0.9.0' (fabricated), actually 1 (number) from @agentclientprotocol/sdk
- Fix clientCapabilities: was claiming they change SDK behavior, but ClineAgent always uses standalone providers (capabilities only matter via AcpAgent stdio wrapper)
- Fix permission options: remove reject_always (never sent by agent, only allow_once/allow_always/reject_once are used)

* docs: clarify custom clineDir usage with CLI --config flag

Address PR review feedback: the BYO auth section mentioned custom
clineDir without showing how to target it from the CLI. Remove the
vague reference and add explicit --config flag documentation with
side-by-side SDK and CLI examples.

* docs: remove misleading 'by default' qualifier from SDK BYO auth section

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-19 21:01:24 -07:00
abeatrix 5a00df264a feat: workflow build 2026-03-19 20:19:40 -07:00
abeatrix 977acc48e8 feat: Logger Wrapper & Hooks Worker 2026-03-19 20:14:42 -07:00
abeatrix 126ba7ce1e Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-19 19:51:24 -07:00
abeatrix 9755b30f00 feat: Cline Telemetry Service 2026-03-19 19:51:19 -07:00
Saoud Rizwan 6308fef0a9 fix(cli): use kanban@latest to always fetch newest version (#9898)
* fix(cli): use kanban@latest to always fetch newest version

npx -y kanban may use a cached version. Using @latest ensures
users always get the most recent kanban release.

* chore(cli): bump version to 2.8.2 and update changelog
2026-03-19 19:37:53 -07:00
Saoud Rizwan 62990b00c4 fix: bound agent retries for missing credentials 2026-03-19 19:02:34 -07:00
CandiedUniverse b3fc79b8ce Remove example hooks (#9896) 2026-03-19 18:42:55 -07:00
abeatrix 6d20225562 feat: cline doctor 2026-03-19 18:33:07 -07:00
abeatrix 93666605e6 fix: handles session clean ups and add doctor 2026-03-19 18:17:43 -07:00
abeatrix d509492db8 feat(core): add OpenTelemetry support and TelemetryService
Introduces a robust telemetry system to the core package by integrating
OpenTelemetry. This allows for standardized tracking of metrics, logs,
and traces across different environments.

Key changes:
- Added `TelemetryService` and `ITelemetryAdapter` to handle telemetry operations.
- Implemented `OpenTelemetryAdapter` with support for Console, OTLP (gRPC, HTTP/JSON, and Protobuf) exporters.
- Updated `package.json` with necessary OpenTelemetry dependencies and entry points.
- Exposed `loadOpenTelemetryAdapter` in the Node.js entry point for dynamic loading.
- Updated build configurations to include telemetry source files.
2026-03-19 17:37:12 -07:00
abeatrix 1853379510 feat(cli): implement persistent hook worker and subprocess client
Introduces a new hook worker mechanism to the CLI to improve the
efficiency of hook execution. This change adds a persistent subprocess
client that maintains a long-running "hook-worker" process, reducing
overhead compared to spawning a new process for every hook event.

Key changes:
- Added `hook-worker` command to the CLI to handle hook payloads via stdin/stdout.
- Implemented `PersistentHookClient` in `@clinebot/agents` to manage communication with the worker process.
- Updated session logic to queue spawn requests and track subagent status through hook events.
- Added support for hook worker request/response multiplexing using unique IDs.
- Integrated `@clinebot/shared` dependency in the CLI package.
2026-03-19 17:27:07 -07:00
abeatrix 08acc52732 telemetry 2026-03-19 17:09:58 -07:00
abeatrix e12a57c076 hooks 2026-03-19 17:09:16 -07:00
CandiedUniverse ff05ec3bbe Latency improvements for remote workspaces (#9858)
* Implement minimal change set for latency improvements

Cline's code review improvements

Further code review improvements

* fix: address code review issues for presentation scheduler

- Add reset() method to TaskPresentationScheduler to prevent stale
  timer leaks between API request retries within the same task
- Call presentationScheduler.reset() in streaming state reset section
- Fix hadVisibleAssistantContent to track reasoning content, not just
  text, preventing every reasoning chunk from getting immediate priority
- Remove unused 'low' priority from PresentationPriority type and
  associated cadence configuration (YAGNI)
- Trim TaskLatencyTrigger to only values actually used: text/reasoning/tool
- Add JSDoc to isRemoteWorkspaceEnvironment documenting the heuristic
  fallback and its false-positive risk
- Add Logger.warn for invalid cadence env-var overrides
- Add comment at scheduler construction explaining detection promise
  dependency on remoteWorkspaceDetectionPromise
- Add comments to CLI host bridge about intentional remoteName omission
- Expand test coverage: reset(), coalescing, priority upgrade tests

* fix: address code review issues in latency/presentation scheduler

- Fix #1/#3: Set didPresentAnyContent=true for text and tool_calls chunks
  so coalescing actually activates for text-only and tool streams (was
  only set for reasoning chunks, defeating the 40/90ms cadence goal)

- Fix #2: Re-check flushInProgress after awaiting in-flight flush in
  runFlushCycle to prevent two concurrent callers from both proceeding
  past the guard and starting concurrent flushes

- Fix #4: Document flushNow() disposed no-op contract so callers
  understand the finalization guarantee

- Fix #5: Add disposed guard to reset() to prevent post-dispose state
  mutation

- Fix #6: Remove platform/version heuristic from isRemoteWorkspaceEnvironment
  — only use remoteName. The substring match on 'remote' produced false
  positives for version strings like '1.0.0-remote-fix'. Non-VSCode hosts
  should populate remoteName explicitly to opt in to the higher cadence.

- Fix #7: Add remoteWorkspaceDetectionSettled flag and warn if getDelayMs
  is called before detection resolves, making the timing dependency
  explicit and detectable in production logs

- Fix #8: Upgrade disabled-scheduler bypass error from Logger.debug to
  Logger.warn so silent flush failures are visible

- Update latency.test.ts: replace heuristic test with false-positive
  regression tests and add null/empty-field coverage

* fix: address code review issues in latency/presentation scheduler

- Fix flushNow race condition: wait for all in-flight flushes to drain
  before setting pendingPriority so the post-flush continuation cannot
  steal it, guaranteeing at least one flush runs after flushNow() returns
- Add regression test for the flushNow race condition
- Fix disabled-path (CLINE_DISABLE_PRESENTATION_SCHEDULER): use
  presentationScheduler.flushNow() instead of a fire-and-forget void
  call so the presentAssistantMessage lock/pending-updates mechanism
  is respected when the scheduler is bypassed
- Rename didPresentAnyContent -> didScheduleAnyContent to accurately
  reflect that content has been scheduled, not necessarily flushed
- Update stale comment in streaming loop to match new variable semantics
- Upgrade remote workspace detection failure log level from debug to warn
- Document reset() interaction with in-flight flush on already-reset state
- Document empty-string remoteName edge case in getHostVersion.ts

* refactor: polish presentation scheduler for production readiness

- Extract PresentationPriority type to shared presentation-types.ts,
  breaking the latency.ts → TaskPresentationScheduler.ts type coupling
- Remove redundant pendingWhileFlushing flag from TaskPresentationScheduler;
  pendingPriority alone is sufficient for the post-flush continuation check
- Cache env var reads in latency.ts at module load (hot path optimization)
- Inline flushAssistantPresentation() one-liner into the scheduler constructor
- Make processNativeToolCalls protected to enable type-safe test access
- Remove as any cast in Task.processNativeToolCalls.test.ts

* Fixes as per Greptile review feedback

* Further fixes as per Greptile feedback

* Further fixes as per Greptile feedback
2026-03-19 17:05:49 -07:00
abeatrix 7ec3184384 fix: use bun for script in linux
Linux now runs script ... -- bun ...,  while macOS keeps the BSD-style form

This addresses the script: unrecognized option '--provider' error that was causing all 6 interactive tests to fail in CI. I verified the harness locally and the previously broken interactive launch/toggle cases now execute correctly under the patched path. My local macOS run still has two separate config-view timeouts, but those are distinct from the Linux script parsing failure in your CI log.
2026-03-19 17:03:03 -07:00
abeatrix 732542d105 fix: hook files validation 2026-03-19 15:37:01 -07:00
Max Paulus 🥪 0ba4dd5c44 fix(acp): recreate session manager when provider changes mid-session
When switching providers (e.g. OpenAI Codex → Cline) via
setSessionConfigOption, the old session manager was reused because
ensureSessionManager() returns early if one already exists. This caused
"Codex CLI exited with code 1" errors on the next prompt.

Tear down the old session manager on provider change, preserving
conversation messages via readMessages/initialMessages so context
carries over to the new provider.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:08:04 -07:00
Max fde2370693 feat: scaffold ACP module and install @agentclientprotocol/sdk
* feat: scaffold ACP module and install @agentclientprotocol/sdk

Add @agentclientprotocol/sdk dependency and create src/acp/ module with
a runAcpMode() placeholder that establishes a stdio-based ACP connection.
Wire --acp flag in main.ts as a mutually exclusive early exit before
provider resolution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(acp): implement CliAcpAgent with initialize and newSession handlers

Adds the ACP Agent class that implements the acp.Agent interface with:
- initialize(): returns protocol version, agent capabilities, and agent info
- newSession(): generates session ID, stores session state, returns plan/act modes
- Stub methods for prompt, cancel, setSessionMode, authenticate, shutdown
- Wires agent into runAcpMode() via AgentSideConnection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(acp): implement prompt handler bridging ACP to runAgent

Add session-updates.ts to translate AgentEvents to ACP SessionUpdate
notifications (message chunks, thought chunks, tool calls). Implement
prompt() on CliAcpAgent using CliSessionManager to run the agent and
stream updates back via the ACP connection. Also implement cancel()
and improve shutdown() to clean up active sessions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(acp): implement session modes, shutdown, and session info updates

- Add currentMode tracking to SessionState, defaulting to "act"
- Implement setSessionMode() with validation and current_mode_update notification
- Wire buildConfig() to use session's current mode instead of hardcoded "act"
- Emit session_info_update with updatedAt timestamp after prompt completion
- Add sendCurrentModeUpdate and sendSessionInfoUpdate helpers to session-updates.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(acp): implement permission request handling for tool approvals

Bridge CLI tool approval system to ACP's session/request_permission flow.
When the agent invokes a tool requiring approval, the new permissions module
translates the request into ACP format, sends it to the client for user
decision, and maps the response back to CLI's ToolApprovalResult.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(acp): implement session cancel with per-session AbortController

Add AbortController tracking to SessionState so cancel() works at any
stage of prompt() execution. The controller signal is checked at each
async boundary (config build, session manager creation) and an event
listener propagates abort to sessionManager.abort() once the agent is
running. shutdown() also aborts controllers before direct cleanup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix some tool call names

* fixup! feat(acp): implement CliAcpAgent with initialize and newSession handlers

* remove tui-test

* refactor(acp): replace describeToolCall with formatToolInput from helpers

Remove the duplicate describeToolCall function in tool-utils.ts and
replace it with buildToolTitle, which delegates to the existing
formatToolInput from utils/helpers.ts for input summarization.

Also expand TOOL_KIND_MAP to include current tool names (read_files,
run_commands, editor, search_codebase, fetch_web_content, spawn_agent,
skills) alongside the legacy names.

Update permissions.ts and session-updates.ts to use the new
buildToolTitle function.

* don't truncate some tool kinds

* fix acp auth check

* fix(acp): persist session manager across prompt() calls for conversation continuity

Previously each prompt() created a new CliSessionManager with interactive:false
and disposed it after, losing all conversation history between turns. Now the
session manager is created lazily on the first prompt() with interactive:true
and reused via send() for subsequent prompts, matching the pattern in
run-interactive.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(acp): expose unstable_setSessionModel and return models in newSession

Add support for the ACP `session/set_model` method so clients can change
the model at runtime. Return the full list of provider models in the
newSession response via SessionModelState.

- Add updateSessionModel to CliSessionManager/SessionManager interfaces
- Implement in DefaultSessionManager (calls agent.updateConnection)
- Implement in RPC session manager path (updates config.model)
- Add unstable_setSessionModel to AcpAgent
- Populate models.availableModels from getModelsForProvider in newSession

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix acp agent version and name

* feat(acp): add session config options for provider, model, and mode

Implement setSessionConfigOption on AcpAgent to allow clients to
configure provider, model, and mode via the ACP config option system.
newSession now returns configOptions alongside the legacy modes/models
fields. Provider names are resolved from the model registry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* add build && link cli shortcut to kanban

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 14:56:12 -07:00
abeatrix 8a103690b9 fix: pre-commit hook 2026-03-19 14:42:36 -07:00
abeatrix d342562e0d feat: persistent message per turn 2026-03-19 14:31:15 -07:00
abeatrix 9449d59a9b Cline SDK 0.0.5 2026-03-19 13:18:29 -07:00
abeatrix 2a38695cfd refactor: runtime session 2026-03-19 13:13:03 -07:00
abeatrix 85a66e14d8 refactor: session-data and file names 2026-03-19 11:48:35 -07:00
abeatrix 4b22746055 refactor: centralize model catalog and history 2026-03-19 11:21:23 -07:00
abeatrix ec85d21903 Cline SDK 0.0.4 2026-03-19 10:35:31 -07:00
abeatrix 64a10b99f9 0.0.3 2026-03-18 23:14:24 -07:00
Ara bde7049c01 Release v3.74.0 Notes (#9879) 2026-03-18 19:08:50 -07:00
abeatrix d40b8307c2 feat: Bun backend + new transport API for Code App
Document the new persistent Bun desktop backend, added dev/build commands, updated startup flow, and replaced the chat transport envelope with the unified command/response/event websocket protocol so contributors follow the current architecture.
2026-03-18 19:04:35 -07:00
Ara 7cd06744ad feat: implement dynamic free model detection for Cline API (#9878)
Replace hardcoded free models list with runtime resolution from
recommended models. The handler now dynamically fetches free model
IDs using refreshClineRecommendedModels with fallback to static
defaults, and normalizes model IDs for consistent comparison.
2026-03-18 18:55:06 -07:00
abeatrix 6ae128db36 refactor: @clinebot/core/node for Node runtime
helpers and session backend imports

`@clinebot/core/node` (instead of `@clinebot/core/server`) for Node runtime
helpers and session backend imports, keeping workspace boundary guidance
accurate after the package path change.
2026-03-18 18:17:23 -07:00
abeatrix e87ac67961 fix: thinking args parsing in CLI 2026-03-18 16:22:42 -07:00
abeatrix c6dc8b3c17 Merge branch 'bee/vscode' 2026-03-18 16:15:14 -07:00
shey-cline c88d3238cf Replace Error Message When Not Logged In to Cline (#9632) 2026-03-18 16:01:06 -07:00
abeatrix 3b8fbc1a1c refactor: VS Code example 2026-03-18 14:48:00 -07:00
abeatrix 344bbbcbbb Cline SDK 0.0.2 2026-03-18 14:45:47 -07:00
jessy-cline 852ca2348f fix(ui): align ClineRulesToggleModal padding with ServersToggleModal (#9870)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-03-18 14:13:39 -07:00
abeatrix ca0b8a0660 refactor: package exports and publish workflows 2026-03-18 13:59:19 -07:00
abeatrix 1968e08a57 feat: sdk publish workflow version script 2026-03-18 12:59:20 -07:00
abeatrix 3152dfc6ef Cline SDKs 0.0.1 2026-03-18 12:40:56 -07:00
Saoud Rizwan f60cbceecb fix(core): use nullish for editor optional fields (#2)
* fix(core): allow nullish editor optional fields

* Update validation to use truthy checks for fields

* fix: editor executor

---------

Co-authored-by: Bee <68532117+abeatrix@users.noreply.github.com>
Co-authored-by: abeatrix <beatrix@cline.bot>
2026-03-18 12:27:58 -07:00
CandiedUniverse 7091ccf2c7 Fix flaky CLI Enter-key handling across Windows/test environments (#9867)
* fix(cli): normalize enter key handling in ink inputs

* fix(cli): pass input through auth select enter handling
2026-03-18 12:20:25 -07:00
Mathis ad6c33ac5b feat: add file read deduplication cache to prevent repeated reads (#9836)
* feat: add file read deduplication cache to prevent repeated reads

- Add fileReadCache to TaskState for tracking read files per task
- ReadFileToolHandler checks cache before reading, returns cached content on repeat reads
- Warns model after 3+ reads of same file to stop re-reading
- WriteToFileToolHandler and ApplyPatchHandler invalidate cache on file changes
- Reduces wasted API tokens from models reading same files repeatedly

* fix: address file read cache gaps - image blocks, execute_command, redundant invalidation

* Update src/core/task/tools/handlers/ExecuteCommandToolHandler.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* PR changes

- __`TaskState.ts`__ — Simplified cache type from `{ content: string; readCount: number; imageBlock? }` to `{ readCount: number; mtime: number; imageBlock? }`. Dropped `content` to save memory; added `mtime` for external change detection.

- __`ReadFileToolHandler.ts`__ — Four improvements:

  - __Removed redundant `.set()` call__ — reviewer was correct that objects are modified by reference in Map
  - __Added mtime-based cache validation__ — on cache hit, `stat()` the file and compare mtime. If the file was modified externally (user edited in their editor), the cache entry is evicted and a fresh read occurs
  - __Dropped content from cache__ — cache now only stores metadata (readCount, mtime, imageBlock). On cache hits, the file is re-read from disk, addressing memory concerns
  - __Softened readCount >= 3 warning__ — removed the aggressive "Do NOT read this file again" language; now says "Please use the information you already have and proceed with your task"

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-18 10:38:23 -07:00
abeatrix d36ca071c5 fix: test case update 2026-03-18 10:26:39 -07:00
abeatrix 40f5263cd9 refactor: VS Code example to use session runtime 2026-03-18 10:00:14 -07:00
abeatrix d0c062e039 fix: remove resetTerminalState 2026-03-18 08:57:52 -07:00
abeatrix 4f505b8f5d fix: background session launcher 2026-03-18 08:56:41 -07:00
abeatrix cbacfbd133 fix: diff view in code app 2026-03-18 07:56:50 -07:00
abeatrix 62623cb576 feat: match old CLI commands and options 2026-03-18 06:00:27 -07:00
abeatrix 77e865f812 refactor: use cli package display name 2026-03-18 03:57:21 -07:00
abeatrix 520e536431 feat: set default max token 2026-03-18 03:32:02 -07:00
abeatrix c6b85fe67a feat: includes line numbers for file read output by default 2026-03-18 03:17:59 -07:00
abeatrix 1d36a65a80 chore: update peerDependencies 2026-03-18 02:49:24 -07:00
abeatrix 5e09869e1c fix: add missing pacakge for typechecks 2026-03-18 02:16:55 -07:00
abeatrix 2b87806958 fix: gemini tools parser 2026-03-18 02:11:42 -07:00
abeatrix 7435fba1bd feat: ToolRoutingRule and clean up tool schema 2026-03-18 01:35:43 -07:00
abeatrix eb1e4bc076 feat: improve team await time 2026-03-18 00:36:15 -07:00
abeatrix 5dc19f3df5 fix: normalize code app workspace apth 2026-03-18 00:15:58 -07:00
abeatrix 8027254327 fix: build scripts and protos paths 2026-03-17 23:28:43 -07:00
abeatrix ebe7553508 fix: pino log location 2026-03-17 23:03:03 -07:00
abeatrix 18dad637e6 refactor: consolidate exports from rpc and shared in core 2026-03-17 22:52:57 -07:00
abeatrix f5575591ae chore: remove docs 2026-03-17 22:08:12 -07:00
abeatrix 67b207f7e8 feat: add --max-consecutive-mistakes flag to CLI and fix tests 2026-03-17 21:59:08 -07:00
abeatrix 5681cb7524 feat: add --reasoning-effort flag to CLI 2026-03-17 21:45:44 -07:00
abeatrix 74a8b5f366 fix: gemini tool calls process and misc fixes 2026-03-17 21:26:46 -07:00
abeatrix 43bf9babb7 chore: clean up package exports 2026-03-17 20:18:34 -07:00
abeatrix 940c6ab423 refactor: update package names to @clinebot from @cline 2026-03-17 19:30:59 -07:00
abeatrix 50dfaa3875 fix: lazy-load on CLI subcommands 2026-03-17 17:00:31 -07:00
abeatrix bb5abe6717 feat: VS Code example app with sidebar view 2026-03-17 16:47:22 -07:00
abeatrix 00d099320d refactor: vs code example to use React webview 2026-03-17 16:26:54 -07:00
abeatrix 044a1d036e fix: adapter message building 2026-03-17 14:24:28 -07:00
abeatrix 4f073cafd1 feat: WhatsApp for Business and gChat adapters 2026-03-17 13:58:47 -07:00
Tony Loehr 71e312e92a feat: add feature tips tooltip during thinking state (#9799)
* feat: add feature tips tooltip during thinking state

Show rotating feature tips below the Thinking indicator to keep users engaged and educate them on features like Double-Check Completion, .clinerules, Plan Mode, MCP Servers, checkpoints, and more.

- New FeatureTip component with 12 curated tips
- 2s delayed appearance, 8s cycling with fade transitions
- Visible throughout entire thinking/reasoning phase
- Proper timer cleanup on unmount

* Update webview-ui/src/components/chat/FeatureTip.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update webview-ui/src/components/chat/FeatureTip.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Update webview-ui/src/components/chat/ChatRow.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix: remove unused useMemo import from FeatureTip

* fix: increase test delays in QuitCommand.test.tsx for Windows CI reliability

* Update webview-ui/src/components/chat/FeatureTip.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix: add smooth fade-in for first tooltip appearance

The first tooltip was appearing abruptly because the element went from
not-in-DOM (return null) to opacity-100 instantly. Added hasFadedIn state
with requestAnimationFrame to ensure the CSS transition applies on initial
render, giving the first tip a smooth 300ms fade-in.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-17 13:51:37 -07:00
Charlotte Stinson 5903840f79 Added Fields to Responses API for the Oracle Code Assist (OCA) Provider (#9852)
* added fields to responses api

* fix temp problem

* cleaning up

* clean modelId

* Apply suggestion from @greptile-apps[bot]

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: Charlotte Stinson <charlottestinson@Charlottes-MacBook-Pro.local>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-17 13:41:39 -07:00
Beatrix Woo 0db9a83187 feat: connector adatprer and review approval hooks 2026-03-17 11:44:48 -07:00
ClineXDiego 0c677b63db fix(browser): skip WebP for GLM and Devstral models running through llama.cpp (#9837)
llama.cpp's STB image library doesn't support WebP format. Users running
GLM 4.6V, GLM 4.5, and Devstral models via llama.cpp server (openai-compatible
endpoint) were hitting a 400 error when using the browser tool because Cline
sends screenshots as WebP by default.

modelDoesntSupportWebp() only checked for Grok models. Extend it to also
cover GLM and Devstral model families using the existing family detection
functions. Also update isGLMModelFamily() to handle space-separated model IDs
like 'GLM 4.6V' (the format llama.cpp server reports for this model).

Fixes #8203
2026-03-17 14:57:40 -03:00
ClineXDiego b741135f85 fix(litellm): respect user-configured context window in getModel() (#9834) 2026-03-17 05:36:44 -07:00
Ara 2eab216815 fix(wandb): honor explicit model IDs outside static catalog (#9839) 2026-03-16 16:40:28 -07:00
Ara e57174eec8 fix(fireworks): add missing serverless models and pricing (#9810)
* feat: refresh Fireworks serverless model defaults

* fix(fireworks): handle cached token usage and write pricing

* fixing maxtokens for gemini family

* feat(fireworks): add missing serverless models and pricing
2026-03-16 14:28:23 -07:00
Max ad4631c682 bump cline version (#9833) 2026-03-16 13:36:41 -07:00
Mathis aae23e8685 fix: Claude Code provider failing with 4.6 models and newer CLI versions (#9783)
* fix: Claude Code provider failing with 4.6 models and newer CLI versions

- Update --disallowedTools list to match current Claude Code CLI tools
  (12 new tools were unblocked, causing models to use native tool_use
  instead of Cline's XML tools)
- Fix rate_limit_event handling for new CLI format (top-level type
  instead of system subtype)
- Handle unknown content block types and new message types gracefully
- Fix assistantHasContent check to account for tool calls accumulated
  via toolUseHandler even when useNativeToolCalls is false

* resolved .include mismatch to .containEql

* Update src/integrations/claude-code/types.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Removed `LegacyRateLimitEvent` type and its union reference from `types.ts`

* Fixed loop with async tool calls in new claude code.

* PR review feedback fixes

__Fix #2 (claude-code.ts):__ Cleaned up the error field check — replaced verbose `"error" in message` guard + ternary chain with a simpler `message.error` check using optional chaining and nullish coalescing (`message.content?.[0]` + `?? fallback`).

__Fix #3 (claude-code.ts):__ Replaced `message.content.length > 0 ? message.content[0] : undefined` with `message.content?.[0]` using optional chaining for the `stop_reason` block.

__Fix #4 (claude-code.ts):__ Replaced repeated `(content as any)` casts in the `default` switch case with a single typed cast: `const unknownBlock = content as { type: string; text?: string }`, making the code cleaner and safer.

__Fix #5 (ApplyPatchHandler.ts):__ Replaced both `await import("node:path")` and `require("node:path")` dynamic imports with a static `import { resolve as resolvePath } from "node:path"` at the top of the file.

* Remove file read deduplication feature (moved to separate PR)

* Remove ReadFileToolHandler file-not-found test (moved to separate PR)

* Add LegacyRateLimitEvent type for older CLI format

* Restore ReadFileToolHandler.ts and test from upstream/main (fix stale local main revert)

* Revert ReadFileToolHandler.ts to match fork main (no try/catch, no test file)

* manually reverting back

* Update src/core/api/providers/claude-code.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Restore ReadFileToolHandler.ts and test to match cline/cline upstream main

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-16 13:01:55 -07:00
dependabot[bot] 0ff27591d5 chore(deps-dev): bump lodash (#8803)
Bumps the npm_and_yarn group with 1 update in the /testing-platform directory: [lodash](https://github.com/lodash/lodash).


Updates `lodash` from 4.17.21 to 4.17.23
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-16 12:25:46 -07:00
dependabot[bot] 23127f22de chore(deps): bump qs from 6.14.1 to 6.15.0 (#9343)
Bumps [qs](https://github.com/ljharb/qs) from 6.14.1 to 6.15.0.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.14.1...v6.15.0)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.15.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-16 12:00:02 -07:00
CandiedUniverse 57ed14d1b1 Fix issue with Windows notification (#9743)
* Fix issue with Windows notification

Fix Windows proto tooling

Fix Windows unit test path normalization

Revert "Fix Windows unit test path normalization"

This reverts commit 73400a3ca6f0300d009f7c8238a016d769186f3a.

Remove package-lock.json changes

* Remove unnecessary changes

* Use command approval string for notifications

* Address PR feedback on Windows notifications

* Fix Windows protoc path for CI

* Polish notification safety and test coverage

* Fix unfound tests in CI

* Harden Windows notifications and protoc execution

* Fix Windows path normalization in glob test

* Fix as per Greptile feedback
2026-03-16 09:44:20 -07:00
aikido-autofix[bot] a769585406 fix(security): update dependencies (#9812)
Co-authored-by: aikido-autofix[bot] <119856028+aikido-autofix[bot]@users.noreply.github.com>
Co-authored-by: Max <maxpaulus43@gmail.com>
2026-03-16 09:15:40 -07:00
CandiedUniverse c44b29b002 Fix Windows CLI tests related to /q and /exit (#9747)
* Fix flaky Windows CLI quit slash tests

* Refine CLI slash command handling and test stability

* Address Greptile cleanup feedback
2026-03-16 07:59:26 -07:00
dependabot[bot] bb4e397a51 chore(deps): bump undici from 7.20.0 to 7.24.3 (#9825)
Bumps [undici](https://github.com/nodejs/undici) from 7.20.0 to 7.24.3.
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.20.0...v7.24.3)

---
updated-dependencies:
- dependency-name: undici
  dependency-version: 7.24.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-16 07:52:50 -07:00
Saoud Rizwan 9824d8d476 Bump CLI version from 2.7.0 to 2.7.1 2026-03-15 20:36:02 -07:00
Saoud Rizwan a46c5288ca Fix Notification hook getting called by command output asks 2026-03-15 20:08:06 -07:00
Mohammad Bakir 91b947de69 feat: Add W&B Inference by Coreweave as provider (#9800)
* feat(wandb): add W&B Inference by CoreWeave provider

Adds support for W&B Inference as an API provider using a W&B API key.
Implements a provider handler with OpenAI-compatible streaming and a static
model catalog, and wires the provider through the API layer, configuration
schema, storage, CLI model picker, and settings UI.

* Updated input/output price of NVIDIA-Nemotron

* Updated helpText

* handle reasoning tokens in streaming respons

* Added clarifying comment on how W&B token usage is reported and why cached tokens

* fix: restore proto field numbers changed by generation script

* Update src/core/api/providers/wandb.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-13 16:06:35 -07:00
Saoud Rizwan 7b25a21b26 fix(cli): add -y flag to npx kanban to auto-confirm install 2026-03-13 15:47:18 -07:00
Ara 1d1071dcf5 fix: consolidate Parallel tool-calling fixes (#9738)
* fix: consolidate parallel tool-calling fixes

* test(snapshot): fix vertex gemini3 snapshot newline

* fix gemini toolcall id collision (#9768)

* test(snapshot): fix vertex gemini3 snapshot newline

* fix(gemini): prevent native tool-call ID collisions

* fixing maxtokens for gemini family

* test(gemini): assert fallback tool call ids
2026-03-13 09:20:23 -07:00
Dominic Cooney 8d07b7d6cb fix: catch errors in path-based tool handlers instead of crashing (#9732)
* fix: catch errors in path-based tool handlers instead of crashing

ListCodeDefinitionNamesToolHandler, ListFilesToolHandler, and
SearchFilesToolHandler let exceptions from their core operations
propagate through ToolExecutor's re-throw path, crashing the CLI
process. This is the same class of bug fixed for ReadFileToolHandler
in #9730.

Changes per handler:
- Wrap the core operation in try/catch, returning formatResponse.toolError()
  on failure so the model can see the error and recover gracefully.
- Move consecutiveMistakeCount reset to after a successful operation so
  repeated failures accumulate toward the yolo-mode mistake limit.
- Increment consecutiveMistakeCount on caught errors.

Add end-to-end tests exercising each handler with a mock TaskConfig,
covering: non-existent paths, missing parameters, failure accumulation,
and success-based counter reset.

* address review: expand try/catch scope, add stub-based tests

- Include resolveWorkspacePath inside try/catch in
  ListCodeDefinitionNamesToolHandler and ListFilesToolHandler (matching
  SearchFilesToolHandler's pattern) so path resolution failures are
  also caught gracefully.
- Fix trivially-true assertion in file-not-a-directory test.
- Add 6 new stub-based tests that force core operations to throw:
  parseSourceCodeForDefinitionsTopLevel, listFiles, and
  determineSearchPaths — verifying the catch paths return
  formatResponse.toolError() and increment consecutiveMistakeCount.
- Total: 19 passing tests (up from 13).

* address review: move clineignore check before IO in ListFilesToolHandler

Move the .clineignore access validation before resolveWorkspacePath and
listFiles so blocked paths are rejected without incurring IO cost.
Also ensures consecutiveMistakeCount is only reset after all
validations and the core operation succeed.

* address review: increment counter on clineignore denial

Clineignore denial in ListFilesToolHandler now increments
consecutiveMistakeCount so repeated attempts at blocked paths
accumulate toward the yolo-mode mistake limit. Added 2 tests
verifying single and repeated clineignore denials.
Total: 21 passing tests.

* fix: increment consecutiveMistakeCount when SearchFilesToolHandler searches fail

Previously, SearchFilesToolHandler's executeSearch() caught regexSearchFiles
errors and returned {success: false}, but the handler unconditionally reset
consecutiveMistakeCount to 0 even when ALL searches failed. This contradicted
the PR's goal of accumulating failures toward the yolo-mode mistake limit.

Now we check if any search succeeded before resetting the counter:
- If at least one search succeeded: reset to 0 (existing behavior for successes)
- If all searches failed: increment the counter (new fix)

Also added comprehensive test coverage for this scenario, including tests for:
- regexSearchFiles throwing errors
- Repeated search failures accumulating
- Successful search resetting the counter after failures

* fix: detect error strings in ListCodeDefinitionNamesToolHandler

parseSourceCodeForDefinitionsTopLevel returns error strings instead of
throwing exceptions for file paths and non-existent directories. The
handler now detects these error conditions and increments
consecutiveMistakeCount so repeated failures accumulate correctly.

This addresses Greptile's feedback that the counter was unconditionally
resetting to 0 for all real-world failure modes of this handler.
2026-03-12 20:58:06 -07:00
Dominic Cooney aacc69a558 fix: catch file-not-found in ReadFileToolHandler instead of crashing CLI (#9730)
* fix: catch extractFileContent errors in ReadFileToolHandler

When extractFileContent throws (e.g. file not found), the exception
propagated through ToolExecutor which re-threw it, crashing the CLI
process with exit code 1.

Now file read errors are caught and returned as formatResponse.toolError()
so the model can see the error and recover gracefully (e.g. try a
different file path) instead of terminating the entire task.

Also increments consecutiveMistakeCount so the yolo-mode mistake limit
still functions correctly.
2026-03-13 10:52:32 +09:00
Max a27bedffb6 max/cli tui e2e tests (#9753)
* add tui UI tests

using microsoft/tui-test library, can run many headless versions of
cline and execute ui tests (requires Node <= 20)

improve brittle sleep calls

* add cli-tui-tests github action

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-03-12 16:06:25 -07:00
Diego Ferreyra bbeecefe84 docs: fix broken Oracle Code Assist website link (#9782)
The Oracle Code Assist URL moved from /artificial-intelligence/code-assist/
to /application-development/code-assist/. The old URL returns a 404.

Fixes #9776

Co-authored-by: gatof81 <gatof81@users.noreply.github.com>
2026-03-12 11:56:34 -03:00
alex-lum 50b57f472f feat(telemetry): add provider to task.tokens event (#9762) 2026-03-11 17:30:03 -07:00
CandiedUniverse 0e7e0099cd Changelog and version bump for release (#9775) 2026-03-11 17:21:04 -07:00
AJ Juaire 44cbfe9d9c Update Jupyter Notebook gifs (#9772)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-03-11 16:25:46 -07:00
Saoud Rizwan 4ecccf5105 feat(cli): add mcp add shortcuts for stdio and http servers (#9773) 2026-03-11 16:17:43 -07:00
Ara 6d29bc6551 fix(prompt): resolve native tool placeholder interpolation (#9764)
* fix(prompt): resolve CWD and MULTI_ROOT_HINT in native tool schemas

* refactor(prompt): share multi-root hint constant
2026-03-11 08:10:58 -07:00
Saoud Rizwan 820057308a feat(cli): add kanban alias command (#9763) 2026-03-10 21:57:16 -07:00
Ara 7355f7e3b9 fix(gemini): cap flash output tokens to 8192 across providers (#9749) 2026-03-10 14:10:14 -07:00
abeatrix 828f8ef349 test: add interactive e2e test for cli 2026-03-10 13:44:18 -07:00
abeatrix 806ece01b6 feat: add unit and smoke tests for routines 2026-03-10 13:30:18 -07:00
abeatrix 94ed5266a5 chore: update examples 2026-03-10 13:13:44 -07:00
abeatrix ff9a3d4817 chore: clean up 2026-03-10 13:06:18 -07:00
abeatrix edbff7b02d chore: migrate to typed protobuf messages end-to-end 2026-03-10 12:47:10 -07:00
abeatrix ee426bdf58 fix: auth token store 2026-03-10 12:12:05 -07:00
Max 156f18f7b2 call telemetry service after initializeCli (#9752)
- calling telemetry service before initializeCli call causes a
"hostprovider not initialized error", which invokes errorservice, which
causes another "hostprovider not initialized error", which was breaking
this cline use case: echo "say hello" | cline

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-03-10 11:50:34 -07:00
abeatrix 5da21e6537 fix: cache views 2026-03-10 11:47:50 -07:00
abeatrix 6f2729c2d2 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-10 11:05:38 -07:00
abeatrix 5155130cd8 fix: gemini tools config and add approval ui 2026-03-10 11:04:10 -07:00
Beatrix Woo ffe0d99036 fix: workspace config 2026-03-10 05:16:24 -07:00
abeatrix 3ad7fb71ae feat: vscode example 2026-03-10 00:30:02 -07:00
abeatrix 0aa34d9d74 feat: title edit 2026-03-09 22:02:03 -07:00
Tomás Barreiro 6129caa423 Get user consent before loading images in Markdown (#9745)
* Add an UsafeImage handler that asks for consent before loading specific images

* Fix div as child of p

* Render self-contained images without consent

* Render alt conditionally and store approved src

* use a block span
2026-03-09 21:06:57 -07:00
abeatrix a8e6500842 feat: provider config used at run time 2026-03-09 20:58:05 -07:00
abeatrix 7900111810 chore: docs update 2026-03-09 20:39:31 -07:00
abeatrix cb4eb0564c clite dev log 2026-03-09 20:35:22 -07:00
abeatrix 08cfeadc9d feat: cline routine 2026-03-09 20:27:31 -07:00
abeatrix 50b7a7dd25 feat: team runtime and tooling 2026-03-09 19:02:35 -07:00
alex-lum ffa4785f43 fix(telemetry): restore cache token and cost metrics in captureTokenUsage (#9741)
* feat(telemetry): restore cache token and cost metrics in captureTokenUsage

Add optional `options` parameter to `captureTokenUsage()` to record
cache write/read token counters and cost histograms that were
previously missing from telemetry.

- Extend `captureTokenUsage` with `cacheWriteTokens`, `cacheReadTokens`,
  and `totalCost` fields via an options object
- Record `cline.tokens.cache_write`, `cline.tokens.cache_read` counters/
  histograms and `cline.tokens.cost` histogram when provided
- Forward cache/cost data from both streaming `onUsageChunk` and
  `getApiStreamUsage` fallback call sites in the task loop
- Add 3 test cases covering options forwarding, undefined skipping,
  and event property inclusion

* refactor(telemetry): extract shared TokenUsage type and add value assertions

Address PR review feedback:
- Extract shared TokenUsage interface used by both captureTokenUsage and
  captureConversationTurnEvent, preventing future drift
- Add numeric value assertions for cache/cost counters and histograms
  so regressions recording wrong values are caught
2026-03-09 15:55:23 -07:00
abeatrix ceb488d884 refactor: generate model list with version number 2026-03-09 14:52:56 -07:00
abeatrix 253dc73077 fix: simplify retry logic 2026-03-09 14:39:07 -07:00
abeatrix 8f652a7b18 refactor: move component folders 2026-03-09 13:56:35 -07:00
abeatrix f72d1ef4a9 chore: clean up docs 2026-03-09 13:46:44 -07:00
CandiedUniverse 92ec126dca Fix Windows unit test path normalization (#9742) 2026-03-09 13:23:47 -07:00
Ara c4c8b16afb chore: remove changesets bot artifacts (#9739) 2026-03-09 12:26:48 -07:00
abeatrix b9c3dea15e fix: import source 2026-03-09 11:38:43 -07:00
Max 2eed79dcd7 use setSessionOverride instead of setGlobalState (#9707)
- cli was storing fields to persistent state when it shouldn't be. The
value of these flags should only live for the duration of the CLI
session

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-03-09 11:20:57 -07:00
shey-cline d39a53feed Add Padding to Retry Message (#9635) 2026-03-09 09:29:27 -07:00
Dominic Cooney cae9bff416 test: rebaseline vertex_gemini3.tools.snap (#9731)
Commit 55569efb7 changed the list_code_definition_names path parameter
description but did not update the snapshot baseline.
2026-03-09 07:57:05 -07:00
Saoud Rizwan 36618fb327 fix(cli): remount TUI only on width resize (#9736) 2026-03-09 04:03:59 -07:00
Saoud Rizwan a335b5cb3e fix(cli): prevent startup prompt replay on resize remount (#9735) 2026-03-09 03:30:01 -07:00
Saoud Rizwan bce71b4448 feat(cli): add --continue for current directory (#9726)
* feat: add cli continue flag for current directory

* docs(cli): clarify continue command examples
2026-03-08 19:54:44 -07:00
Robin Newhouse f6d2b4d9ac Add --auto-condense CLI flag to enable AI-powered context compaction (#9705)
Exposes the existing useAutoCondense setting as a CLI flag, following
the same pattern as --double-check-completion. This allows enabling
auto-condense in eval runs (e.g. SWE-bench via Harbor) to reduce
context exhaustion failures.

Made-with: Cursor
2026-03-08 16:28:48 -07:00
Robin Newhouse 911fcfda47 gemini-hc: add parameter descriptions to Gemini tool schemas (#9681)
The Gemini converter was the only provider that didn't include
parameter-level descriptions in native tool call schemas. Anthropic
and OpenAI converters both resolve param.instruction into each
parameter's description field. This was missing for Google/Gemini,
meaning the model only saw parameter names and types with no
explanation of what each parameter expects.

Made-with: Cursor
2026-03-08 16:27:50 -07:00
Robin Newhouse 55569efb74 fix(tools): prevent crash when list_files/list_code_definition_names receives a file path (#9680)
listFiles() passed unvalidated paths as globby's `cwd`, crashing with
"The cwd option must be a path to a directory" when the model provided a
file path instead of a directory. This affected ~22% of SWE-bench tasks.

- Add isDirectory guard in listFiles() before calling globby
- Fix listFiles() to use resolved absolutePath for cwd instead of raw dirPath
- Return actionable error in parseSourceCodeForDefinitionsTopLevel when
  path is a file, guiding the model to use read_file instead
- Clarify list_code_definition_names parameter description to
  distinguish directory input from file input

Made-with: Cursor
2026-03-08 16:07:03 -07:00
Robin Newhouse 18561ed59d prompt: add test verification rules and make CLI_RULES language-agnostic (#9679)
- S1: Don't modify test assertions to match buggy code
- S2: Run project's existing test suite to verify fixes
- CLI_RULES: Remove Node.js-specific examples (npm/tsc)

Made-with: Cursor
2026-03-08 15:58:13 -07:00
Saoud Rizwan fa7265fa33 feat: add Anthropic Opus 4.6 fast mode variants (#9725)
* feat: add Anthropic Opus 4.6 fast mode variants

* refactor: localize Anthropic fast mode beta constant

* fix: correct Anthropic 1M fast mode pricing
2026-03-08 15:31:37 -07:00
M.Yoshida(Jyuko Co.,Ltd) bf96223639 docs: add .github/copilot-instructions.md for AI coding agents (#9606)
* docs: add .github/copilot-instructions.md for AI coding agents

* docs: update copilot-instructions with provider, state, and CLI guidance
2026-03-08 14:51:55 -07:00
Beatrix Woo e31f6621d9 feat: examples with interactive guide 2026-03-07 13:37:19 -08:00
Beatrix Woo 8c10438ccb chore: update dev setup 2026-03-07 12:41:54 -08:00
Saoud Rizwan c489f79508 fix(cli): apply task flags before welcome TUI mount (#9721) 2026-03-07 11:20:53 -08:00
abeatrix b3ff60c903 feat: register client event 2026-03-07 03:59:21 -08:00
abeatrix bae14830ef feat: wire pino logger 2026-03-07 03:39:51 -08:00
abeatrix 243a34b21d wip: cline config 2026-03-07 03:33:59 -08:00
abeatrix 13e798c736 Merge branch 'main' of https://github.com/cline/sdk-wip 2026-03-07 03:08:56 -08:00
abeatrix c411e1b241 feat: apply patch tool 2026-03-07 03:08:27 -08:00
abeatrix 8a5a021199 wip: TUI for @cline/cli init 2026-03-07 03:04:28 -08:00
abeatrix d6915b4de2 feat: asksage, dify, mistral 2026-03-07 03:00:32 -08:00
abeatrix ba740b0faf refactor: cli split 2026-03-07 01:46:14 -08:00
abeatrix 330f82bd91 chore: clean up types 2026-03-06 23:20:56 -08:00
abeatrix a8ab8c81f6 fix: zod union type 2026-03-06 22:33:42 -08:00
abeatrix ebe10caef1 feat: FileContent and Plugins 2026-03-06 20:41:30 -08:00
abeatrix c213d60983 fix: zod validation 2026-03-06 18:45:32 -08:00
CandiedUniverse 6ccd662a1f Hooks: Add feature toggle (toggled off by default) (#9671)
* Add implementation plan doc

* feat(hooks): reintroduce runtime hooks feature toggle

* fix: thread effective hooks toggle through hook execution

* test: cover hooks feature toggle visibility and settings wiring

* Remove implementation plan doc

* Move Hooks toggle to Advanced section in Feature Settings

* Fixes as per PR feedback

* Clarifying hooksEnabled

* Make hooksEnabled true by default

* Further fixes as per Greptile feedback

* Further fixes as per Greptile feedback

* Fix failing tests
2026-03-06 13:49:03 -08:00
abeatrix 094c54dbec feat: docs 2026-03-06 13:28:32 -08:00
abeatrix c195cb2157 fix: session artifacts store 2026-03-06 13:19:26 -08:00
abeatrix fd6b953918 refactor: buildtime env setup 2026-03-06 12:55:06 -08:00
abeatrix cd1e436254 refactor: agents 2026-03-06 12:37:32 -08:00
Tomás Barreiro a2fa4d3b92 [ENG-1571] Enable CLI Error autocapture (#9686)
* Introduce the enableErrorAutocapture option

* Enable capturing CLI extension

* Capture exception immediately

* Handled uncaptured exceptions

* refactor

* Add tests

* Capture unhandledExceptions

* Add an error boundary to the ink app

* Wrap the App in the ErrorBoundary

* Check for consent before capturing error

* Add context to the error capturing

* Fix tests

* refactor

* Remove `unref`
2026-03-06 20:39:44 +01:00
abeatrix 36ffcb5f44 docs(rpc): document unified chat_event bridge startup flow
Update SDK docs to reflect the latest RPC-backed chat architecture and transport naming:

- Clarify that both `apps/code` and `apps/desktop` now use `clite rpc ensure --json`, set `CLINE_RPC_ADDRESS`, and then register the client.
- Document the move to a single persistent runtime bridge script per app, backed by shared `@cline/rpc` helpers (`runRpcRuntimeCommandBridge` / runtime chat client helpers).
- Update wording from legacy `agent://chunk`-centric streaming to canonical websocket `chat_event` updates (while noting compatibility behavior).
- Add `docs` to `.gitignore` to avoid committing generated/local documentation artifacts.
2026-03-06 11:38:15 -08:00
CandiedUniverse c8f81374a3 Fix flaky hooks tests on Windows (#9690)
* test(hooks): reduce Windows hook-runner launch overhead

test(hooks): make Windows hook tests deterministic

refactor(hooks): use platform-agnostic launcher cache naming

Improvements as per Cline code review feedback

test(hooks): remove direct-node acceleration path and stabilize windows timings

* test(hooks): add deterministic launcher cache concurrency coverage

* Improvements as per Cline code review feedback

* Update src/core/hooks/__tests__/hookprocess.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Improvements as per Greptile feedback

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-06 10:46:34 -08:00
ClineXDiego eb25634bd4 fix(bedrock): handle thinking and redacted_thinking blocks in message conversion and streaming (#9424)
Fixes #9269 - Thinking blocks missing in Bedrock Opus 4.6

Changes:
- Add explicit handling for 'thinking' and 'redacted_thinking' content types
  in formatMessagesForConverseAPI() so they are silently skipped instead of
  triggering 'Unsupported content type: thinking' warnings
- Capture signature from additionalModelResponseFields thinking responses
- Add signature_delta handling in contentBlockDelta for streaming
- Add redacted_thinking block handling in contentBlockStart for streaming
- Extend ContentBlockStart/Delta interfaces with signature and data fields
- Add 'redacted_thinking' and 'document' to SupportedContentType union
- Add tests for thinking/redacted_thinking block filtering in message conversion
2026-03-06 10:41:43 -08:00
Max 1753d9dc15 release changelog and version bump (#9706)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-03-06 09:45:20 -08:00
Saoud Rizwan a562868cb9 feat(cli): add --auto-approve-all flag for interactive mode (#9698) 2026-03-06 02:08:59 -08:00
Saoud Rizwan bf5265758f feat(hooks): add Notification hook for attention boundaries (#9699)
* feat(hooks): add Notification hook for attention and completion

* chore(hooks): use default Notification template

* Revert "chore(hooks): use default Notification template"

This reverts commit 85f4e942fc.

* fix(hooks): escape JSON quotes in bash templates

* Revert "fix(hooks): escape JSON quotes in bash templates"

This reverts commit 29dfb9e01e.

* chore(hooks): preserve single-backslash template escaping

* fix(hooks): keep single-backslash JSON escaping in templates

* Revert "fix(hooks): keep single-backslash JSON escaping in templates"

This reverts commit b4c829489f.

* fix(hooks): keep escaped JSON echo template pattern
2026-03-06 02:07:25 -08:00
Saoud Rizwan 469752a201 feat(cli): add --hooks-dir flag for runtime hook injection (#9658)
* feat(cli): add --hooks-dir flag for runtime hook injection

Adds a --hooks-dir <path> CLI flag that allows passing an additional
hooks directory at spawn time. This enables orchestration tools (like
Kanbanana) to inject per-session lifecycle hooks without mutating
the user's global or workspace hooks directories.

The runtime hooks directory is included alongside existing global
(~/Documents/Cline/Hooks/) and workspace (.clinerules/hooks/)
directories during hook discovery. All hooks from all directories
are merged and run in parallel, so runtime hooks are purely additive.

* fix(cli): initialize runtime hooks before interactive startup
2026-03-05 23:33:41 -08:00
abeatrix 78c35405f2 feat: slack bot example and at mention 2026-03-05 19:15:50 -08:00
abeatrix 1ffe3f6d45 feat: SapAiCoreHandler 2026-03-05 18:56:15 -08:00
abeatrix 809f1da904 feat: add scroll and refactor file size 2026-03-05 18:43:37 -08:00
abeatrix 1834f479c3 feat: user credit balance 2026-03-05 18:22:09 -08:00
Ara 8fb2e8a3aa Adding gpt 5.4 models to chatgpt subscripiton (#9692)
* Adding gpt-5.4 to chatgpt subscription

* Fixing stuff

* Apply suggestion from @greptile-apps[bot]

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-05 18:06:17 -08:00
abeatrix 4ad13d4339 feat: rpc event stream with ui bridge 2026-03-05 17:47:16 -08:00
abeatrix 018c738c32 feat: wire Code app with rpc server 2026-03-05 14:49:32 -08:00
ClineXDiego aef32f52f9 fix: bypass git hooks on checkpoint initial commit (#9688)
Add --no-verify to the initial checkpoint commit in
CheckpointGitOperations.ts. This was already used for subsequent
commits in CheckpointTracker.ts but was missing from the initial
empty commit, causing Cline to fail to initialize when users have
global pre-commit hooks (e.g., conventional commits enforcement).

Fixes #9672
2026-03-05 19:02:00 -03:00
abeatrix d2b40b2e17 refactor: restructure packages 2026-03-05 13:42:54 -08:00
abeatrix bb317eff9a refactor: move storage layer out of agents 2026-03-04 23:09:36 -08:00
abeatrix c41f10f272 feat: Settings View in Code App 2026-03-04 17:40:10 -08:00
Dominic Cooney 2baf966db7 fix: handle streamableHttp reconnects and preserve OAuth redirect URIs across sessions (#9642)
- Make redirect URIs with dynamic ports valid, or reregister.
- Handle reconnects for streaming HTTP MCP servers.
2026-03-05 09:00:20 +09:00
abeatrix b93d353aee refactor: cli code split 2026-03-04 15:33:15 -08:00
abeatrix f53b6966eb feat: codex, claude code, opencode, vertex providers 2026-03-04 15:13:29 -08:00
alex-lum 152ba674da feat: add OTel tracking for AI output line/file metrics (lines added/deleted/changed, files created/deleted/moved) (#9562)
* feat: add telemetry for AI output accepted/rejected across tool handlers

Add line-level diff stats and file operation tracking to telemetry
events when users accept or reject tool outputs. Introduces a shared
`computeLineDiffStats` utility and `captureAiOutputAccepted`/
`captureAiOutputRejected` methods on the telemetry service, wired
into ApplyPatch, WriteToFile, ExecuteCommand, InsertContent, and
SearchAndReplace handlers.

* feat(telemetry): add source tracking for agent vs human edits

Add telemetry differentiation between agent-generated changes and
human modifications to capture more granular edit metrics:

- Add 'source' field to captureAiOutputAccepted telemetry events
- Track human edits by computing diff stats between agent's proposed
  content and final saved content
- Apply source tracking to ApplyPatchHandler and WriteToFileToolHandler
- Enable separate analytics for agent vs human contributions

This allows measuring how often and to what extent users modify
AI-generated code, providing insights into AI output quality and
user trust patterns.

* refactor(telemetry): centralize ai output attribution across file edit handlers

- add shared `AiOutputTelemetry` utility for accepted/rejected events
- refactor `WriteToFileToolHandler` and `ApplyPatchHandler` to use shared helpers
- preserve existing telemetry behavior (`source: "agent" | "human"`) while reducing duplication
- keep line diff/file-op attribution semantics unchanged

* fix(telemetry): use pre-save content for human edit line diff stats

The source:"human" telemetry was diffing agent content against
finalContent (post-save), which includes auto-formatting changes
from the editor. This inflated linesChanged/linesDeleted counts
when the formatter modified lines alongside the user's actual edits.

Use diff.applyPatch() to reconstruct the user's pre-save content
from the existing userEdits patch, excluding formatter noise from
the line diff stats.

* fixing syntax error

* refactor(telemetry): make next-hunk bounds check explicit

* remove comment

Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>

---------

Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>
2026-03-04 14:45:57 -08:00
Max 12f5dc2e9e update changelog and bump version numbers (#9664)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-03-04 13:39:15 -08:00
abeatrix 71f5beaf2f refactor: use ai sdk for vertex 2026-03-04 12:55:17 -08:00
abeatrix 4738e82948 chore: clean up scripts and shared 2026-03-04 12:09:51 -08:00
Saoud Rizwan 28d806a4e4 fix(openrouter): stop sending max_tokens in stream requests (#9634) 2026-03-04 18:48:59 +01:00
Ara e92c7de8c0 Restrict /test-jetbrains workflow trigger to authorized users (#9657)
Add author_association check so only MEMBER, OWNER, and COLLABORATOR
users can trigger the JetBrains test workflow via issue comments.
Previously any GitHub user could trigger it, allowing unauthorized
use of the GitHub App token and Actions minutes.

Fixes GHSA-5fq9-fh5x-w83r (SEC-29)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 09:13:41 -08:00
abeatrix f604679eee feat: Auth for Cline, OCA, Codex & private models 2026-03-04 01:47:32 -08:00
abeatrix 32c34e1d57 chore: biome fix 2026-03-03 23:40:01 -08:00
abeatrix 51c6d30ffa feat: hooks 2026-03-03 23:26:58 -08:00
abeatrix 0b5ce05359 chore: pre-commit hook set up 2026-03-03 23:07:12 -08:00
abeatrix ec9815f49a fix: Update Editor Tool and set biome rules 2026-03-03 22:39:59 -08:00
abeatrix 8fa4920291 feat: gRpc server 2026-03-03 19:20:14 -08:00
CandiedUniverse 718e5b53f6 Add model identifier to the JSON payload that hooks receive (#9646)
* Add provider/model context to all hook payloads

* Fixes as per Greptile feedback

* Further fixes as per Greptile feedback

* Further fixes as per Greptile feedback

* further fixes as per Greptile feedback

* Fix flapping hooks tests on Windows
2026-03-03 19:08:41 -08:00
abeatrix f898a969c5 feat: code app 2026-03-03 16:27:31 -08:00
abeatrix 5499dafd86 chore: folder structure update 2026-03-03 16:15:24 -08:00
abeatrix ce34f1f74a feat: ask follow up question tool and clean up 2026-03-03 16:06:12 -08:00
Saoud Rizwan e484534bcb fix(task): process usage chunks independently from non-usage stream flow (#9576) 2026-03-03 12:27:20 -08:00
Dominic Cooney 2822723495 fix: stop infinite getLatestMcpServers RPC loop when opening MCP servers panel (#9643)
* fix: stop infinite getLatestMcpServers RPC loop when opening MCP servers panel

The ServersToggleModal useEffect had setMcpServers in its dependency array,
but the context provided an unstable inline wrapper around the useState setter,
creating a new function reference on every render. This caused the effect to
re-fire on every context re-render while the modal was visible, producing 14+
RPC calls in ~15ms.

- Remove setMcpServers from useEffect deps in ServersToggleModal (the effect
  should only fire when visibility changes)
- Replace inline arrow wrappers in ExtensionStateContext with direct references
  to the stable useState setters (setMcpServers, setRequestyModels,
  setHuggingFaceModels, setMcpMarketplaceCatalog)

* address review feedback: add setMcpServers back to deps, use property shorthand

- Add setMcpServers back to useEffect deps in ServersToggleModal now that the
  context passes the stable useState setter directly (per Copilot review)
- Remove eslint-disable comment since it's no longer needed
- Use property shorthand for setGroqModels and setBasetenModels in context value
2026-03-03 09:08:34 -08:00
abeatrix 17d3235187 wip feat: support Rules, Skills, Workflows 2026-03-02 23:45:07 -08:00
Saoud Rizwan b5c0cc18d8 fix(subagent): fix subagents erroring out by handling context-window limit errors (#9637)
* fix(subagent): align error handling with main loop behavior

* fix(subagent): persist context truncation state across retries
2026-03-02 22:13:32 -08:00
Dominic Cooney fb04a9bd15 Log RPC size histogram. (#9626) 2026-03-03 14:53:30 +09:00
abeatrix 53dae9dff8 refactor: simplify model info conversion logic 2026-03-02 18:45:35 -08:00
abeatrix 9b958ae27d chore: add comprehensive test suite for agent, tools, and retry logic
Introduces unit tests across multiple modules in the agents package:

- `agent.test.ts`: Tests for Agent class covering single-turn runs,
  tool call execution, tool policy approval, max iteration limits,
  and event emission behavior
- `retry.test.ts`: Tests for `retryAsync` and `withRetry` decorator
  covering successful retries, retry-after delays, non-retriable
  errors, and async generator retry preservation
2026-03-02 17:43:34 -08:00
abeatrix 7fb2da07ad feat: add sandbox support 2026-03-02 17:31:51 -08:00
abeatrix e5d6ccf0ae feat: vertex provider 2026-03-02 17:19:25 -08:00
abeatrix 1e41e73565 refactor: enforce workspace import boundaries across packages
Replace deep subpath imports (e.g. `@cline/llms/providers`,
`@cline/llms/models`) with top-level package imports (`@cline/llms`)
to comply with the new cross-workspace import boundary policy.

- Update all internal usages of `@cline/llms/providers` and
  `@cline/llms/models` to access exports via the package root
  (e.g. `providers.getLiveModelsCatalog`, `models.CLINE_MODELS`)
- Document allowed vs. disallowed cross-workspace imports in README
- Fix stale local file paths in agents/ARCHITECTURE.md
- Add reference to `bun run check:boundaries` enforcement command
2026-03-02 17:03:09 -08:00
abeatrix 31af503851 feat(cli,core): integrate ProviderSettingsManager for provider persistence
Introduce `ProviderSettingsManager` from `@cline/core` into the CLI
startup flow so that provider, model, and API key selections are
persisted to `~/.cline/data/settings/providers.json` and reused across
runs.

- CLI now loads last-used provider settings on startup and applies a
  precedence policy: explicit CLI flags > persisted settings > built-in
  defaults/catalog fallback
- Saves effective provider/model selection after resolution so future
  CLI and desktop runs reuse the same defaults
- Export `ProviderSettingsManager` from `core/src/index.ts`
- Update `ARCHITECTURE.md` and `README.md` in `agents` and `cli` to
  document provider settings schema ownership (`@cline/llms/providers`)
  and the new settings manager responsibilities
- Set `CLINE_PROVIDER_SETTINGS_PATH` env var to override storage path
2026-03-02 16:39:49 -08:00
Renee Huang 6def83a5d9 [doc] api doc changes (#9581)
* initial doc changes

* rm general api endpoint

* Add API documentation section with endpoint reference pages

- Add new API docs: overview, getting-started, authentication, models,
  chat-completions, errors, and SDK examples
- Update api/reference.mdx with expanded endpoint documentation
- Update enterprise-solutions/api-reference.mdx with improvements
- Update docs.json with new API section navigation entries

---------

Co-authored-by: Juan Pablo <juan@cline.bot>
Co-authored-by: Tony Loehr <turingxo@gmail.com>
2026-03-02 16:22:39 -08:00
CandiedUniverse fc3d986d05 Release: version bump and changelog updates (#9640)
* Update changelog for release

* Version bump the package*.json files for release
2026-03-02 15:53:06 -08:00
abeatrix 8babb8fe0c refactor: update stream schema 2026-03-02 15:33:13 -08:00
abeatrix 3aed81a096 fix thinking chunk stream 2026-03-02 15:13:37 -08:00
abeatrix 8d5b49e783 feat: support thinking and json 2026-03-02 14:43:06 -08:00
abeatrix 535c5127ae feat: desktop app 2026-03-02 13:21:44 -08:00
abeatrix d656d2d492 fix build versions 2026-03-02 13:14:30 -08:00
abeatrix 78aedcde84 feat: agents, core, cli, llms sdk packages 2026-03-02 12:47:50 -08:00
Saoud Rizwan 520ddb83bb fix(checkpoints): retry nested git restore and prevent silent .git_disabled leftovers (#9620)
* fix(checkpoints): harden nested git repo restore cleanup

* Fix checkpoint initialization to take less time

---------

Co-authored-by: cline-test <132302818+candieduniverse@users.noreply.github.com>
2026-03-02 12:22:01 -08:00
Ara d96c5d4b40 workflow: add default auto-tag flow for Publish Release (#9584)
* workflow: add auto-tag mode to publish release

* workflow: pin auto-tag release to tested commit sha

* workflow: clarify publish release input semantics

* workflow: constrain publish tag input to refs/tags
2026-03-02 11:38:48 -08:00
Br1an e6cbae0edc fix: prevent Chinese filename escaping in diff view (#9612)
* fix: prevent Chinese filename escaping in diff view

Use Uri.parse() instead of Uri.from() for the diff view URI to prevent
non-ASCII characters (e.g. Chinese) in filenames from being
percent-encoded. This is consistent with how other diff URIs are created
in openMultiFileDiff.ts and VscodeCommentReviewController.ts.

Uri.from() encodes the path component, turning Chinese characters into
percent-encoded sequences like %E7%A0%94..., which causes the diff view
to display escaped filenames and fail to open properly.

* fix: encode URI-reserved delimiters in filename before Uri.parse

Encode %, #, and ? in the filename before passing to Uri.parse() to
prevent them from being interpreted as URI delimiters. This handles
edge cases where filenames contain these characters (valid on macOS/Linux)
while preserving non-ASCII characters like Chinese.
2026-03-02 10:35:48 -08:00
Saoud Rizwan 76bd0926e6 fix: trigger auto-compaction on OpenRouter context overflow errors (#9633)
* fix(context): detect wrapped OpenRouter 400 context errors

* fix(openrouter): preserve status for context overflow detection

* docs(context): clarify OpenRouter error-shape handling

* Revert "docs(context): clarify OpenRouter error-shape handling"

This reverts commit 9458d4472b.

* Revert "fix(openrouter): preserve status for context overflow detection"

This reverts commit a2c76d1693.

* Revert "fix(context): detect wrapped OpenRouter 400 context errors"

This reverts commit 3e8fb10b9e.

* Reapply "fix(context): detect wrapped OpenRouter 400 context errors"

This reverts commit 6df27d6853.

* Reapply "docs(context): clarify OpenRouter error-shape handling"

This reverts commit beb52e1429.

* fix(context): narrow OpenRouter status parsing fallback

* fix(context): align OpenRouter status parsing with agreed shape
2026-03-02 10:34:27 -08:00
Renee Huang 819f9ce00d show sdk docs in cline page (#9597) 2026-03-02 10:14:41 -08:00
Max fd8cecddd5 update cline sdk docs (#9532) 2026-02-27 11:28:48 -08:00
CandiedUniverse a502bd8653 WIP: Make hooks work on Windows PowerShell (#9552)
* feat(hooks): add Windows hook execution via PowerShell

* chore(changeset): add release note for Windows hooks

* Get hooks working on Windows

Remove changeset file (we no longer use changeset files)

feat(hooks): support Windows PowerShell hook resolution and management

feat(hooks): complete windows powershell hook support and tests

Detect linux-style hooks only on macOS and linux and detect PowerShell-style hooks only on Windows

Fixes for failing unit tests on Windows in CI

Fix failing unit tests on Windows in CI

Fix unit tests for hooks on Windows

Be clear about .ps1 file extension for hooks in PowerShell vs. bash/binary for linux-style hooks

Remove separate test suite step

Reapply hooks-specific test suite

Fix failing hooks tests

* Harden Windows hook PowerShell runtime and test coverage

* test: centralize hook test env and platform overrides

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-02-27 10:51:46 -08:00
Robin Newhouse d48d5ee74d fix: restore gpt-oss native file editing on OpenAI-compatible models (#9434)
* fix(core): enable gpt-oss native file editing

* test(evals): add gpt-oss openai-compat smoke coverage
2026-02-27 10:18:25 -08:00
Tomás Barreiro cd320ea01f Add User-Agent to requests to the Cline back-end (#9583) 2026-02-26 18:52:31 -08:00
CandiedUniverse 60485277c4 Version bump and changelog for release (#9577) 2026-02-26 15:57:14 -08:00
ClineXDiego 0a8f1ef248 fix: clear all OCA secrets on auth refresh failure to prevent re-auth loop (#9569)
When OCA token refresh fails with 400 invalid_grant or 401, legacy secrets
ocaAccessToken and ocaTokenSet (from older Cline versions) were left in VS
Code's secret storage. clearAuth() only cleared ocaApiKey and ocaRefreshToken,
causing every subsequent re-auth attempt to fail in a loop requiring manual
SQLite deletion to recover.

Fix:
- Add ocaAccessToken and ocaTokenSet to SecretKeys in state-keys.ts
- Update OcaAuthProvider.clearAuth() to clear all 4 OCA secrets

Fixes #9567
2026-02-26 20:12:48 -03:00
ClineXDiego 4b2619daf7 fix: resolve "Could not find the file context" error in Explain Changes (#9449)
* fix: resolve 'Could not find the file context' error in Explain Changes

Both handleCommentReply() in explainChangesShared.ts and the onCommentStart
callback in explainChanges.ts were using a strict absolutePath-only match
when looking up files in changedFiles. If the VS Code comment controller
returns a path in a different format (relative vs absolute, different
separators on Windows), the lookup would silently fail and show
'Error: Could not find the file context'.

Add relativePath as a fallback in both lookup sites, making them
consistent with the already-correct logic in streamAIExplanationComments.

Fixes #9382

* Refactor to use parseInt instead of Number.parseInt
2026-02-26 20:12:37 -03:00
Ara 913cf4b74d feat: add dynamic Cline provider model fetching from Cline endpoint (#9102)
* Adding 1m

* fix: wire cline model proto fields for api config

* fix: wire cline picker to shared recommended model logic

* fix: address Cline model picker parity and startup model-info sync

* remove OpenRouter preset model ID support

* rename Cline endpoint feature flag

* Fixing stuff

* Fixing stuff

* Fixing stuff

* refactor: gate cline models endpoint behind feature flag

- Update refreshClineModels to use the EXTENSION_CLINE_MODELS_ENDPOINT feature flag instead of a hardcoded boolean, allowing controlled rollouts of the endpoint source.
- Remove recommended/free models fallback logic, featured model cards, and the initialTab property from OpenRouterModelPicker to simplify the UI component.
2026-02-26 14:58:45 -08:00
Robin Newhouse 8e5be3f648 fix: use JSON_SCHEMA for yaml.load to prevent unsafe deserialization (#9500)
* fix: use JSON_SCHEMA for yaml.load to prevent unsafe deserialization

Add { schema: yaml.JSON_SCHEMA } to both yaml.load() calls to reject
custom YAML tags (e.g. !!js/function) that could enable code execution
from untrusted .clinerules or skills files.

Add security tests verifying custom tags are rejected.

* add changeset
2026-02-26 14:48:58 -08:00
CandiedUniverse 6c519ff6e5 Increase timeout of flaky test; this is not the long term solution but it will be quick and easy today (#9568) 2026-02-26 12:45:18 -08:00
Robin Newhouse c61f9a9394 fix: fetch model info from API in CLI headless auth for Cline and Vercel providers (#9547)
The CLI's applyProviderConfig() was reading model info from a disk
cache (controller.readOpenRouterModels) instead of fetching from
the provider API. In headless/Docker environments (e.g., terminal-bench)
the cache doesn't exist, so model info was never set. Both handlers
then fell back to openRouterDefaultModelInfo with maxTokens: 8192,
causing write_to_file truncation on large outputs.

Changes:
- Replace controller.readOpenRouterModels() (disk cache) with
  refreshOpenRouterModels() (fetches from API, with cache fallback)
- Add vercel-ai-gateway to the model info fetch path using
  refreshVercelAiGatewayModels()

Relates to #7998

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-26 12:42:34 -08:00
Raushan Singh fa53f301a4 fix: generate commit message from staged changes only when staging exists (#9529)
The "Generate Commit Message" feature was using all changes instead of
only staged changes. Now prioritizes staged changes via getGitDiffStagedFirst(),
falling back to all changes only when nothing is staged.

Closes #5749

Co-authored-by: Raushan Singh <raushrak@Raushans-MacBook-Air.local>
2026-02-26 18:53:33 +01:00
Robin Newhouse c36e375af5 fix: update stale maxTokens values for Claude 3.7+ models across Anthropic, Bedrock, Vertex, and SAP AI Core (#9545)
* fix: update stale maxTokens values for Claude 3.7+ models

Every Claude model from 3.7 Sonnet onward had maxTokens set to 8192
in the static model definitions. These values were correct for Claude
3.5 and earlier, but Anthropic has significantly increased output
limits for newer models:

- Claude Opus 4.6: 128K (was 8192, 15.6x too low)
- Claude 3.7 Sonnet: 128K (was 8192, 15.6x too low)
- Claude Sonnet 4.6/4.5/4, Haiku 4.5, Opus 4.5: 64K (was 8192)
- Claude Opus 4, Opus 4.1: 32K (was 8192)

These static definitions are the source of truth for Anthropic direct,
Bedrock, Vertex, and SAP AI Core providers. With the old values, any
write_to_file call exceeding 8192 output tokens would be silently
truncated, producing a missing 'content' parameter error.

Values verified against Anthropic docs, AWS Bedrock docs, Google
Vertex AI docs, and the Vercel AI Gateway API.

Relates to #7998

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: update openRouterDefaultModelInfo.maxTokens to 64K

This fallback ModelInfo (representing claude-sonnet-4.5) is used
when dynamic model info isn't available — notably by the Cline and
Vercel providers in the CLI when the model cache is empty (e.g.,
fresh Docker containers in terminal-bench).

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-26 09:44:25 -08:00
Robin Newhouse 94b02bf052 fix: use model.info.maxTokens for OpenRouter instead of hardcoded 8192 (#9544)
The OpenRouter stream transform had a 30-line switch statement that
hardcoded max_tokens=8192 for every Claude model. This was written when
8192 was the actual max output for Claude, but modern Claude models
support much higher limits (e.g. 128K for Sonnet 4.6, 64K for others).

OpenRouter's API already reports the correct max_completion_tokens per
model, and model.info.maxTokens reflects this (128000 for Sonnet 4.6).
The hardcoded switch was silently overriding the dynamic value.

This caused write_to_file failures on OpenRouter (and the Cline
provider, which shares this code path) whenever the tool call content
exceeded 8192 output tokens. The response was truncated
(finish_reason: "length"), producing incomplete JSON that lost the
content parameter.

Runtime evidence:
- Before: max_tokens=8192 sent, completion_tokens=8192 (ceiling),
  finish_reason="length", write_to_file content missing
- After: max_tokens=128000 sent, completion_tokens=9824 (needed more
  than 8192), finish_reason="tool_calls", write_to_file succeeded

Fixes #7998

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-25 21:57:30 -08:00
shey-cline f10b6f39be Add Additional Markdown Formatting in CLI (#9392) 2026-02-24 14:43:53 -08:00
shey-cline 42e6a24d0f Add Focus Indicator on Action Buttons in Extension (#9487) 2026-02-24 14:43:16 -08:00
CandiedUniverse 452733c3fd Release changeset PR (#9528)
* Update package.json and package-lock.json version numbers for patch release

* Patch fix changeset PR

* update cli package.json

* fixup! Patch fix changeset PR

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-02-24 14:10:39 -08:00
Juan Pablo Flores 7fadcfaa3f feat: add thinking to MiniMax M2.5 and add the M2.5-highspeed model to MiniMax provider (#9394)
* feat: add MiniMax M2.5 model to MiniMax provider

- Add MiniMax-M2.5 to minimaxModels with 192K context, 128K max tokens,
  prompt caching, and reasoning/thinking support
- Update minimaxDefaultModelId to MiniMax-M2.5
- Add minimax/minimax-m2.5 to OpenRouter prompt caching switch

Closes #9391

* fix: add temperature: 1 to MiniMax M2.5 for reasoning support

* docs: update MiniMax provider docs with M2.5 model

* feat: wire up thinking/reasoning support for MiniMax M2.5

- Pass thinkingBudgetTokens from factory to MinimaxHandler
- Use thinking param in API call when reasoning is enabled
- Disable temperature and forced tool_choice when thinking is on
- Add ThinkingBudgetSlider to MiniMaxProvider UI for M2.5

* Add MiniMax-M2.5-highspeed

* Add thinking for highspeed

* Refactor thinking logic

---------

Co-authored-by: BarreiroT <tomasmbarreiroi@gmail.com>
Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>
2026-02-24 22:24:41 +01:00
Tomás Barreiro 9989225b69 Parse CLINE_OTEL_EXPORTER_OTLP_HEADERS headers and force the build constant ones (#9534) 2026-02-24 22:07:55 +01:00
Ara 8a73f63189 feat: update recommended model from GPT-5.2 Codex to GPT-5.3 Codex (#9533)
- Update model ID and name from gpt-5.2-codex to gpt-5.3-codex
- Change tag from "HOT" to "NEW" for the updated model
- Add What's New banner entry promoting Codex 5.3 availability
2026-02-24 12:49:20 -08:00
Ara 31e8c85f0a Remove voice mode UI and disable dictation (#9511)
* remove voice mode UI and disable dictation flags

* remove legacy dictation settings path and dead voice recorder

* remove dictation feature stack and state/proto hooks
2026-02-24 12:26:15 -08:00
Bee 5b9916866d fix: remove placeholder tools from final native tool list (#9499)
This commit ensures that internal placeholder tools, specifically `focus_chain`, are filtered out from the final list of native tools exposed to the LLM.

- Added a test case in `PromptRegistry.test.ts` to verify `focus_chain` is excluded from native tools output.
- Updated snapshot files for various models (OpenAI GPT-5, Vertex Gemini 3, etc.) to reflect the removal of the `focus_chain` tool definition.
2026-02-24 12:07:55 -08:00
Tomás Barreiro f001e735f8 Add isLocatedInPath tests (#9526)
* Add isLocatedInPath tests

* Add another test case
2026-02-24 19:22:55 +01:00
Tomás Barreiro 32893ee343 Fix OpenAI Codex by setting Store to false (#9523) 2026-02-24 18:28:32 +01:00
Raushan Singh 0d4e47e5c3 fix: use isLocatedInPath() instead of string.includes() for path containment check (#9519)
Fixes false positives in getReadablePath() when directories share a prefix
(e.g., /home/user/project matching /home/user/project-backup). The existing
isLocatedInPath() function correctly handles path boundaries using path.relative().

Closes #8761

Co-authored-by: Raushan Singh <raushrak@Raushans-MacBook-Air.local>
2026-02-24 18:23:29 +01:00
Max c1a43482e7 sdk lib (#9259)
* sdk lib

* improve cline sdk api surface

- better api design and messages

* fix some types, fix session id retrieval, improve wording

* hide controller from sdk surface completely

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-02-23 22:30:54 -08:00
4827 changed files with 887240 additions and 600291 deletions
+208
View File
@@ -0,0 +1,208 @@
---
name: cline-sdk
description: Comprehensive Cline SDK skill for building AI agents. Covers the Agent runtime, ClineCore sessions, custom tools, plugins, events, LLM providers, scheduling, multi-agent teams, and production deployment. Use for any task involving @cline/sdk or its sub-packages.
metadata:
references: agent, clinecore
---
# Cline SDK Skill
Consolidated skill for building AI agents with the Cline SDK. Use the decision trees below to find the right entry point and API surface, then load detailed references.
## Critical Rules
Follow these rules in all Cline SDK code:
1. Install with `npm install @cline/sdk`. The `@cline/sdk` package re-exports everything from `@cline/core`, `@cline/agents`, `@cline/llms`, and `@cline/shared`.
2. Requires Node.js 22 or later.
3. Use `createTool()` from `@cline/sdk` (or `@cline/shared`) to define tools. Tool names must be `snake_case`.
4. Return errors as structured data from tool `execute` functions. Throwing counts as a "mistake" against the agent's mistake limit.
5. Use `lifecycle: { completesRun: true }` on tools that should end the agent loop (e.g. a "submit answer" tool).
6. When using `ClineCore`, always call `dispose()` when done to clean up resources.
7. The standalone `Agent` and `ClineCore` have different event systems. For `Agent`: use `agent.subscribe()` to get `AgentRuntimeEvent` types (text streaming is `"assistant-text-delta"`, result text is `result.outputText`). For `ClineCore`: use `cline.subscribe()` to get `CoreSessionEvent` types (text streaming is `"chunk"` with `payload.type === "text"`, result text is `result.text`). There is no top-level `onEvent` field on `AgentRuntimeConfig` -- use `agent.subscribe()` or `hooks.onEvent` instead. Do not use event types like `"content_update"` or `"content_start"` with `agent.subscribe()` -- those are internal legacy types from the ClineCore adapter layer.
## How to Use This Skill
### Reference File Structure
The two main API surfaces (`Agent` and `ClineCore`) follow a 4-file pattern. Cross-cutting concepts are single-file guides.
Each main API surface in `./references/<api>/` contains:
| File | Purpose | When to Read |
|------|---------|--------------|
| `REFERENCE.md` | Overview, when to use, quick start | Always read first |
| `api.md` | Full API: classes, methods, config, types | Writing code |
| `patterns.md` | Common patterns, best practices | Implementation guidance |
| `gotchas.md` | Pitfalls, limitations, debugging | Troubleshooting |
Cross-cutting concepts in `./references/<concept>/` have `REFERENCE.md` as the entry point.
### Reading Order
1. Start with `REFERENCE.md` for your chosen API surface
2. Then read additional files relevant to your task:
- Writing agent code -> `api.md`
- Common patterns -> `patterns.md`
- Creating tools -> `tools/REFERENCE.md`
- Adding plugins/hooks -> `plugins/REFERENCE.md`
- Configuring LLM providers -> `providers/REFERENCE.md`
- Streaming events -> `events/REFERENCE.md`
- Deploying to production -> `production/REFERENCE.md`
- Scheduling agents -> `scheduling/REFERENCE.md`
- Multi-agent orchestration -> `multi-agent/REFERENCE.md`
- Debugging -> `gotchas.md`
### Example Paths
```
./references/agent/REFERENCE.md # Start here for lightweight agents
./references/clinecore/REFERENCE.md # Start here for full runtime
./references/agent/api.md # Agent class, config, methods
./references/tools/REFERENCE.md # Creating and using tools
./references/plugins/REFERENCE.md # Plugin system
./references/providers/REFERENCE.md # LLM provider configuration
```
## Quick Decision Trees
### "Which API surface should I use?"
```
Which API?
+-- I want a simple, stateless agent with custom tools
| +-- agent/ (Agent class from @cline/agents)
+-- I need session persistence, built-in tools, config discovery
| +-- clinecore/ (ClineCore from @cline/core)
+-- I want built-in file/shell/search/web tools
| +-- clinecore/ (has built-in tools; Agent does not)
+-- I want scheduled or recurring agents
| +-- clinecore/ (automation API)
+-- I need multi-process or multi-client session sharing
| +-- clinecore/ (hub-backed runtime)
+-- I'm building a browser-compatible agent
| +-- agent/ (no Node.js dependencies)
```
### "I need to create tools"
```
Tools?
+-- Define a custom tool with schema -> tools/REFERENCE.md
+-- Use built-in tools (bash, editor, read_files) -> tools/REFERENCE.md (built-in section)
+-- Control tool approval/policies -> tools/REFERENCE.md (policies section)
+-- Tool that ends the agent loop -> tools/REFERENCE.md (completion tools)
+-- Package tools as a reusable plugin -> plugins/REFERENCE.md
```
### "I need to handle events"
```
Events?
+-- Stream text/reasoning in real time -> events/REFERENCE.md
+-- Track token usage and costs -> events/REFERENCE.md
+-- Watch tool calls -> events/REFERENCE.md
+-- Detect completion/errors -> events/REFERENCE.md
+-- Hook into lifecycle stages -> plugins/REFERENCE.md
```
### "I need to configure a model provider"
```
Providers?
+-- Anthropic (Claude) -> providers/REFERENCE.md
+-- OpenAI (GPT) -> providers/REFERENCE.md
+-- Google (Gemini/Vertex) -> providers/REFERENCE.md
+-- AWS Bedrock -> providers/REFERENCE.md
+-- Mistral -> providers/REFERENCE.md
+-- OpenAI-compatible (vLLM, Together, etc.) -> providers/REFERENCE.md
+-- Custom/self-hosted provider -> providers/REFERENCE.md
```
### "I need plugins or hooks"
```
Plugins?
+-- Package tools + hooks together -> plugins/REFERENCE.md
+-- Observe tool calls (logging, metrics) -> plugins/REFERENCE.md
+-- Intercept lifecycle events -> plugins/REFERENCE.md
+-- Add system prompt rules -> plugins/REFERENCE.md
+-- Distribute via npm/git -> plugins/REFERENCE.md
```
### "I need multi-agent coordination"
```
Multi-agent?
+-- Spawn one-off background agents -> multi-agent/REFERENCE.md (sub-agents)
+-- Persistent cross-session teams -> multi-agent/REFERENCE.md (teams)
+-- Parent-child delegation -> multi-agent/REFERENCE.md (sub-agents)
+-- Peer-to-peer task board -> multi-agent/REFERENCE.md (teams)
```
### "I need scheduling or automation"
```
Scheduling?
+-- Recurring cron jobs -> scheduling/REFERENCE.md
+-- One-off scheduled tasks -> scheduling/REFERENCE.md
+-- Event-driven triggers -> scheduling/REFERENCE.md
+-- CLI schedule management -> scheduling/REFERENCE.md
```
### "I need to go to production"
```
Production?
+-- Error handling and status checks -> production/REFERENCE.md
+-- Cost control and token limits -> production/REFERENCE.md
+-- Observability (OpenTelemetry) -> production/REFERENCE.md
+-- Security and sandboxing -> production/REFERENCE.md
+-- Deployment patterns -> production/REFERENCE.md
```
### Troubleshooting Index
- Agent loop not stopping -> `tools/REFERENCE.md` (completion tools)
- Tool errors crashing the agent -> `agent/gotchas.md` or `clinecore/gotchas.md`
- Provider auth failures -> `providers/REFERENCE.md`
- Session not persisting -> `clinecore/gotchas.md`
- Token usage too high -> `production/REFERENCE.md` (cost control)
- Hub connection issues -> `clinecore/gotchas.md`
- Plugin not loading -> `plugins/REFERENCE.md`
- Events not firing -> `events/REFERENCE.md`
## Product Index
### API Surfaces
| API | Entry File | Description |
|-----|------------|-------------|
| Agent | `./references/agent/REFERENCE.md` | Lightweight stateless agent loop |
| ClineCore | `./references/clinecore/REFERENCE.md` | Full runtime with sessions, persistence, built-in tools |
### Cross-Cutting Concepts
| Concept | Entry File | Description |
|---------|------------|-------------|
| Tools | `./references/tools/REFERENCE.md` | Built-in and custom tool creation |
| Plugins | `./references/plugins/REFERENCE.md` | Extension system with hooks |
| Events | `./references/events/REFERENCE.md` | Real-time streaming events |
| Providers | `./references/providers/REFERENCE.md` | LLM provider configuration |
| Production | `./references/production/REFERENCE.md` | Deployment, security, observability |
| Scheduling | `./references/scheduling/REFERENCE.md` | Cron jobs and automation |
| Multi-Agent | `./references/multi-agent/REFERENCE.md` | Teams and sub-agents |
### Package Map
| Package | Purpose |
|---------|---------|
| `@cline/sdk` | Everything you need, install this one |
| `@cline/core` | Sessions, persistence, built-in tools, config, hub |
| `@cline/agents` | Stateless agent loop, tool orchestration, streaming |
| `@cline/llms` | LLM provider gateway |
| `@cline/shared` | Types, tool helpers, hook engine |
## Resources
Repository: https://github.com/cline/cline
SDK Source: https://github.com/cline/cline/tree/main/sdk
Documentation: https://docs.cline.bot/sdk/overview
Discord: https://discord.gg/cline
@@ -0,0 +1,107 @@
# Agent Runtime
The `Agent` class (also exported as `AgentRuntime`) is the lightweight, stateless agent loop from `@cline/agents`. It handles the core iteration cycle: send messages to an LLM, execute tool calls, collect results, and repeat until the task is done.
## When to Use Agent
| Use Agent when... | Use ClineCore instead when... |
|---|---|
| You want a simple agent with custom tools | You need built-in tools (bash, editor, etc.) |
| You want minimal dependencies | You need session persistence |
| You need browser compatibility | You need config discovery from `.cline/` |
| You're building a stateless worker | You need multi-process session sharing |
| You want full control over the runtime | You want batteries-included setup |
## Quick Start
```typescript
import { Agent } from "@cline/sdk"
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
systemPrompt: "You are a helpful assistant.",
tools: [],
})
const result = await agent.run("What is the capital of France?")
console.log(result.outputText)
```
## Core Concepts
The Agent operates in a loop:
1. Accept user input (string, message, or array of messages)
2. Build turn context (system prompt, messages, tools)
3. Call the LLM provider
4. If the model returns tool calls, execute them and loop back to step 3
5. If the model returns text without tool calls, the run completes
6. Emit events throughout for streaming
The agent is stateless in the sense that it does not persist anything to disk. Conversation history is held in memory and can be accessed via `snapshot()`.
## Key APIs
- `new Agent(config)` or `createAgent(config)` - Create an agent
- `agent.run(input)` - Start a run with user input
- `agent.continue(input?)` - Continue an existing conversation
- `agent.abort(reason?)` - Cancel an active run
- `agent.subscribe(listener)` - Listen to streaming events
- `agent.snapshot()` - Get current runtime state
- `agent.restore(messages)` - Replace message history
See `api.md` for full API details.
## Multi-Turn Conversations
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "You are a helpful assistant.",
tools: [],
})
const first = await agent.run("What is 2 + 2?")
console.log(first.outputText)
const second = await agent.continue("Now multiply that by 3")
console.log(second.outputText)
```
Use `agent.hasRun` to check if a run has already been executed, which determines whether to call `run()` or `continue()`.
## Event Streaming
Use `agent.subscribe()` to stream events in real time. Register the listener before calling `run()` to avoid missing early events.
There is no top-level `onEvent` field on the Agent config. For an async alternative, use `hooks.onEvent` (see `api.md` and `gotchas.md`).
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "You are a helpful assistant.",
tools: [],
})
agent.subscribe((event) => {
if (event.type === "assistant-text-delta") {
process.stdout.write(event.text)
}
})
const result = await agent.run("What is the capital of France?")
```
See `events/REFERENCE.md` for the full event type catalog.
## Next Steps
- `api.md` - Full Agent API reference
- `patterns.md` - Common patterns and best practices
- `gotchas.md` - Pitfalls and debugging
- `../tools/REFERENCE.md` - Creating custom tools
- `../events/REFERENCE.md` - Event system details
- `../providers/REFERENCE.md` - Provider configuration
@@ -0,0 +1,231 @@
# Agent API Reference
## Constructor
```typescript
import { Agent } from "@cline/sdk"
const agent = new Agent(config: AgentRuntimeConfig)
```
Also available via factory function:
```typescript
import { createAgent } from "@cline/sdk"
const agent = createAgent(config)
```
## AgentRuntimeConfig
Two config forms exist as a discriminated union:
### With Provider ID (recommended)
```typescript
interface AgentRuntimeConfigWithProvider {
providerId: string // e.g. "anthropic", "openai", "gemini"
modelId: string // e.g. "claude-sonnet-4-6", "gpt-5.5"
apiKey?: string // provider API key
baseUrl?: string // custom endpoint
headers?: Record<string, string>
systemPrompt?: string
tools?: AgentTool[]
initialMessages?: AgentMessage[]
toolPolicies?: Record<string, ToolPolicy>
hooks?: Partial<AgentRuntimeHooks>
plugins?: AgentPlugin[]
}
```
### With Pre-built Model
```typescript
interface AgentRuntimeConfigWithModel {
model: AgentModel // pre-built model from gateway
systemPrompt?: string
tools?: AgentTool[]
initialMessages?: AgentMessage[]
toolPolicies?: Record<string, ToolPolicy>
hooks?: Partial<AgentRuntimeHooks>
plugins?: AgentPlugin[]
}
```
Note: there is no top-level `onEvent` field on `AgentRuntimeConfig`. For event streaming, use `agent.subscribe()` or `hooks.onEvent` (see AgentRuntimeHooks below).
## Methods
### run(input)
Start the agent with user input. Returns when the agent loop completes.
```typescript
const result: AgentRunResult = await agent.run("Build a REST API")
```
Input can be a string, an `AgentMessage`, or an array of `AgentMessage[]`.
### continue(input?)
Continue an existing conversation with optional new input.
```typescript
const result = await agent.continue("Now add authentication")
```
### abort(reason?)
Cancel the currently active run.
```typescript
agent.abort("User cancelled")
```
### subscribe(listener)
Register a listener for streaming events.
```typescript
const unsubscribe = agent.subscribe((event: AgentRuntimeEvent) => {
// handle event
})
// Later: stop listening
unsubscribe()
```
### snapshot()
Get the current runtime state including message history.
```typescript
const state: AgentRuntimeStateSnapshot = agent.snapshot()
```
### restore(messages)
Replace the agent's message history.
```typescript
agent.restore(previousMessages)
```
### hasRun
Boolean property indicating whether `run()` has been called at least once.
```typescript
if (agent.hasRun) {
await agent.continue(input)
} else {
await agent.run(input)
}
```
## AgentRunResult
Returned by `run()` and `continue()`.
```typescript
interface AgentRunResult {
agentId: string
agentRole?: string
runId: string
status: "completed" | "aborted" | "failed"
iterations: number
outputText: string
messages: readonly AgentMessage[]
usage: AgentUsage
error?: Error
}
```
### Status Values
- `"completed"` - Agent finished normally
- `"aborted"` - Cancelled via `abort()`
- `"failed"` - Unrecoverable error
## AgentMessage
```typescript
interface AgentMessage {
id: string
role: "user" | "assistant" | "tool"
content: AgentMessagePart[]
createdAt: number
metadata?: Record<string, unknown>
modelInfo?: { id: string; provider: string; family?: string }
metrics?: {
inputTokens: number
outputTokens: number
cacheReadTokens?: number
cacheWriteTokens?: number
cost?: number
}
}
```
## AgentUsage
```typescript
interface AgentUsage {
inputTokens: number
outputTokens: number
cacheReadTokens: number
cacheWriteTokens: number
totalInputTokens: number
totalOutputTokens: number
totalCost?: number
}
```
## AgentRuntimeHooks
```typescript
interface AgentRuntimeHooks {
beforeRun?(context): AgentStopControl | undefined
afterRun?(context): void
beforeModel?(context): AgentBeforeModelResult | undefined
afterModel?(context): AgentStopControl | undefined
beforeTool?(context): AgentBeforeToolResult | undefined
afterTool?(context): AgentAfterToolResult | undefined
onEvent?(event: AgentRuntimeEvent): void | Promise<void>
}
```
Hooks can intercept and modify behavior at each stage. Return a stop control from `beforeRun`, `afterModel`, or `beforeTool` to halt the agent loop.
`hooks.onEvent` receives the same `AgentRuntimeEvent` types as `agent.subscribe()`, but hook callbacks are awaited (can be async), while `subscribe()` listeners are called synchronously. Use `subscribe()` for UI streaming and `hooks.onEvent` for async side effects like logging to an external service.
## AgentRuntimeStateSnapshot
```typescript
interface AgentRuntimeStateSnapshot {
messages: readonly AgentMessage[]
usage: AgentUsage
iterations: number
status: string
}
```
## Factory: createAgentRuntime
Lower-level factory that returns the same `Agent` class:
```typescript
import { createAgentRuntime } from "@cline/sdk"
const runtime = createAgentRuntime(config)
```
## See Also
- `REFERENCE.md` - Overview and quick start
- `patterns.md` - Common patterns
- `../tools/REFERENCE.md` - Tool creation
- `../events/REFERENCE.md` - Event types
- `../providers/REFERENCE.md` - Provider setup
@@ -0,0 +1,134 @@
# Agent Gotchas
## Agent Loop Never Stops
If the agent keeps iterating without completing:
- Make sure at least one tool has `lifecycle: { completesRun: true }` if you want the agent to explicitly finish.
- Without any tools, the agent will complete after the model returns text without tool calls.
- If using tools, ensure the system prompt guides the model toward calling the completion tool when done.
- Check that `completesRun` tools return successfully (not throwing errors).
## Tool Errors Count as Mistakes
When a tool's `execute` function throws an exception, the SDK counts it as a "mistake." After too many mistakes, the agent stops with a `mistake_limit` finish reason.
Instead, return errors as structured data:
```typescript
// Bad: throwing
execute: async (input) => {
throw new Error("File not found")
}
// Good: returning error data
execute: async (input) => {
return { error: "File not found", path: input.path }
}
```
## run() vs continue()
- Call `run()` for the first interaction. It sets up the conversation.
- Call `continue()` for subsequent messages. It appends to the existing conversation.
- Calling `run()` a second time resets the conversation history.
- Use `agent.hasRun` to check which method to call.
## Browser Compatibility
`@cline/agents` (and by extension, the `Agent` class) is browser-safe with no Node.js dependencies. However, `@cline/core` and `ClineCore` require Node.js 22+. If you import from `@cline/sdk`, you get everything including the Node-only code. For browser usage, import directly from `@cline/agents`:
```typescript
import { Agent } from "@cline/agents"
```
## No Top-Level onEvent on Agent Config
`AgentRuntimeConfig` does not have a top-level `onEvent` field. Passing `onEvent` to `new Agent({ onEvent: ... })` has no effect. There are two ways to receive events:
```typescript
// Option 1: subscribe() - synchronous, best for UI streaming
const agent = new Agent({ ...config })
agent.subscribe((event) => {
if (event.type === "assistant-text-delta") {
process.stdout.write(event.text)
}
})
// Option 2: hooks.onEvent - awaited, best for async side effects
const agent = new Agent({
...config,
hooks: {
onEvent: async (event) => {
if (event.type === "assistant-text-delta") {
await logToService(event.text)
}
},
},
})
```
Both receive the same `AgentRuntimeEvent` types. Prefer `subscribe()` for streaming UI.
## Event Listener Timing
Register event listeners via `subscribe()` before calling `run()`:
```typescript
// Good: subscribe before run
agent.subscribe(handler)
const result = await agent.run(input)
// Bad: subscribing after run starts loses early events
const promise = agent.run(input)
agent.subscribe(handler) // may miss events
```
## Tool Input Schema Matters
The model uses the tool's `inputSchema` to decide what arguments to pass. A vague or missing schema leads to incorrect tool calls.
- Use `z.enum()` for fixed value sets, not free-form strings
- Describe every property with `.describe()` in Zod or `description` in JSON Schema
- Include constraints (rate limits, max values) in the tool description
## Memory and Long Conversations
The Agent holds all messages in memory. For long-running conversations, memory usage grows with each turn. Consider:
- Using `ClineCore` with compaction for long sessions
- Periodically creating a new agent with a summary of the conversation
- Monitoring `result.usage.totalInputTokens` to track context growth
## Abort Signal Handling in Tools
Long-running tools should respect the abort signal:
```typescript
execute: async (input, context) => {
for (const item of items) {
if (context.abortSignal?.aborted) {
return { partial: results, aborted: true }
}
results.push(await process(item))
}
return { results }
}
```
## Provider API Key
If you get authentication errors, check:
- `apiKey` is set in the config or via environment variables
- The key matches the `providerId` (e.g., Anthropic key for `providerId: "anthropic"`)
- For OpenAI-compatible providers, both `apiKey` and `baseUrl` are set
See `../providers/REFERENCE.md` for provider-specific setup.
## See Also
- `api.md` - Full API reference
- `patterns.md` - Common patterns
- `../tools/REFERENCE.md` - Tool creation
- `../clinecore/REFERENCE.md` - Use ClineCore for persistence
@@ -0,0 +1,258 @@
# Agent Patterns
## Interactive CLI Agent
A multi-turn conversational agent in the terminal with streaming output:
```typescript
import { Agent } from "@cline/sdk"
import * as readline from "node:readline"
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
systemPrompt: "You are a helpful assistant. Keep responses concise.",
tools: [],
})
agent.subscribe((event) => {
if (event.type === "assistant-text-delta") {
process.stdout.write(event.text)
}
})
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
function prompt(): void {
rl.question("\nYou: ", async (input) => {
const trimmed = input.trim()
if (!trimmed || trimmed === "exit") {
rl.close()
return
}
process.stdout.write("\nAssistant: ")
if (agent.hasRun) {
await agent.continue(trimmed)
} else {
await agent.run(trimmed)
}
process.stdout.write("\n")
prompt()
})
}
prompt()
```
## Conversational Agent (Slack Bot, Chat App)
Maintain per-thread agents with conversation memory:
```typescript
import { Agent } from "@cline/sdk"
const agents = new Map<string, Agent>()
async function handleMessage(threadId: string, message: string) {
let agent = agents.get(threadId)
if (!agent) {
agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "You are a concise assistant.",
tools: [],
})
agents.set(threadId, agent)
}
const result = agent.hasRun
? await agent.continue(message)
: await agent.run(message)
return result.outputText
}
```
## Streaming UI
Build a real-time UI by handling events via `subscribe()`:
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "You are a helpful assistant.",
tools: [myTool],
})
agent.subscribe((event) => {
switch (event.type) {
case "assistant-text-delta":
ui.appendText(event.text)
break
case "assistant-message":
ui.endText()
break
case "turn-started":
ui.startTurn(event.iteration)
break
case "turn-finished":
if (event.toolCallCount > 0) ui.showToolCount(event.toolCallCount)
break
case "usage-updated":
ui.updateUsage(event.usage.inputTokens, event.usage.outputTokens)
break
}
})
const result = await agent.run("Hello!")
```
## Structured Output via Completion Tool
Use a tool with `completesRun: true` to extract structured data:
```typescript
import { Agent, createTool } from "@cline/sdk"
import { z } from "zod"
const submitReview = createTool({
name: "submit_review",
description: "Submit the final code review with structured feedback.",
inputSchema: z.object({
summary: z.string(),
issues: z.array(z.object({
file: z.string(),
line: z.number(),
severity: z.enum(["error", "warning", "info"]),
message: z.string(),
})),
approved: z.boolean(),
}),
lifecycle: { completesRun: true },
execute: async (input) => input,
})
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "Review the code diff and submit structured feedback.",
tools: [submitReview],
})
const result = await agent.run(diffContent)
const review = result.toolCalls.find(tc => tc.name === "submit_review")
console.log(review?.output)
```
## Agent with Abort/Timeout
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "Analyze this data.",
tools: [],
})
const timeout = setTimeout(() => agent.abort("Timeout"), 30_000)
try {
const result = await agent.run(data)
if (result.status === "aborted") {
console.log("Agent was aborted")
} else {
console.log(result.outputText)
}
} finally {
clearTimeout(timeout)
}
```
## Agent with Plugins
```typescript
import { Agent } from "@cline/sdk"
import type { AgentPlugin } from "@cline/sdk"
const loggingPlugin: AgentPlugin = {
name: "logging",
manifest: { capabilities: ["hooks"] },
setup() {},
hooks: {
beforeTool({ toolCall }) {
console.log(`Calling tool: ${toolCall.toolName}`)
},
afterRun({ result }) {
console.log(`Completed in ${result.iterations} iterations`)
},
},
}
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
systemPrompt: "You are a helpful assistant.",
tools: [myTool],
plugins: [loggingPlugin],
})
```
## Restoring State Across Sessions
Save and restore agent state manually:
```typescript
// Save state
const snapshot = agent.snapshot()
const serialized = JSON.stringify(snapshot.messages)
// Later: restore
const agent2 = new Agent({ ...config })
const messages = JSON.parse(serialized)
agent2.restore(messages)
const result = await agent2.continue("Continue where we left off")
```
For automatic persistence, use `ClineCore` instead.
## Pre-Built Model via Gateway
For advanced provider configuration:
```typescript
import { Agent } from "@cline/sdk"
import { createGateway } from "@cline/llms"
const gateway = createGateway({
providerConfigs: [
{ providerId: "anthropic", apiKey: process.env.ANTHROPIC_API_KEY },
{ providerId: "openai", apiKey: process.env.OPENAI_API_KEY },
],
})
const model = gateway.createAgentModel({
providerId: "anthropic",
modelId: "claude-opus-4-7",
})
const agent = new Agent({
model,
systemPrompt: "You are a helpful assistant.",
tools: [],
})
```
## See Also
- `api.md` - Full API reference
- `gotchas.md` - Common pitfalls
- `../tools/REFERENCE.md` - Creating tools
- `../plugins/REFERENCE.md` - Plugin system
@@ -0,0 +1,131 @@
# ClineCore Runtime
`ClineCore` is the full-featured runtime from `@cline/core`. It wraps the `Agent` loop with session persistence, built-in tools (bash, editor, file reading, search, web fetch), config discovery, plugin loading, and optional hub-backed multi-process support.
## When to Use ClineCore
| Use ClineCore when... | Use Agent instead when... |
|---|---|
| You need built-in tools (bash, editor, etc.) | You only need custom tools |
| You want session persistence to disk | Stateless is fine |
| You need config discovery from `.cline/` dirs | You handle config yourself |
| You want scheduled/automated agents | You don't need scheduling |
| You need multi-client session sharing | Single-process is fine |
| You're building a full application | You want minimal dependencies |
## Quick Start
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create({ clientName: "my-app" })
const session = await cline.start({
prompt: "Set up CI with GitHub Actions",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
cwd: "/path/to/project",
enableTools: true,
},
})
console.log(session.result?.text)
await cline.dispose()
```
## Core Concepts
### Sessions
Every `cline.start()` call creates a session with a unique ID. Sessions persist their messages and metadata to SQLite. You can list, read, resume, and delete sessions.
### Built-in Tools
ClineCore provides these tools automatically when `enableTools: true`:
| Tool | Description |
|------|-------------|
| `bash` | Execute shell commands |
| `editor` | Edit files |
| `read_files` | Read file contents |
| `apply_patch` | Apply unified diffs |
| `search` | Search file contents and structure |
| `fetch_web` | HTTP requests and web content |
### Config Discovery
ClineCore watches `.cline/` directories for:
- Rules (system prompt additions)
- Skills (domain knowledge)
- Workflows (multi-step procedures)
- Hooks (lifecycle logic)
- Plugins (tool + hook bundles)
- MCP servers (external tool providers)
### Backend Modes
| Mode | Description |
|------|-------------|
| `"auto"` (default) | Tries to connect to a local hub; falls back to in-process if unavailable |
| `"local"` | In-process execution, local SQLite storage, no hub |
| `"hub"` | Requires a compatible local WebSocket hub; fails if unavailable |
| `"remote"` | Connects to an explicit remote hub endpoint |
The default mode is `"auto"`. For simple scripts and CLI tools, `"local"` avoids hub discovery overhead. Hub mode enables multi-client session sharing (e.g., a dashboard watching a running session from another process).
## Key APIs
- `ClineCore.create(options)` - Create and initialize
- `cline.start(input)` - Start a new session
- `cline.send({ sessionId, prompt })` - Send follow-up message
- `cline.subscribe(listener)` - Listen to session events
- `cline.list()` - List sessions
- `cline.get(sessionId)` - Get session metadata
- `cline.readMessages(sessionId)` - Read persisted messages
- `cline.getAccumulatedUsage(sessionId)` - Token/cost totals
- `cline.abort(sessionId)` - Abort a session
- `cline.delete(sessionId)` - Delete a session
- `cline.dispose()` - Clean up resources
See `api.md` for full API details.
## Event Streaming
`cline.subscribe()` emits `CoreSessionEvent` types. These are different from the `AgentRuntimeEvent` types emitted by the standalone `Agent` class -- see `../events/REFERENCE.md` for the full comparison.
```typescript
cline.subscribe((event) => {
switch (event.type) {
case "chunk":
if (event.payload.type === "text") {
process.stdout.write(event.payload.text)
}
break
case "ended":
console.log(`Session ended: ${event.payload.finishReason}`)
break
}
})
```
ClineCore results use `AgentResult` with `.text` (not `.outputText` like the standalone Agent's `AgentRunResult`).
## Session Persistence
Sessions are stored at:
```
~/.cline/data/sessions/
sessions.db # SQLite database
[session-id].json # Message history
```
## Next Steps
- `api.md` - Full ClineCore API reference
- `patterns.md` - Common patterns and best practices
- `gotchas.md` - Pitfalls and debugging
- `../tools/REFERENCE.md` - Custom tool creation
- `../plugins/REFERENCE.md` - Plugin system
- `../scheduling/REFERENCE.md` - Scheduled agents
@@ -0,0 +1,304 @@
# ClineCore API Reference
## Creating ClineCore
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create(options: ClineCoreOptions)
```
### ClineCoreOptions
```typescript
interface ClineCoreOptions {
clientName: string // identifies your app
distinctId?: string // user/instance identifier
backendMode?: "auto" | "local" | "hub" | "remote"
hub?: HubOptions
remote?: RemoteOptions
capabilities?: RuntimeCapabilities
toolPolicies?: Record<string, ToolPolicy>
automation?: boolean | ClineCoreAutomationOptions
fetch?: typeof fetch
}
```
### RuntimeCapabilities
```typescript
interface RuntimeCapabilities {
requestToolApproval?: (request: ToolApprovalRequest) => Promise<ToolApprovalResult>
// ... other capability callbacks
}
```
## Starting Sessions
### start(input)
```typescript
const session = await cline.start(input: ClineCoreStartInput)
```
Returns a `StartSessionResult`:
```typescript
interface StartSessionResult {
sessionId: string
manifest: SessionManifest
manifestPath: string
messagesPath: string
result?: AgentResult
}
```
### ClineCoreStartInput
```typescript
interface ClineCoreStartInput {
prompt: string
config: CoreSessionConfig
source?: string
interactive?: boolean
sessionMetadata?: Record<string, unknown>
initialMessages?: AgentMessage[]
toolPolicies?: Record<string, ToolPolicy>
capabilities?: RuntimeCapabilities
}
```
### CoreSessionConfig
```typescript
interface CoreSessionConfig {
cwd?: string // working directory
providerId: string // LLM provider
modelId: string // model identifier
apiKey?: string // provider API key
systemPrompt?: string // custom system prompt
tools?: readonly AgentTool[] // additional custom tools
enableTools?: boolean // enable built-in tools
hooks?: Partial<AgentRuntimeHooks> // runtime hooks
extensions?: AgentPlugin[] // plugins loaded inline
pluginPaths?: string[] // paths to plugin packages
extensionLoading?: "isolated" | "direct"
extensionContext?: { // context passed to plugin setup()
workspace?: { rootPath: string; cwd: string }
}
checkpointConfig?: CoreCheckpointConfig
compactionConfig?: CoreCompactionConfig
telemetry?: ITelemetryService
logger?: BasicLogger
enableSpawnAgent?: boolean // enable sub-agent spawning
enableAgentTeams?: boolean // enable team coordination
teamName?: string // team identifier
}
```
`extensions` passes plugin objects directly. `pluginPaths` points to directories with `package.json` containing a `cline.plugins` field. Set `extensionContext.workspace` so plugins receive `ctx.workspaceInfo` in their `setup()` call -- without it, `ctx.workspaceInfo` is undefined.
## Follow-Up Messages
### send({ sessionId, prompt })
Send a follow-up message to an existing session:
```typescript
const result = await cline.send({
sessionId: session.sessionId,
prompt: "Now add authentication",
})
```
Returns `AgentResult | undefined`.
## Event Subscription
### subscribe(listener, options?)
```typescript
const unsubscribe = cline.subscribe(
(event: CoreSessionEvent) => {
// handle events
},
{ sessionId: "optional-filter" }
)
```
### CoreSessionEvent
```typescript
type CoreSessionEvent =
| { type: "chunk"; payload: SessionChunkEvent }
| { type: "agent_event"; payload: { sessionId: string, event: AgentEvent } }
| { type: "ended"; payload: SessionEndedEvent }
| { type: "team_progress"; payload: SessionTeamProgressEvent }
| { type: "status"; payload: { sessionId: string, status: string } }
| { type: "hook"; payload: SessionToolEvent }
```
## Session Management
### list(limit?, options?)
```typescript
const sessions: SessionRecord[] = await cline.list(50)
```
### get(sessionId)
```typescript
const session: SessionRecord = await cline.get(sessionId)
```
### readMessages(sessionId)
```typescript
const messages: AgentMessage[] = await cline.readMessages(sessionId)
```
### getAccumulatedUsage(sessionId)
```typescript
const usage = await cline.getAccumulatedUsage(sessionId)
// usage.usage - root agent only
// usage.aggregateUsage - root + subagents/teammates
```
### update(sessionId, updates)
```typescript
await cline.update(sessionId, { title: "New title" })
```
### abort(sessionId, reason?)
```typescript
await cline.abort(sessionId, "User cancelled")
```
### stop(sessionId)
```typescript
await cline.stop(sessionId)
```
### delete(sessionId)
```typescript
await cline.delete(sessionId)
```
### restore(input)
Restore a session from a checkpoint:
```typescript
await cline.restore({ sessionId, checkpointId })
```
### dispose(reason?)
Clean up all resources. Always call this when done:
```typescript
await cline.dispose("Shutting down")
```
## AgentResult
Returned by session operations:
```typescript
interface AgentResult {
text: string
usage: LegacyAgentUsage
messages: MessageWithMetadata[]
toolCalls: ToolCallRecord[]
iterations: number
finishReason: "completed" | "max_iterations" | "aborted" | "mistake_limit" | "error"
model: { id: string; provider: string; info?: ModelInfo }
startedAt: Date
endedAt: Date
durationMs: number
}
```
## Tool Policies
Control tool access at the session level:
```typescript
const session = await cline.start({
prompt: "Review the code",
config: { ... },
toolPolicies: {
read_files: { autoApprove: true },
bash: { autoApprove: false },
editor: { enabled: false },
},
})
```
### ToolPolicy
```typescript
interface ToolPolicy {
enabled?: boolean // false = tool is hidden from the model
autoApprove?: boolean // false = requires approval callback
}
```
## Interactive Approval
```typescript
const cline = await ClineCore.create({
clientName: "my-app",
capabilities: {
requestToolApproval: async (request) => {
console.log(`Tool: ${request.toolName}, Input: ${JSON.stringify(request.input)}`)
const approved = await askUser(`Allow ${request.toolName}?`)
return { approved }
},
},
})
```
## Automation API
When `automation` is enabled in `ClineCore.create()`:
```typescript
const cline = await ClineCore.create({
clientName: "my-app",
automation: true,
})
// Access automation methods
cline.automation.start()
cline.automation.stop()
cline.automation.reconcile(specs)
cline.automation.ingestEvent(event)
cline.automation.listEvents()
cline.automation.listSpecs()
cline.automation.listRuns()
```
## Settings API
```typescript
// Read settings
const settings = await cline.settings.list()
// Toggle tools, plugins, MCP servers
await cline.settings.toggle({ type: "tool", name: "bash", enabled: true })
```
## See Also
- `REFERENCE.md` - Overview and quick start
- `patterns.md` - Common patterns
- `gotchas.md` - Pitfalls
- `../tools/REFERENCE.md` - Tool creation
- `../plugins/REFERENCE.md` - Plugin system
@@ -0,0 +1,148 @@
# ClineCore Gotchas
## Always Call dispose()
`ClineCore` holds resources (file watchers, database connections, hub connections). Failing to call `dispose()` can leave orphan processes and file locks.
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
try {
// ... use cline
} finally {
await cline.dispose()
}
```
## Node.js 22 Required
ClineCore and `@cline/core` require Node.js 22 or later. If you're on an older version, you'll get runtime errors. Check with `node --version`.
## Session Config vs Global Config
Tool policies can be set at two levels:
- Global: in `ClineCore.create({ toolPolicies })` -- applies to all sessions
- Per-session: in `cline.start({ toolPolicies })` -- overrides global for that session
Per-session policies take precedence.
## enableTools Must Be Explicit
Built-in tools (bash, editor, read_files, etc.) are not available unless you set `enableTools: true` in the session config:
```typescript
await cline.start({
prompt: "Read package.json",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
enableTools: true, // required for built-in tools
},
})
```
Without this, the agent only has access to custom tools you provide via `config.tools`.
## cwd Matters for Built-in Tools
Built-in tools like `bash`, `editor`, and `read_files` operate relative to `config.cwd`. If not set, they use the process working directory. Always set it explicitly for predictable behavior:
```typescript
config: {
cwd: "/absolute/path/to/project",
// ...
}
```
## Hub Startup Latency
With `backendMode: "auto"`, the first session may be slow if a hub daemon needs to be spawned. For immediate responsiveness:
- Use `backendMode: "local"` for in-process execution (fastest startup)
- Pre-warm the hub with `cline hub ensure` CLI command
- Accept the one-time startup cost and let subsequent sessions reuse the hub
## Session Storage Location
Sessions are stored at `~/.cline/data/sessions/`. This includes:
- `sessions.db` - SQLite database with session metadata
- `[session-id].json` - Individual message history files
If you're running in a container or ephemeral environment, these paths may not persist across restarts.
## requestToolApproval Blocks Execution
When a tool policy has `autoApprove: false` and you provide a `requestToolApproval` callback, the agent loop blocks until your callback resolves. If your callback never resolves (e.g., waiting for user input that never comes), the session hangs.
For automated pipelines, either:
- Set all tools to `autoApprove: true`
- Implement a timeout in your approval callback
## Plugin Discovery Paths
ClineCore discovers plugins from:
- Global: `~/.cline/plugins/`
- Workspace: `.cline/plugins/`
For SDK consumers, pass plugins via `extensions: [plugin]` or `pluginPaths: ["./path"]` in the session config.
If a plugin isn't loading, verify:
- The file is in one of the discovery directories, or passed via `extensions`/`pluginPaths`
- The file exports a default plugin object with a non-empty `manifest.capabilities` array
- Every `api.register*` call in `setup()` has a matching capability declared
- If `hooks` is present on the plugin, `"hooks"` is in `capabilities`
## extensionContext.workspace Is Required for Plugins
If your plugins use `ctx.workspaceInfo` (e.g., to resolve workspace paths), you must set `extensionContext.workspace` in the session config. Without it, `ctx.workspaceInfo` is undefined:
```typescript
await cline.start({
config: {
extensions: [myPlugin],
extensionContext: {
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
},
},
})
```
The CLI sets this automatically, but SDK consumers must set it explicitly.
## send() Requires an Active Session
`cline.send()` only works on sessions that are still active. If a session has already completed, `send()` may return `undefined` or fail. Check session status with `cline.get(sessionId)` first.
## Result May Be Undefined
`session.result` can be `undefined` if the session was started but hasn't completed yet (e.g., in a non-blocking hub mode). Check for this:
```typescript
const session = await cline.start({ ... })
if (session.result) {
console.log(session.result.text)
} else {
console.log("Session started but not yet complete")
}
```
## Compaction and Long Sessions
For long-running sessions, message history grows and eventually exceeds the model's context window. ClineCore handles this via compaction, which summarizes older messages. Configure it via `compactionConfig`:
```typescript
config: {
compactionConfig: {
strategy: "summarize",
// ...
},
}
```
The default strategy works for most cases, but extremely long sessions may benefit from tuning.
## See Also
- `api.md` - Full API reference
- `patterns.md` - Common patterns
- `../agent/gotchas.md` - Agent-level gotchas
- `../tools/REFERENCE.md` - Tool troubleshooting
- `../providers/REFERENCE.md` - Provider troubleshooting
@@ -0,0 +1,279 @@
# ClineCore Patterns
## Basic Session with Built-in Tools
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create({ clientName: "my-app" })
const session = await cline.start({
prompt: "Read package.json and summarize the dependencies",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
cwd: process.cwd(),
enableTools: true,
},
})
console.log(session.result?.text)
await cline.dispose()
```
## Streaming Session with UI Updates
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
cline.subscribe((event) => {
switch (event.type) {
case "chunk":
if (event.payload.type === "text") {
ui.appendText(event.payload.text)
}
break
case "ended":
ui.showComplete(event.payload.finishReason)
break
}
})
await cline.start({
prompt: "Refactor the auth module",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
cwd: "/path/to/project",
enableTools: true,
},
})
```
## Multi-Turn Session
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
const session = await cline.start({
prompt: "Create a new Express server",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
cwd: "/path/to/project",
enableTools: true,
},
})
// Follow-up
const result = await cline.send({
sessionId: session.sessionId,
prompt: "Now add a health check endpoint",
})
console.log(result?.text)
await cline.dispose()
```
## Tiered Permission Model
Auto-approve reads, require approval for writes:
```typescript
const cline = await ClineCore.create({
clientName: "my-app",
toolPolicies: {
read_files: { autoApprove: true },
search: { autoApprove: true },
fetch_web: { autoApprove: true },
bash: { autoApprove: false },
editor: { autoApprove: false },
apply_patch: { autoApprove: false },
},
capabilities: {
requestToolApproval: async (request) => {
const approved = await promptUser(
`Allow ${request.toolName}?\n${JSON.stringify(request.input, null, 2)}`
)
return { approved }
},
},
})
```
## Custom Tools Alongside Built-ins
```typescript
import { ClineCore, createTool } from "@cline/sdk"
import { z } from "zod"
const deployTool = createTool({
name: "deploy",
description: "Deploy the application to the specified environment.",
inputSchema: z.object({
environment: z.enum(["staging", "production"]),
}),
execute: async (input) => {
const result = await runDeployment(input.environment)
return { url: result.url, status: "deployed" }
},
})
const cline = await ClineCore.create({ clientName: "my-app" })
await cline.start({
prompt: "Deploy the app to staging",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
cwd: process.cwd(),
enableTools: true,
tools: [deployTool],
},
})
```
## Session with Plugins
Load plugins inline with `extensions` and provide workspace context so plugins can access `ctx.workspaceInfo`:
```typescript
import { ClineCore } from "@cline/sdk"
import myPlugin from "./my-plugin"
const cline = await ClineCore.create({
clientName: "my-app",
backendMode: "local",
})
await cline.start({
prompt: "Do the thing my plugin enables",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
cwd: process.cwd(),
enableTools: true,
extensions: [myPlugin],
extensionContext: {
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
},
},
})
await cline.dispose()
```
For directory-based plugin packages, use `pluginPaths` instead:
```typescript
config: {
pluginPaths: ["./my-cline-plugin"],
extensionContext: {
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
},
}
```
See `../plugins/REFERENCE.md` for the full plugin authoring guide.
## Session Listing and Replay
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
// List recent sessions
const sessions = await cline.list(10)
for (const session of sessions) {
console.log(`${session.id}: ${session.title}`)
}
// Read messages from a past session
const messages = await cline.readMessages(sessions[0].id)
for (const msg of messages) {
console.log(`[${msg.role}] ${msg.content}`)
}
// Check usage
const usage = await cline.getAccumulatedUsage(sessions[0].id)
console.log(`Total tokens: ${usage.aggregateUsage.totalInputTokens + usage.aggregateUsage.totalOutputTokens}`)
```
## Graceful Shutdown
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
process.on("SIGTERM", async () => {
await cline.dispose("SIGTERM received")
process.exit(0)
})
// Run sessions...
```
## Stateless Worker Pattern
For request/response workloads (API endpoints, queue consumers):
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create({
clientName: "worker",
backendMode: "local",
})
async function handleRequest(prompt: string, workspace: string) {
const session = await cline.start({
prompt,
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
cwd: workspace,
enableTools: true,
},
})
return {
text: session.result?.text,
usage: session.result?.usage,
sessionId: session.sessionId,
}
}
```
## Hub-Backed Multi-Client
Multiple clients can attach to the same session:
```typescript
// Process 1: start session
const cline = await ClineCore.create({
clientName: "backend",
backendMode: "hub",
})
const session = await cline.start({
prompt: "Long running refactor task",
config: { ... },
})
// Process 2: attach and stream events
const viewer = await ClineCore.create({
clientName: "dashboard",
backendMode: "hub",
})
viewer.subscribe((event) => {
dashboard.render(event)
}, { sessionId: session.sessionId })
```
## See Also
- `api.md` - Full API reference
- `gotchas.md` - Common pitfalls
- `../tools/REFERENCE.md` - Tool creation
- `../plugins/REFERENCE.md` - Plugin system
- `../scheduling/REFERENCE.md` - Scheduled agents
@@ -0,0 +1,269 @@
# Events
The Cline SDK has three event layers. Which one you use depends on whether you're working with the standalone `Agent` class or `ClineCore`.
## Which Events Do I Get?
| If you use... | You subscribe with... | You receive... | Text streaming event |
|---|---|---|---|
| Standalone `Agent` | `agent.subscribe()` | `AgentRuntimeEvent` | `assistant-text-delta` |
| `ClineCore` | `cline.subscribe()` | `CoreSessionEvent` | `chunk` (with `payload.type === "text"`) |
These are different event types with different shapes. Do not mix them up.
## Layer 1: AgentRuntimeEvent (Standalone Agent)
Emitted by the `Agent` class via `agent.subscribe()`. This is what you get when using `new Agent(...)` directly. Every event includes a `snapshot` field with the current `AgentRuntimeStateSnapshot`.
### Run Lifecycle
```typescript
{ type: "run-started", snapshot }
{ type: "run-finished", snapshot, result: AgentRunResult }
{ type: "run-failed", snapshot, error: Error }
```
### Turns
```typescript
{ type: "turn-started", snapshot, iteration: number }
{ type: "turn-finished", snapshot, iteration: number, toolCallCount: number }
```
### Text Streaming
```typescript
// Streaming text delta (arrives as chunks during generation)
{ type: "assistant-text-delta", snapshot, iteration: number, text: string, accumulatedText: string }
// Streaming reasoning delta (when model uses extended thinking)
{ type: "assistant-reasoning-delta", snapshot, iteration: number, text: string }
// Complete assistant message after model finishes
{ type: "assistant-message", snapshot, iteration: number, message: AgentMessage, finishReason: string }
```
### Messages
```typescript
// Fired when any message (user or assistant) is added to conversation history
{ type: "message-added", snapshot, message: AgentMessage }
```
### Tool Events
```typescript
{ type: "tool-started", snapshot, toolCall: { toolName: string, toolCallId: string, input: unknown } }
{ type: "tool-updated", snapshot, toolCall: { toolName: string, toolCallId: string }, update: string }
{ type: "tool-finished", snapshot, toolCall: { toolName: string, toolCallId: string }, message: AgentMessage }
```
### Usage
```typescript
{
type: "usage-updated",
snapshot,
usage: {
inputTokens: number,
outputTokens: number,
cacheReadTokens?: number,
cacheWriteTokens?: number,
totalCost?: number,
},
}
```
### Notices
```typescript
{ type: "status-notice", snapshot, message: string, metadata?: Record<string, unknown> }
```
### Subscribing
Use `agent.subscribe()`. Register the listener before calling `run()` to avoid missing early events.
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
systemPrompt: "You are a helpful assistant.",
tools: [],
})
agent.subscribe((event) => {
switch (event.type) {
case "assistant-text-delta":
process.stdout.write(event.text)
break
case "tool-started":
console.log(`\nUsing tool: ${event.toolCall.toolName}`)
break
case "usage-updated":
console.log(`Cost: $${event.usage.totalCost?.toFixed(4)}`)
break
case "run-finished":
console.log(`\nDone: ${event.result.status}`)
break
}
})
const result = await agent.run("Hello!")
```
You can also receive events through hooks (these are awaited, so they can be async):
```typescript
const agent = new Agent({
...config,
hooks: {
onEvent: async (event) => {
// Same AgentRuntimeEvent types as subscribe()
},
},
})
```
## Layer 2: AgentEvent (ClineCore Internal)
When using `ClineCore`, a `RuntimeEventAdapter` translates Layer 1 events into a legacy format called `AgentEvent`. You do not interact with this layer directly -- it is projected into `CoreSessionEvent` for subscribers. The key mappings:
| AgentRuntimeEvent (Layer 1) | AgentEvent (Layer 2) |
|---|---|
| `turn-started` | `iteration_start` |
| `turn-finished` | `iteration_end` |
| `assistant-text-delta` | `content_start` (text) |
| `assistant-message` | `content_end` (text) |
| `tool-started` | `content_start` (tool) |
| `tool-updated` | `content_update` (tool) |
| `tool-finished` | `content_end` (tool) |
| `usage-updated` | `usage` (with computed deltas) |
| `run-finished` | `done` |
| `run-failed` | `error` |
| `run-started`, `message-added` | (suppressed, not emitted) |
This layer exists for backwards compatibility. If you see event types like `content_update` or `iteration_start` in other documentation, they refer to this layer, not to what `agent.subscribe()` emits.
## Layer 3: CoreSessionEvent (ClineCore Subscriber)
Emitted by `ClineCore` via `cline.subscribe()`. These are higher-level session events.
```typescript
type CoreSessionEvent =
| { type: "chunk"; payload: SessionChunkEvent }
| { type: "agent_event"; payload: { sessionId: string, event: AgentEvent } }
| { type: "ended"; payload: SessionEndedEvent }
| { type: "team_progress"; payload: SessionTeamProgressEvent }
| { type: "status"; payload: { sessionId: string, status: string } }
| { type: "hook"; payload: SessionToolEvent }
```
### SessionChunkEvent
```typescript
interface SessionChunkEvent {
type: "text" | "reasoning"
text: string
sessionId: string
}
```
### SessionEndedEvent
```typescript
interface SessionEndedEvent {
sessionId: string
finishReason: "completed" | "max_iterations" | "aborted" | "mistake_limit" | "error"
result?: AgentResult
}
```
### Subscribing
```typescript
cline.subscribe((event) => {
switch (event.type) {
case "chunk":
if (event.payload.type === "text") {
process.stdout.write(event.payload.text)
}
break
case "ended":
console.log(`Finished: ${event.payload.finishReason}`)
break
}
})
```
Filter by session:
```typescript
cline.subscribe(handler, { sessionId: "specific-session-id" })
```
## Hub Events (Layer 3b)
When ClineCore runs in hub mode (via `backendMode: "hub"` or `"auto"` when a hub is available), events are projected over WebSocket using `HubEventName` types like `assistant.delta`, `iteration.started`, `tool.started`, etc. You do not interact with these directly -- `cline.subscribe()` still gives you `CoreSessionEvent` regardless of backend mode.
## Result Type Differences
The standalone Agent and ClineCore return different result types:
| API | Result type | Text property |
|---|---|---|
| `agent.run()` | `AgentRunResult` | `result.outputText` |
| `cline.start()` / `cline.send()` | `AgentResult` | `result.text` |
## Common Patterns
### Streaming Text (Standalone Agent)
```typescript
agent.subscribe((event) => {
if (event.type === "assistant-text-delta") {
process.stdout.write(event.text)
}
})
```
### Streaming Text (ClineCore)
```typescript
cline.subscribe((event) => {
if (event.type === "chunk" && event.payload.type === "text") {
process.stdout.write(event.payload.text)
}
})
```
### Usage Tracking (Standalone Agent)
```typescript
agent.subscribe((event) => {
if (event.type === "usage-updated" && event.usage.totalCost) {
console.log(`Running cost: $${event.usage.totalCost.toFixed(4)}`)
}
})
```
### Tool Call Logging (Standalone Agent)
```typescript
agent.subscribe((event) => {
if (event.type === "tool-started") {
console.log(`Tool started: ${event.toolCall.toolName}`)
}
if (event.type === "tool-finished") {
console.log(`Tool finished: ${event.toolCall.toolName}`)
}
})
```
## See Also
- `../agent/REFERENCE.md` - Agent runtime overview
- `../clinecore/REFERENCE.md` - ClineCore session management
- `../plugins/REFERENCE.md` - Plugin hooks for lifecycle events
- `../production/REFERENCE.md` - Observability in production
@@ -0,0 +1,157 @@
# Multi-Agent Coordination
The Cline SDK supports two models for multi-agent work: sub-agents (parent-child) and teams (peer-to-peer).
## Sub-Agents vs Teams
| Feature | Sub-Agents | Teams |
|---------|-----------|-------|
| Enable with | `enableSpawnAgent: true` | `enableAgentTeams: true` |
| Persistence | Session-scoped only | Across sessions |
| Coordination | Parent-child hierarchy | Peer-to-peer |
| Shared state | None | Task board, mailbox, mission log |
| Best for | One-off delegation | Complex multi-session projects |
## Sub-Agents
Sub-agents are spawned by a parent agent during a run. They execute independently and report results back.
### Enabling Sub-Agents
```typescript
const cline = await ClineCore.create({ clientName: "my-app" })
await cline.start({
prompt: "Refactor the auth module and update tests",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
enableSpawnAgent: true,
enableTools: true,
},
})
```
When `enableSpawnAgent` is true, the agent gets access to sub-agent tools:
| Tool | Description |
|------|-------------|
| `start_subagent` | Spawn a background agent with a task |
| `message_subagent` | Send a message to a running sub-agent |
| `handoff_to_agent` | Delegate the current task entirely |
| `submit_and_exit` | Signal completion |
### How Sub-Agents Work
1. The parent agent decides a subtask can be delegated
2. It calls `start_subagent` with a role, task description, and optionally a preset
3. The sub-agent runs independently in the background
4. The parent can check status or send follow-up messages
5. Sub-agent results are available to the parent when complete
## Teams
Teams provide persistent, cross-session coordination between agents.
### Enabling Teams
```typescript
await cline.start({
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
enableAgentTeams: true,
teamName: "auth-sprint",
enableTools: true,
},
})
```
### Team Tools
When `enableAgentTeams` is true, the coordinator agent gets:
| Tool | Description |
|------|-------------|
| `team_spawn_teammate` | Create a new agent with a role and task |
| `team_delegate_task` | Assign a task to an existing teammate |
| `team_check_status` | Check on a delegated task's progress |
| `team_get_result` | Get the completed result from a teammate |
### Team Persistence
Teams store shared state in:
```
~/.cline/data/teams/[team-name]/
task-board.json # task assignments and status
mailbox.json # inter-agent messages
mission-log.json # coordination log
```
This state persists across sessions, so team members can pick up where they left off.
### CLI Team Access
```bash
cline --team-name auth-sprint "Continue the auth refactor"
```
## Choosing Between Sub-Agents and Teams
Use sub-agents when:
- You need one-off parallel execution within a single session
- Tasks are independent and don't need to communicate with each other
- Results only matter to the parent agent
Use teams when:
- Work spans multiple sessions over time
- Agents need to coordinate and share progress
- Tasks have dependencies between them
- You want a persistent record of multi-agent collaboration
## Patterns
### Parallel Research with Sub-Agents
A parent agent spawns multiple sub-agents to research different topics simultaneously:
```typescript
await cline.start({
prompt: `Research these three topics in parallel:
1. Current best practices for JWT auth
2. OAuth 2.0 provider comparison
3. Session management patterns
Spawn a sub-agent for each topic, then synthesize the results.`,
config: {
enableSpawnAgent: true,
enableTools: true,
// ...
},
})
```
### Team Sprint
A coordinator manages a multi-session project:
```typescript
await cline.start({
prompt: `You are the coordinator for the auth-sprint team.
Review the task board and delegate the next highest-priority task
to a teammate. Check status on any in-progress tasks.`,
config: {
enableAgentTeams: true,
teamName: "auth-sprint",
enableTools: true,
// ...
},
})
```
## See Also
- `../clinecore/REFERENCE.md` - ClineCore runtime
- `../clinecore/api.md` - Session config for teams
- `../tools/REFERENCE.md` - Tool system
- `../plugins/REFERENCE.md` - Plugin system
@@ -0,0 +1,649 @@
# Plugins
A Cline plugin is a TypeScript module that extends any agent built on the Cline SDK. The same plugin runs in the Cline CLI, VS Code and JetBrains extensions, and any custom app built on `@cline/core`.
A plugin can:
- Register tools the model can call.
- Hook into the agent loop before/after runs, model calls, and tool calls.
- Rewrite provider messages before they hit the model (custom compaction, redaction, context shaping).
- Register slash commands, prompt rules, providers, and automation event types.
A plugin ships in one of two shapes:
1. Single-file plugin -- one `.ts` file that exports a default plugin object. Drop it in a discovery folder and it loads.
2. Plugin package -- a directory with `package.json`, npm dependencies, and optionally bundled assets. Installable via `cline plugin install`.
Both shapes use the same plugin API.
## The Mental Model
When the host starts a session, it builds a registry of plugins and runs four phases:
1. resolve -- collect the plugin objects.
2. validate -- check each plugin's `manifest`. Capabilities must be non-empty; declared hook stages must have matching handlers; if `hooks` is present, `"hooks"` must be in `capabilities`.
3. setup -- call each plugin's `setup(api, ctx)` once. This is where you `registerTool`, `registerCommand`, etc.
4. activate -- registry is frozen, the agent loop starts, and your hooks/tools are live.
Two invariants the registry enforces:
- Every contribution requires a matching capability. Calling `api.registerRule(...)` without `"rules"` in `manifest.capabilities` throws.
- Capabilities and handlers must agree. Declaring `"hooks"` without a `hooks` object, or vice versa, fails validation.
After validation, registration is one-shot -- no dynamic register/unregister during the session.
## The Smallest Working Plugin
```typescript
import type { AgentPlugin } from "@cline/core"
import { createTool } from "@cline/core"
const plugin: AgentPlugin = {
name: "hello-plugin",
manifest: {
capabilities: ["tools"],
},
setup(api, ctx) {
api.registerTool(
createTool({
name: "say_hello",
description: "Greet a person by name.",
inputSchema: {
type: "object",
properties: { name: { type: "string" } },
required: ["name"],
},
async execute({ name }: { name: string }) {
return { greeting: `Hello, ${name}!` }
},
}),
)
},
}
export default plugin
```
The agent will see `say_hello` as a callable tool.
## The Manifest
```typescript
manifest: {
capabilities: ["tools", "hooks"], // required, non-empty array
paths?: string[], // optional, multi-entry packages
providerIds?: string[], // optional, provider plugins
modelIds?: string[], // optional, model plugins
}
```
### The Complete Capability List
| Capability | What It Unlocks in `api` |
|-----------|--------------------------|
| `"tools"` | `api.registerTool()` |
| `"commands"` | `api.registerCommand()` (slash commands in chat surfaces) |
| `"rules"` | `api.registerRule()` (string injected into the system prompt) |
| `"messageBuilders"` | `api.registerMessageBuilder()` (rewrites provider-bound messages) |
| `"providers"` | `api.registerProvider()` (custom model provider) |
| `"automationEvents"` | `api.registerAutomationEventType()` and `ctx.automation?.ingestEvent()` |
| `"hooks"` | The runtime `hooks` object on the plugin (lifecycle callbacks) |
You declare any combination -- most real plugins need 1-3 capabilities.
## setup(api, ctx) -- The Registration Phase
`setup()` runs once per session before the agent loop starts. Everything you register here is frozen for the lifetime of the session.
### The api Object
Each `register*` method requires the matching capability in your manifest:
```typescript
api.registerTool(tool) // requires "tools"
api.registerCommand({ name, description, handler }) // requires "commands"
api.registerRule({ id, content, source }) // requires "rules"
api.registerMessageBuilder({ name, build }) // requires "messageBuilders"
api.registerProvider({ name, description }) // requires "providers"
api.registerAutomationEventType({ eventType, source }) // requires "automationEvents"
```
### The ctx Object -- Host-Provided Session Context
The second argument carries everything the host knows about the current session. All fields are optional, so feature-detect before using them -- the same plugin must work in hosts that supply less context (unit tests, sandboxed plugin processes).
```typescript
ctx.session?.sessionId // string, stable core session id
ctx.client?.name // host: "cline-cli", "cline-vscode", etc.
ctx.user // authenticated user/org info, when available
ctx.workspaceInfo // { rootPath, hint, latestGitBranchName,
// latestGitCommitHash, associatedRemoteUrls }
ctx.automation?.ingestEvent // emit normalized automation events
ctx.logger?.log // structured logger scoped to this plugin
ctx.telemetry // ITelemetryService, only present in-process
```
Two rules about `ctx.workspaceInfo`:
1. Always prefer `ctx.workspaceInfo?.rootPath` over `process.cwd()`. The CLI may have been launched with `--cwd` without calling `chdir`, and VS Code workspaces don't share a single CWD. `workspaceInfo` is sourced from the session config and is always correct.
2. Don't use `import.meta.url` tricks to find "the workspace". That gives you the plugin's own location, not the user's project.
### Persisting State Across Hooks
`setup()` runs first; hooks fire later. The simplest way to share state is module-level variables:
```typescript
let sessionWorkspaceRoot: string | undefined
let sessionBranch: string | undefined
const plugin: AgentPlugin = {
name: "metrics",
manifest: { capabilities: ["hooks"] },
setup(api, ctx) {
sessionWorkspaceRoot = ctx.workspaceInfo?.rootPath
sessionBranch = ctx.workspaceInfo?.latestGitBranchName
},
hooks: {
beforeTool({ toolCall, input }) {
if (sessionBranch === "main" && toolCall.toolName === "run_commands") {
// inspect input, optionally block
}
return undefined
},
},
}
```
A single Node process may host multiple sessions concurrently. If your plugin will run in a multi-session host, key your state by `ctx.session?.sessionId`:
```typescript
const stateBySession = new Map<string, MyState>()
setup(api, ctx) {
const id = ctx.session?.sessionId
if (id) stateBySession.set(id, /* ... */)
}
```
## Runtime Hooks
Runtime hooks are typed in-process callbacks on the same hook layer the runtime uses internally. They run inside the agent loop with full type information -- no IPC, no JSON marshaling.
Declare `"hooks"` in `manifest.capabilities`, then add a `hooks` property:
```typescript
const plugin: AgentPlugin = {
name: "metrics",
manifest: { capabilities: ["hooks"] },
hooks: {
beforeRun(ctx) { /* ... */ },
beforeTool({ toolCall, input }) { /* ... */ },
afterTool({ toolCall, result }) { /* ... */ },
afterRun({ result }) { /* ... */ },
onEvent(event) { /* ... */ },
},
}
```
### The Seven Hooks
| Hook | Fires | Can Stop the Loop? | Common Uses |
|------|-------|--------------------|-------------|
| `beforeRun` | Before the runtime loop starts | Yes | Greet, log, attach session metadata |
| `afterRun` | After the runtime loop finishes (success, abort, or fail) | No | Notifications, metrics, persistent logs |
| `beforeModel` | Before each model request | Yes (mutate req) | Inject context, last-mile prompt edits |
| `afterModel` | After each model response, before tool execution | Yes | Block based on model output |
| `beforeTool` | Before each tool execution | Yes (`{ stop }`) | Audit, redact, block dangerous tools |
| `afterTool` | After each tool execution | Can replace result | Post-process, redact secrets in tool output |
| `onEvent` | On every `AgentRuntimeEvent` emitted by the runtime | No | Streaming UIs, telemetry pipes |
### Stopping the Loop from a Hook
Several hooks return an optional control object. The most common pattern is `beforeTool` blocking a destructive tool call:
```typescript
beforeTool({ toolCall, input }) {
if (toolCall.toolName === "run_commands") {
const { commands } = input as { commands?: string[] }
if (sessionBranch === "main" && commands?.some(c => c.startsWith("git push"))) {
return { stop: true, reason: "Blocked git push on protected branch" }
}
}
return undefined // explicit "continue"
}
```
Returning `undefined` (or omitting `return`) lets execution continue normally.
### afterRun Semantics
`afterRun` fires for every terminal status -- `completed`, `aborted`, `failed`. If you only want to act on success:
```typescript
afterRun({ result }) {
if (result.status !== "completed") return
// notify, log success metrics, etc.
}
```
### Plugin Hooks vs File Hooks
The runtime supports two hook systems:
- File hooks -- external scripts in `.cline/hooks/` invoked with serialized JSON. Right for user/workspace-specific scripts that don't ship with code.
- Plugin runtime hooks -- typed in-process callbacks. Right when the behavior belongs to a reusable extension and needs typed access to the runtime.
Core adapts file hooks onto the runtime hook layer, so you don't need both. If you're shipping a plugin, write it as runtime hooks.
## Message Builders
Message builders rewrite the provider-bound message list before the model call. They run after runtime messages are converted into SDK message blocks but before core's built-in safety builder.
Use them for:
- Custom compaction policies (replace middle history with a summary).
- Redacting PII or secrets before they reach the provider.
- Reshaping context for a specific model's strengths.
```typescript
api.registerMessageBuilder({
name: "summarize-middle-history",
build(messages) {
if (estimateTokens(messages) < THRESHOLD) return messages
return [...prefix, summary, ...recent]
},
})
```
Multiple builders run in registration order; the output of one is the input of the next.
When to use `beforeModel` instead: reach for the `beforeModel` hook only if you need the runtime snapshot or want to mutate the request object itself. Pure message rewrites belong in a builder.
## Automation Events
Plugins can declare normalized event types and emit them into Cline automation. Hosts that don't have automation enabled simply ignore both -- feature-detect `ctx.automation`.
```typescript
manifest: { capabilities: ["automationEvents"] },
setup(api, ctx) {
api.registerAutomationEventType({
eventType: "github.pull_request.opened",
source: "github",
description: "A new GitHub PR was opened",
attributesSchema: { /* JSON Schema for envelope.attributes */ },
})
if (!ctx.automation) return // host has no automation
ctx.automation.ingestEvent({
eventId: "pr-1234",
eventType: "github.pull_request.opened",
source: "github",
subject: "owner/repo#1234",
occurredAt: new Date().toISOString(),
attributes: { /* ... */ },
})
}
```
## Loading a Plugin
There are three ways a plugin gets into a session:
### Auto-Discovery (CLI)
The CLI scans these directories on startup:
- `<workspace>/.cline/plugins/` -- project-scoped plugins.
- `~/.cline/plugins/` -- user-scoped plugins.
Drop a `.ts` or `.js` file in, run `cline`, done:
```bash
mkdir -p .cline/plugins
cp my-plugin.ts .cline/plugins/
cline -i "do the thing my plugin enables"
```
### Explicit extensions in SDK Config
When you build your own host with `ClineCore`, pass the plugin object directly:
```typescript
import plugin from "./my-plugin"
import { ClineCore } from "@cline/core"
const host = await ClineCore.create({ backendMode: "local" })
await host.start({
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY ?? "",
cwd: process.cwd(),
enableTools: true,
systemPrompt: "You are a helpful assistant.",
extensions: [plugin],
extensionContext: {
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
},
},
prompt: "...",
interactive: false,
})
```
### pluginPaths for Directory-Based Plugins
When the plugin is a directory with `package.json`, point `pluginPaths` at the directory:
```typescript
config: {
pluginPaths: ["./path/to/my-plugin-package"],
}
```
Or install with the CLI:
```bash
cline plugin install ./path/to/my-plugin-package
cline plugin install @scope/my-cline-plugin # from npm
cline plugin install --git github.com/owner/repo # from git
```
## Single-File Plugin Template
Save as `my-plugin.ts`, drop in `.cline/plugins/`:
```typescript
import { type AgentPlugin, ClineCore, createTool } from "@cline/core"
let sessionRoot: string | undefined
const plugin: AgentPlugin = {
name: "my-plugin",
manifest: {
capabilities: ["tools", "hooks"],
},
setup(api, ctx) {
sessionRoot = ctx.workspaceInfo?.rootPath
api.registerTool(
createTool({
name: "do_thing",
description: "Do the thing this plugin exists for.",
inputSchema: {
type: "object",
properties: { target: { type: "string" } },
required: ["target"],
},
async execute(input) {
const { target } = input as { target: string }
return { ok: true, target, root: sessionRoot }
},
}),
)
},
hooks: {
beforeRun() {
console.log("[my-plugin] run started")
},
afterRun({ result }) {
if (result.status !== "completed") return
console.log(`[my-plugin] done in ${result.iterations} iteration(s)`)
},
},
}
async function runDemo(): Promise<void> {
const host = await ClineCore.create({ backendMode: "local" })
try {
const result = await host.start({
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY ?? "",
cwd: process.cwd(),
enableTools: true,
systemPrompt: "You are a helpful assistant. Use tools when needed.",
extensions: [plugin],
extensionContext: {
workspace: { rootPath: process.cwd(), cwd: process.cwd() },
},
},
prompt: "Use do_thing on the target 'world'.",
interactive: false,
})
console.log(result.result?.text ?? "")
} finally {
await host.dispose()
}
}
if (import.meta.main) {
await runDemo()
}
export { plugin, runDemo }
export default plugin
```
Copy it, rename the tool, swap in your logic. The `runDemo()` function lets you test with `ANTHROPIC_API_KEY=sk-... bun run my-plugin.ts`.
## Plugin Package
Use a plugin package when you need npm dependencies, multiple entry points, bundled assets, or npm/git distribution.
### Layout
```
my-cline-plugin/
+-- package.json
+-- tsconfig.json (optional, for local typechecking)
+-- index.ts (the plugin entry point)
+-- README.md
+-- assets/ (optional, bundled content)
+-- templates/
+-- schemas/
```
### package.json -- The Discovery Contract
```json
{
"name": "my-cline-plugin",
"version": "0.1.0",
"private": true,
"description": "What this plugin does, in one sentence.",
"type": "module",
"exports": {
".": "./index.ts"
},
"cline": {
"plugins": [
{
"paths": ["./index.ts"],
"capabilities": ["tools", "hooks"]
}
]
},
"peerDependencies": {
"@cline/core": "*"
},
"peerDependenciesMeta": {
"@cline/core": { "optional": true }
},
"dependencies": {
"zod": "^4.1.5"
}
}
```
Key fields:
- `type: "module"` -- required. Cline plugins are ES modules.
- `cline.plugins` -- the discovery contract. Array of entries, each with `paths` (entry files) and `capabilities` (pre-declared, validated before importing).
- `peerDependencies` for `@cline/core` -- the host already provides it. Marking it optional lets you typecheck in isolation.
### Bundling Assets
Resolve asset paths with `import.meta.url`, not `process.cwd()`:
```typescript
import { dirname, join } from "node:path"
import { fileURLToPath } from "node:url"
import { readFileSync, existsSync } from "node:fs"
const MODULE_DIR = dirname(fileURLToPath(import.meta.url))
const TEMPLATES_DIR = join(MODULE_DIR, "assets", "templates")
function loadTemplate(name: string): string | undefined {
const path = join(TEMPLATES_DIR, `${name}.md`)
return existsSync(path) ? readFileSync(path, "utf8") : undefined
}
```
This is the only place `import.meta.url` is appropriate in a plugin -- locating files inside the plugin package. For workspace paths, always use `ctx.workspaceInfo?.rootPath`.
### The Override Pattern (Bundled / Global / Project)
A package can ship default assets and let users override them. The convention is a three-tier lookup, last write wins by `name`:
1. bundled -- files inside the plugin package (defaults shipped with the plugin).
2. global -- files under `~/.cline/data/settings/<kind>/` (user overrides).
3. project -- files under `<workspace>/.cline/<kind>/` (project overrides).
### Multiple Plugin Entries
If your package exposes more than one plugin, list each in `cline.plugins`:
```json
"cline": {
"plugins": [
{ "paths": ["./tools-plugin.ts"], "capabilities": ["tools"] },
{ "paths": ["./hooks-plugin.ts"], "capabilities": ["hooks"] }
]
}
```
Each entry file should `export default` its own plugin object.
## Testing Your Plugin
### Unit Tests
The plugin object is plain data. Drive `setup()` against a minimal context and exercise tools directly:
```typescript
import plugin from "../my-plugin"
const tools: unknown[] = []
const api = {
registerTool: (t: unknown) => tools.push(t),
registerCommand: () => {},
registerRule: () => {},
registerMessageBuilder: () => {},
registerProvider: () => {},
registerAutomationEventType: () => {},
}
await plugin.setup?.(api as never, {
workspaceInfo: { rootPath: "/tmp/fake-workspace" },
})
// Now `tools` contains the registered tools -- call tool.execute(input, ctx)
```
### End-to-End with runDemo()
Add a `runDemo()` in your plugin file (see the single-file template above) that boots a real `ClineCore` session:
```bash
ANTHROPIC_API_KEY=sk-... bun run my-plugin.ts
```
### CLI Smoke Test
```bash
mkdir -p .cline/plugins
cp my-plugin.ts .cline/plugins/
cline -i "trigger something that exercises the plugin"
```
For packages:
```bash
cline plugin install ./my-cline-plugin
cline -i "..."
```
If the plugin fails validation or setup, the CLI prints a clear error and continues without it.
## Common Gotchas
- "capabilities must be a non-empty array" -- you forgot `manifest.capabilities`, or it's `[]`.
- "registerRule requires the 'rules' capability" -- capability/handler drift. Add `"rules"` to capabilities, or stop calling `registerRule`.
- Tool not visible to the model -- check `enableTools: true` on the session config, and that you're declaring `"tools"` in capabilities.
- `ctx.workspaceInfo` is undefined in SDK tests -- the host didn't pass `extensionContext.workspace`. In SDK code, set it explicitly (see the ClineCore loading example above).
- State leaking across sessions -- module-level variables are shared across sessions in the same process. Key by `ctx.session?.sessionId` if your host runs multiple sessions concurrently.
- `afterRun` firing on aborts -- guard with `if (result.status !== "completed") return`.
- Heavy work in `setup()` -- `setup()` blocks session start. Defer expensive work into the first tool call or `beforeRun`.
- Importing host internals -- only import from `@cline/core`. Reaching into host-specific packages (e.g. CLI internals) will break in non-CLI hosts.
- Sandboxed plugins and `telemetry` -- telemetry is process-local. Feature-detect `ctx.telemetry` and expect it to be undefined in sandboxed plugin processes.
- Resolving bundled assets -- use `import.meta.url` + `fileURLToPath` to find files inside your package; never `process.cwd()`. For workspace paths, do the opposite: use `ctx.workspaceInfo?.rootPath`, never `import.meta.url`.
- Plugin name collisions -- `name` must be unique within a session. If two plugins share a name, validation fails. Namespace by package (`my-org-redactor`, not `redactor`).
## Decision Guide -- Which Extension Point?
| You want to... | Use |
|----------------|-----|
| Give the model a new capability | `registerTool` |
| Add a slash command in chat surfaces | `registerCommand` |
| Inject text into the system prompt | `registerRule` |
| Rewrite messages before they hit the provider | `registerMessageBuilder` |
| Add a custom model provider | `registerProvider` |
| Emit normalized cron/webhook events | `registerAutomationEventType` + `ctx.automation` |
| Observe or steer the agent loop | `hooks.*` |
| Block a dangerous tool call | `hooks.beforeTool` returning `{ stop: true }` |
| Notify on completion | `hooks.afterRun` (gate on `status === "completed"`) |
| Tweak each model request | `hooks.beforeModel` |
| Stream events to a UI | `hooks.onEvent` |
| Ship reusable templates with the plugin | Bundle assets next to `index.ts`, resolve via `import.meta.url` |
| Let users override defaults globally or per-project | Three-tier lookup: bundled / global / project |
## Pre-Ship Checklist
- `manifest.capabilities` is a non-empty array.
- Every `api.register*` call has a matching capability declared.
- If `hooks` is present, `"hooks"` is in `capabilities`.
- `ctx.workspaceInfo?.rootPath` is used for workspace paths (not `process.cwd()`).
- Optional `ctx` fields are feature-detected.
- Tool names are snake_case verbs; descriptions are written for the model.
- Tool inputs have JSON Schema with `required` set.
- `afterRun` handlers gate on `result.status === "completed"` if they only want successes.
- State that must not leak between concurrent sessions is keyed by `ctx.session?.sessionId`.
- (Package) `package.json` has `type: "module"`, `cline.plugins`, and `@cline/core` as an optional peer dep.
- (Package) Bundled assets resolved via `import.meta.url`, not `process.cwd()`.
- Smoke test: drop the plugin into `.cline/plugins/` (or `cline plugin install`), run `cline -i "..."`, watch it work.
## Plugin Examples from SDK
The SDK repo includes these example plugins:
| Plugin | Description |
|--------|-------------|
| `weather-metrics.ts` | Tool registration + lifecycle metrics |
| `mac-notify.ts` | macOS Notification Center alerts |
| `custom-compaction.ts` | Custom message compaction via message builders |
| `background-terminal.ts` | Detached shell job management |
| `automation-events.ts` | Plugin-emitted automation events |
| `gitignore-read-files-guard.ts` | File access policy enforcement via beforeTool |
| `web-search.ts` | Web search via Exa API |
| `typescript-lsp/` | TypeScript Language Service tools (plugin package) |
| `agents-squad/` | Multi-agent team orchestration (plugin package) |
## See Also
- `../tools/REFERENCE.md` - Tool creation
- `../events/REFERENCE.md` - Event system
- `../agent/REFERENCE.md` - Using plugins with Agent
- `../clinecore/REFERENCE.md` - Using plugins with ClineCore
@@ -0,0 +1,253 @@
# Going to Production
Guidelines for deploying Cline SDK agents in production environments.
## Error Handling
Always check the result status:
```typescript
const result = await agent.run(input)
switch (result.status) {
case "completed":
console.log("Success:", result.outputText)
break
case "aborted":
console.log("Cancelled:", result.error?.message)
break
case "failed":
console.error("Failed:", result.error)
break
}
```
For ClineCore, check `finishReason`:
```typescript
const session = await cline.start({ ... })
switch (session.result?.finishReason) {
case "completed":
// normal completion
break
case "max_iterations":
// agent hit iteration limit
break
case "aborted":
// manually cancelled
break
case "mistake_limit":
// too many tool errors
break
case "error":
// unrecoverable error
break
}
```
## Cost Control
### Token Limits
Set maximum tokens per turn and iteration limits:
```typescript
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
maxTokensPerTurn: 4096,
maxIterations: 10,
tools: [...],
})
```
### Model Selection
Use cheaper models for simple tasks:
```typescript
// Simple classification or formatting
{ providerId: "anthropic", modelId: "claude-haiku-4-5" }
// Complex reasoning and code generation
{ providerId: "anthropic", modelId: "claude-sonnet-4-6" }
// Hardest tasks requiring deep reasoning
{ providerId: "anthropic", modelId: "claude-opus-4-7" }
```
### Usage Tracking
Monitor spending in real time:
```typescript
agent.subscribe((event) => {
if (event.type === "usage-updated" && event.usage.totalCost) {
if (event.usage.totalCost > MAX_BUDGET) {
agent.abort("Budget exceeded")
}
}
})
```
## Observability
### OpenTelemetry Integration
The SDK supports OpenTelemetry for traces, metrics, and logs:
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create({
clientName: "my-app",
// OpenTelemetry config is picked up from environment
// OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, etc.
})
```
### Structured Logging
Use the `BasicLogger` interface for injectable logging:
```typescript
import type { BasicLogger } from "@cline/sdk"
const logger: BasicLogger = {
debug: (msg, meta) => console.debug(msg, meta),
log: (msg, meta) => console.log(msg, meta),
error: (msg, meta) => console.error(msg, meta),
}
await cline.start({
config: {
logger,
// ...
},
})
```
### Custom Metrics via Plugins
```typescript
const metricsPlugin: AgentPlugin = {
name: "metrics",
manifest: { capabilities: ["hooks"] },
setup() {},
hooks: {
beforeRun() {
metrics.increment("agent.runs.started")
},
afterRun({ result }) {
metrics.increment("agent.runs.completed")
metrics.histogram("agent.iterations", result.iterations)
metrics.histogram("agent.tokens.output", result.usage.outputTokens)
},
beforeTool({ toolCall }) {
metrics.increment(`agent.tools.${toolCall.toolName}`)
},
},
}
```
## Security
### Sandbox Tool Execution
Validate tool inputs to prevent path traversal and injection:
```typescript
execute: async (input) => {
const safePath = path.resolve(WORKSPACE_ROOT, input.path)
if (!safePath.startsWith(WORKSPACE_ROOT)) {
return { error: "Path traversal attempt blocked" }
}
return await readFile(safePath, "utf-8")
}
```
### API Key Management
- Use environment variables, never hardcode keys
- Rotate keys regularly
- Use different keys for development and production
```typescript
{
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY, // never a literal string
}
```
### Tool Policy Hardening
Disable tools you don't need and require approval for dangerous ones:
```typescript
toolPolicies: {
read_files: { autoApprove: true },
search: { autoApprove: true },
bash: { autoApprove: false }, // require approval
editor: { autoApprove: false },
apply_patch: { autoApprove: false },
fetch_web: { enabled: false }, // disable entirely
}
```
## Deployment Patterns
### Stateless Worker
For request/response workloads (API endpoints, queue consumers):
```typescript
const cline = await ClineCore.create({
clientName: "worker",
backendMode: "local",
})
app.post("/agent", async (req, res) => {
const session = await cline.start({
prompt: req.body.prompt,
config: { ... },
})
res.json({ text: session.result?.text, usage: session.result?.usage })
})
```
### Persistent Service
For long-running services with session management:
```typescript
const cline = await ClineCore.create({
clientName: "service",
backendMode: "hub",
})
process.on("SIGTERM", async () => {
await cline.dispose("SIGTERM")
process.exit(0)
})
```
### Scheduled Automation
See `../scheduling/REFERENCE.md` for recurring agent tasks.
## Retry and Resilience
- Tool `execute` functions support `retryable: true` (default) and `maxRetries: 3` (default)
- Provider API calls are retried automatically on transient failures
- Use `timeoutMs` on tools to prevent hanging
- Monitor `mistake_limit` finish reason to detect systematic tool failures
## See Also
- `../agent/REFERENCE.md` - Agent overview
- `../clinecore/REFERENCE.md` - ClineCore overview
- `../tools/REFERENCE.md` - Tool configuration
- `../plugins/REFERENCE.md` - Metrics plugins
- `../scheduling/REFERENCE.md` - Scheduled agents
@@ -0,0 +1,257 @@
# Model Providers
The Cline SDK supports every major LLM provider out of the box via `@cline/llms`.
## Supported Providers
| Provider ID | Models |
|-------------|--------|
| `"anthropic"` | Claude Opus 4.7, Sonnet 4.6, Haiku 4.5 |
| `"openai"` | GPT-5.5, GPT-5.3 Codex |
| `"gemini"` | Gemini 3.1 Pro Preview, Gemini 3 Flash Preview |
| `"vertex"` | Google models via Vertex AI |
| `"bedrock"` | Claude, Llama via AWS Bedrock |
| `"mistral"` | Mistral Large, Codestral |
| `"openai-compatible"` | vLLM, Together, Fireworks, Groq, etc. |
## Basic Configuration
### With Agent
```typescript
import { Agent } from "@cline/sdk"
const agent = new Agent({
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
systemPrompt: "You are a helpful assistant.",
tools: [],
})
```
### With ClineCore
```typescript
import { ClineCore } from "@cline/sdk"
const cline = await ClineCore.create({ clientName: "my-app" })
await cline.start({
prompt: "Hello",
config: {
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.ANTHROPIC_API_KEY,
},
})
```
## Provider-Specific Configuration
### Anthropic
```typescript
{
providerId: "anthropic",
modelId: "claude-opus-4-7", // or "claude-sonnet-4-6", "claude-haiku-4-5"
apiKey: process.env.ANTHROPIC_API_KEY,
}
```
### OpenAI
```typescript
{
providerId: "openai",
modelId: "gpt-5.5",
apiKey: process.env.OPENAI_API_KEY,
}
```
### Google (Gemini)
```typescript
{
providerId: "gemini",
modelId: "gemini-3.1-pro-preview",
apiKey: process.env.GOOGLE_API_KEY,
}
```
### Google (Vertex AI)
```typescript
{
providerId: "vertex",
modelId: "gemini-3.1-pro-preview",
// Uses application default credentials or service account
}
```
### AWS Bedrock
```typescript
{
providerId: "bedrock",
modelId: "anthropic.claude-sonnet-4-6",
// Uses AWS credential chain (env vars, config file, IAM role)
// Set AWS_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
}
```
### Mistral
```typescript
{
providerId: "mistral",
modelId: "mistral-large-latest",
apiKey: process.env.MISTRAL_API_KEY,
}
```
### OpenAI-Compatible
For any provider with an OpenAI-compatible API:
```typescript
{
providerId: "openai-compatible",
modelId: "my-model",
apiKey: process.env.API_KEY,
baseUrl: "https://api.together.xyz/v1",
}
```
Works with: vLLM, Together AI, Fireworks, Groq, Ollama, LiteLLM, etc.
## Custom Base URL
Override the API endpoint for any provider:
```typescript
{
providerId: "anthropic",
modelId: "claude-sonnet-4-6",
apiKey: process.env.API_KEY,
baseUrl: "https://my-proxy.example.com/v1",
}
```
## Custom Headers
Pass additional headers to API requests:
```typescript
{
providerId: "openai",
modelId: "gpt-5.5",
apiKey: process.env.API_KEY,
headers: {
"X-Custom-Header": "value",
},
}
```
## Gateway API
For advanced multi-provider setups, use the Gateway directly:
```typescript
import { createGateway, DefaultGateway } from "@cline/llms"
const gateway = createGateway({
providerConfigs: [
{ providerId: "anthropic", apiKey: process.env.ANTHROPIC_API_KEY },
{ providerId: "openai", apiKey: process.env.OPENAI_API_KEY },
],
})
// Create a model for a specific provider
const model = gateway.createAgentModel({
providerId: "anthropic",
modelId: "claude-opus-4-7",
})
// Use with Agent
const agent = new Agent({ model, systemPrompt: "...", tools: [] })
```
### Gateway Methods
```typescript
gateway.registerProvider(registration) // add a custom provider
gateway.configureProvider(config) // update provider settings
gateway.listProviders() // list available providers
gateway.listModels(providerId?) // list available models
gateway.createAgentModel(selection) // create model for agent
gateway.stream(request) // raw streaming (AsyncIterable)
```
## Provider Registry
Query and register providers programmatically:
```typescript
import {
getAllProviders,
getProviderIds,
getProvider,
getModelsForProvider,
registerProvider,
registerModel,
createHandler,
} from "@cline/llms"
// List all registered providers
const providers = getAllProviders()
// Get models for a provider
const models = getModelsForProvider("anthropic")
// Register a custom provider
registerProvider({
id: "my-provider",
name: "My Custom Provider",
handler: createHandler({ ... }),
})
```
## Model Metadata
Access model info (context window, pricing, capabilities):
```typescript
import { getModelsForProvider } from "@cline/llms"
const models = getModelsForProvider("anthropic")
for (const model of models) {
console.log(`${model.id}: context=${model.contextWindow}, input=$${model.inputPrice}/MTok`)
}
```
## Cost Tracking
Track per-request and cumulative costs:
```typescript
// Via events
agent.subscribe((event) => {
if (event.type === "usage-updated") {
console.log(`Cost: $${event.usage.totalCost?.toFixed(4)}`)
}
})
// Via result
const result = await agent.run("...")
console.log(`Total cost: $${result.usage.totalCost?.toFixed(4)}`)
// Via ClineCore accumulated usage
const usage = await cline.getAccumulatedUsage(sessionId)
```
## See Also
- `../agent/REFERENCE.md` - Using providers with Agent
- `../clinecore/REFERENCE.md` - Using providers with ClineCore
- `../production/REFERENCE.md` - Cost control in production
@@ -0,0 +1,227 @@
# Scheduling and Automation
The Cline SDK supports scheduled, one-off, and event-driven agent execution through the automation subsystem in `@cline/core`.
## Overview
Three trigger types:
| Trigger | Description |
|---------|-------------|
| `schedule` | Recurring jobs via cron expressions |
| `one_off` | Single execution tasks |
| `event` | Triggered by external events (GitHub, Linear, custom) |
## CLI Schedule Management
```bash
# Create a recurring schedule
cline schedule create "Daily standup" \
--cron "0 9 * * MON-FRI" \
--prompt "Summarize open PRs and blockers" \
--workspace /path/to/project \
--model anthropic/claude-sonnet-4-6
# List schedules
cline schedule list
# Trigger a schedule immediately
cline schedule trigger <schedule-id>
# Pause/resume
cline schedule pause <schedule-id>
cline schedule resume <schedule-id>
# Delete
cline schedule delete <schedule-id>
# View past executions
cline schedule executions <schedule-id>
```
## Cron Expressions
| Expression | Meaning |
|-----------|---------|
| `0 9 * * MON-FRI` | 9 AM weekdays |
| `0 */6 * * *` | Every 6 hours |
| `0 8 * * MON` | Mondays at 8 AM |
| `*/30 * * * *` | Every 30 minutes |
| `0 0 1 * *` | First of every month |
## File-Based Specs
Create Markdown files in `~/.cline/cron/` (global) or `.cline/cron/` (workspace):
### Recurring Schedule
```markdown
---
trigger: schedule
schedule: "0 9 * * MON-FRI"
timezone: America/New_York
mode: exclusive
prompt: "Check for dependency updates and create PRs for any outdated packages."
modelSelection:
providerId: anthropic
modelId: claude-sonnet-4-6
tools:
enabled: true
---
Additional context or instructions for the agent go in the body.
```
### One-Off Task
```markdown
---
trigger: one_off
prompt: "Generate a comprehensive test coverage report."
modelSelection:
providerId: anthropic
modelId: claude-sonnet-4-6
---
```
### Event-Driven
```markdown
---
trigger: event
eventType: github.pull_request.opened
filters:
repository: myorg/myrepo
debounceMs: 5000
cooldownMs: 60000
prompt: "Review the PR for security issues and code quality."
modelSelection:
providerId: anthropic
modelId: claude-sonnet-4-6
---
```
## CronSpec Types
```typescript
interface CronScheduleSpec {
trigger: "schedule"
schedule: string // cron expression
timezone?: string
mode?: "exclusive" | "concurrent"
prompt: string
modelSelection?: { providerId: string; modelId?: string }
extensionLoading?: "isolated" | "direct"
configExtensions?: RuntimeConfigExtensionKind[]
tools?: { enabled?: boolean; names?: string[] }
}
interface CronOneOffSpec {
trigger: "one_off"
prompt: string
modelSelection?: { providerId: string; modelId?: string }
}
interface CronEventSpec {
trigger: "event"
eventType: string // e.g., "github.pull_request.opened"
filters?: Record<string, unknown>
debounceMs?: number
cooldownMs?: number
prompt: string
modelSelection?: { providerId: string; modelId?: string }
}
```
## Programmatic Automation API
```typescript
const cline = await ClineCore.create({
clientName: "my-app",
automation: true,
})
// Start automation service
cline.automation.start()
// Ingest an external event
cline.automation.ingestEvent({
eventId: "evt-123",
eventType: "github.pull_request.opened",
source: "github",
timestamp: Date.now(),
payload: { pr: { number: 42, title: "..." } },
})
// List specs, runs, events
const specs = await cline.automation.listSpecs()
const runs = await cline.automation.listRuns()
const events = await cline.automation.listEvents()
// Reconcile specs from directory
await cline.automation.reconcile(specDirectory)
// Stop automation
cline.automation.stop()
```
## Event Ingestion from Plugins
Plugins can declare and emit automation events:
```typescript
const webhookPlugin: AgentPlugin = {
name: "webhook-events",
manifest: { capabilities: ["automationEvents"] },
setup(api) {
api.registerAutomationEventType({
type: "webhook.received",
description: "External webhook received",
})
},
}
```
Submit events via the plugin context:
```typescript
ctx.automation.ingestEvent({
eventId: "evt-456",
eventType: "webhook.received",
source: "custom",
timestamp: Date.now(),
payload: { ... },
})
```
## Concurrency Control
| Mode | Behavior |
|------|----------|
| `"exclusive"` | Skip if previous run still active |
| `"concurrent"` | Allow overlapping runs |
## Run Reports
Each completed run writes a Markdown report to `.cline/cron/reports/<run-id>.md` with:
- Run metadata (spec, trigger, timing)
- Summary of agent output
- Usage (tokens, cost)
- Tool calls made
- Trigger event context (for event-driven runs)
## Use Cases
- Daily standup summaries
- Automated dependency update checks
- PR review on open
- Codebase health reports
- Scheduled security scans
- Event-driven CI/CD workflows
## See Also
- `../clinecore/REFERENCE.md` - ClineCore runtime
- `../clinecore/api.md` - Automation API details
- `../plugins/REFERENCE.md` - Plugin events
- `../production/REFERENCE.md` - Production deployment
@@ -0,0 +1,259 @@
# Tools
Tools are how agents interact with the world. The Cline SDK supports both built-in tools (via ClineCore) and custom tools you define yourself.
## Creating Custom Tools
Use `createTool()` from `@cline/sdk` (or `@cline/shared`):
```typescript
import { createTool } from "@cline/sdk"
const myTool = createTool({
name: "search_issues",
description: "Search GitHub issues by query. Returns up to 10 results.",
inputSchema: {
type: "object",
properties: {
query: { type: "string", description: "Search query" },
state: { type: "string", enum: ["open", "closed", "all"] },
},
required: ["query"],
},
execute: async (input) => {
const issues = await github.searchIssues(input.query, input.state)
return { issues, count: issues.length }
},
})
```
### With Zod Schema
```typescript
import { createTool } from "@cline/sdk"
import { z } from "zod"
const deployTool = createTool({
name: "deploy",
description: "Deploy the app to the specified environment.",
inputSchema: z.object({
environment: z.enum(["staging", "production"]).describe("Target environment"),
version: z.string().optional().describe("Version tag, defaults to latest"),
}),
execute: async (input) => {
const result = await deploy(input.environment, input.version)
return { url: result.url, status: "deployed" }
},
})
```
### Tool Config Options
```typescript
createTool({
name: string, // snake_case, unique per agent
description: string, // what the tool does (model reads this)
inputSchema: JSONSchema | ZodSchema, // input validation
execute: async (input, context, onChange?) => output,
timeoutMs?: number, // default: 30000
retryable?: boolean, // default: true
maxRetries?: number, // default: 3
lifecycle?: {
completesRun?: boolean // true = ends agent loop on success
},
})
```
### AgentToolContext
The second argument to `execute` provides runtime context:
```typescript
interface AgentToolContext {
agentId: string
conversationId: string
iteration: number
abortSignal?: AbortSignal
metadata?: Record<string, unknown>
}
```
## Tool Naming Rules
- Names must be `snake_case` (e.g., `search_issues`, `deploy_app`)
- Names must be unique within a single agent's tool set
- Choose descriptive names since the model uses them to decide which tool to call
## Tool Descriptions Matter
The model reads the tool description to decide when and how to use it. Write clear, specific descriptions:
```typescript
// Bad: vague
description: "Does deployment stuff"
// Good: specific with constraints
description: "Deploy the application to staging or production. " +
"Staging deployments are immediate. Production requires a passing CI build. " +
"Returns the deployment URL and status."
```
Include constraints, rate limits, and expected behavior in the description.
## Error Handling in Tools
Return errors as structured data instead of throwing:
```typescript
// Good: return error data
execute: async (input) => {
const file = await readFile(input.path).catch(() => null)
if (!file) {
return { error: "File not found", path: input.path }
}
return { content: file }
}
```
Thrown exceptions count as "mistakes" against the agent's mistake limit. Returned error data lets the agent adjust its approach.
## Completion Tools
Tools with `lifecycle: { completesRun: true }` end the agent loop when they execute successfully:
```typescript
const submitAnswer = createTool({
name: "submit_answer",
description: "Submit the final answer and end the task.",
inputSchema: z.object({
answer: z.string(),
confidence: z.number().min(0).max(1),
}),
lifecycle: { completesRun: true },
execute: async (input) => input,
})
```
The model sees the tool result and the run ends. Access the output via `result.toolCalls`.
## Built-in Tools (ClineCore Only)
When using `ClineCore` with `enableTools: true`, these tools are available automatically:
| Tool | Name | What It Does |
|------|------|-------------|
| Shell | `bash` | Execute shell commands in the session workspace |
| Editor | `editor` | Create and edit files |
| Read | `read_files` | Read file contents |
| Patch | `apply_patch` | Apply unified diffs to files |
| Search | `search` | Search file contents and directory structure |
| Web | `fetch_web` | Fetch web content via HTTP |
Built-in tools respect the `cwd` setting in `CoreSessionConfig`.
## Tool Policies
Control which tools are available and whether they require approval:
```typescript
// In Agent config
const agent = new Agent({
tools: [toolA, toolB, toolC],
toolPolicies: {
tool_a: { autoApprove: true }, // runs without asking
tool_b: { autoApprove: false }, // requires approval
tool_c: { enabled: false }, // hidden from model
},
})
// In ClineCore session
await cline.start({
prompt: "...",
config: { ... },
toolPolicies: {
bash: { autoApprove: true },
editor: { autoApprove: false },
},
})
```
### Policy Options
| Policy | Effect |
|--------|--------|
| `{ autoApprove: true }` | Tool runs without approval |
| `{ autoApprove: false }` | Triggers approval callback before running |
| `{ enabled: false }` | Tool is hidden from the model entirely |
| No policy set | Defaults to enabled and auto-approved |
## Abort Signal in Long-Running Tools
Respect the abort signal for tools that take a long time:
```typescript
execute: async (input, context) => {
const results = []
for (const item of input.items) {
if (context.abortSignal?.aborted) {
return { results, aborted: true, processed: results.length }
}
results.push(await processItem(item))
}
return { results, processed: results.length }
}
```
## Streaming Tool Output
Use the `onChange` callback (third argument) to stream partial results:
```typescript
execute: async (input, context, onChange) => {
let progress = 0
for (const step of steps) {
progress++
onChange?.(`Processing step ${progress}/${steps.length}...`)
await processStep(step)
}
return { completed: true }
}
```
## Testing Tools
Tools are plain async functions, so they're straightforward to test:
```typescript
import { describe, it, expect } from "vitest"
describe("deploy tool", () => {
it("deploys to staging", async () => {
const context = { agentId: "test", conversationId: "test", iteration: 1 }
const result = await deployTool.execute({ environment: "staging" }, context)
expect(result.status).toBe("deployed")
})
})
```
## MCP Tool Integration
ClineCore can connect to MCP (Model Context Protocol) servers for additional tools. Configure in `.cline/mcp-servers.json`:
```json
{
"servers": {
"my-server": {
"command": "node",
"args": ["./mcp-server.js"]
}
}
}
```
MCP tools appear alongside built-in and custom tools automatically.
## See Also
- `../agent/REFERENCE.md` - Using tools with Agent
- `../clinecore/REFERENCE.md` - Using tools with ClineCore
- `../plugins/REFERENCE.md` - Packaging tools as plugins
+200
View File
@@ -0,0 +1,200 @@
---
name: opentui
description: Comprehensive OpenTUI skill for building terminal user interfaces. Covers the core imperative API, React reconciler, and Solid reconciler. Use for any TUI development task including components, layout, keyboard handling, animations, and testing.
metadata:
references: core, react, solid
---
# OpenTUI Platform Skill
Consolidated skill for building terminal user interfaces with OpenTUI. Use decision trees below to find the right framework and components, then load detailed references.
## Critical Rules
**Follow these rules in all OpenTUI code:**
1. **Use `create-tui` for new projects.** See framework `REFERENCE.md` quick starts.
2. **`create-tui` options must come before arguments.** `bunx create-tui -t react my-app` works, `bunx create-tui my-app -t react` does NOT.
3. **Never call `process.exit()` directly.** Use `renderer.destroy()` (see `core/gotchas.md`).
4. **Text styling requires nested tags in React/Solid.** Use modifier elements, not props (see `components/text-display.md`).
## How to Use This Skill
### Reference File Structure
Framework references follow a 5-file pattern. Cross-cutting concepts are single-file guides.
Each framework in `./references/<framework>/` contains:
| File | Purpose | When to Read |
|------|---------|--------------|
| `REFERENCE.md` | Overview, when to use, quick start | **Always read first** |
| `api.md` | Runtime API, components, hooks | Writing code |
| `configuration.md` | Setup, tsconfig, bundling | Configuring a project |
| `patterns.md` | Common patterns, best practices | Implementation guidance |
| `gotchas.md` | Pitfalls, limitations, debugging | Troubleshooting |
Cross-cutting concepts in `./references/<concept>/` have `REFERENCE.md` as the entry point.
### Reading Order
1. Start with `REFERENCE.md` for your chosen framework
2. Then read additional files relevant to your task:
- Building components -> `api.md` + `components/<category>.md`
- Setting up project -> `configuration.md`
- Layout/positioning -> `layout/REFERENCE.md`
- Keyboard/input handling -> `keyboard/REFERENCE.md`
- Animations -> `animation/REFERENCE.md`
- Troubleshooting -> `gotchas.md` + `testing/REFERENCE.md`
### Example Paths
```
./references/react/REFERENCE.md # Start here for React
./references/react/api.md # React components and hooks
./references/solid/configuration.md # Solid project setup
./references/components/inputs.md # Input, Textarea, Select docs
./references/core/gotchas.md # Core debugging tips
```
### Runtime Notes
OpenTUI runs on Bun and uses Zig for native builds. Read `./references/core/gotchas.md` for runtime requirements and build guidance.
## Quick Decision Trees
### "Which framework should I use?"
```
Which framework?
├─ I want full control, maximum performance, no framework overhead
│ └─ core/ (imperative API)
├─ I know React, want familiar component patterns
│ └─ react/ (React reconciler)
├─ I want fine-grained reactivity, optimal re-renders
│ └─ solid/ (Solid reconciler)
└─ I'm building a library/framework on top of OpenTUI
└─ core/ (imperative API)
```
### "I need to display content"
```
Display content?
├─ Plain or styled text -> components/text-display.md
├─ Container with borders/background -> components/containers.md
├─ Scrollable content area -> components/containers.md (scrollbox)
├─ ASCII art banner/title -> components/text-display.md (ascii-font)
├─ Data table with borders/wrapping -> components/code-diff.md (TextTable)
├─ Code with syntax highlighting -> components/code-diff.md
├─ Diff viewer (unified/split) -> components/code-diff.md
├─ Line numbers with diagnostics -> components/code-diff.md
└─ Markdown content (streaming) -> components/code-diff.md (markdown)
```
### "I need user input"
```
User input?
├─ Single-line text field -> components/inputs.md (input)
├─ Multi-line text editor -> components/inputs.md (textarea)
├─ Select from a list (vertical) -> components/inputs.md (select)
├─ Tab-based selection (horizontal) -> components/inputs.md (tab-select)
└─ Custom keyboard shortcuts -> keyboard/REFERENCE.md
```
### "I need layout/positioning"
```
Layout?
├─ Flexbox-style layouts (row, column, wrap) -> layout/REFERENCE.md
├─ Absolute positioning -> layout/patterns.md
├─ Responsive to terminal size -> layout/patterns.md
├─ Centering content -> layout/patterns.md
└─ Complex nested layouts -> layout/patterns.md
```
### "I need animations"
```
Animations?
├─ Timeline-based animations -> animation/REFERENCE.md
├─ Easing functions -> animation/REFERENCE.md
├─ Property transitions -> animation/REFERENCE.md
└─ Looping animations -> animation/REFERENCE.md
```
### "I need to handle input"
```
Input handling?
├─ Keyboard events (keypress, release) -> keyboard/REFERENCE.md
├─ Focus management -> keyboard/REFERENCE.md
├─ Paste events -> keyboard/REFERENCE.md
├─ Mouse events -> components/containers.md
├─ Text selection & copy-on-select -> keyboard/REFERENCE.md (selection)
└─ Clipboard (OSC 52) -> keyboard/REFERENCE.md (clipboard)
```
### "I need to test my TUI"
```
Testing?
├─ Snapshot testing -> testing/REFERENCE.md
├─ Interaction testing -> testing/REFERENCE.md
├─ Test renderer setup -> testing/REFERENCE.md
└─ Debugging tests -> testing/REFERENCE.md
```
### "I need to debug/troubleshoot"
```
Troubleshooting?
├─ Runtime errors, crashes -> <framework>/gotchas.md
├─ Layout issues -> layout/REFERENCE.md + layout/patterns.md
├─ Input/focus issues -> keyboard/REFERENCE.md
└─ Repro + regression tests -> testing/REFERENCE.md
```
### Troubleshooting Index
- Terminal cleanup, crashes -> `core/gotchas.md`
- Text styling not applying -> `components/text-display.md`
- Input focus/shortcuts -> `keyboard/REFERENCE.md`
- Layout misalignment -> `layout/REFERENCE.md`
- Flaky snapshots -> `testing/REFERENCE.md`
For component naming differences and text modifiers, see `components/REFERENCE.md`.
## Product Index
### Frameworks
| Framework | Entry File | Description |
|-----------|------------|-------------|
| Core | `./references/core/REFERENCE.md` | Imperative API, all primitives |
| React | `./references/react/REFERENCE.md` | React reconciler for declarative TUI |
| Solid | `./references/solid/REFERENCE.md` | SolidJS reconciler for declarative TUI |
### Cross-Cutting Concepts
| Concept | Entry File | Description |
|---------|------------|-------------|
| Layout | `./references/layout/REFERENCE.md` | Yoga/Flexbox layout system |
| Components | `./references/components/REFERENCE.md` | Component reference by category |
| Keyboard | `./references/keyboard/REFERENCE.md` | Keyboard input handling |
| Animation | `./references/animation/REFERENCE.md` | Timeline-based animations |
| Testing | `./references/testing/REFERENCE.md` | Test renderer and snapshots |
### Component Categories
| Category | Entry File | Components |
|----------|------------|------------|
| Text & Display | `./references/components/text-display.md` | text, ascii-font, styled text |
| Containers | `./references/components/containers.md` | box, scrollbox, borders |
| Inputs | `./references/components/inputs.md` | input, textarea, select, tab-select |
| Code & Diff | `./references/components/code-diff.md` | code, line-number, diff, markdown, text-table |
## Resources
**Repository**: https://github.com/anomalyco/opentui
**Core Docs**: https://github.com/anomalyco/opentui/tree/main/packages/core/docs
**Examples**: https://github.com/anomalyco/opentui/tree/main/packages/core/src/examples
**Awesome List**: https://github.com/msmps/awesome-opentui
@@ -0,0 +1,431 @@
# Animation System
OpenTUI provides a timeline-based animation system for smooth property transitions.
## Overview
Animations in OpenTUI use:
- **Timeline**: Orchestrates multiple animations
- **Animation Engine**: Manages timelines and rendering
- **Easing Functions**: Control animation curves
## When to Use
Use this reference when you need timeline-driven animations, easing curves, or progressive transitions.
## Basic Usage
### React
```tsx
import { useTimeline } from "@opentui/react"
import { useEffect, useState } from "react"
function AnimatedBox() {
const [width, setWidth] = useState(0)
const timeline = useTimeline({
duration: 2000,
})
useEffect(() => {
timeline.add(
{ width: 0 },
{
width: 50,
duration: 2000,
ease: "easeOutQuad",
onUpdate: (anim) => {
setWidth(Math.round(anim.targets[0].width))
},
}
)
}, [])
return (
<box
width={width}
height={3}
backgroundColor="#6a5acd"
/>
)
}
```
### Solid
```tsx
import { useTimeline } from "@opentui/solid"
import { createSignal, onMount } from "solid-js"
function AnimatedBox() {
const [width, setWidth] = createSignal(0)
const timeline = useTimeline({
duration: 2000,
})
onMount(() => {
timeline.add(
{ width: 0 },
{
width: 50,
duration: 2000,
ease: "easeOutQuad",
onUpdate: (anim) => {
setWidth(Math.round(anim.targets[0].width))
},
}
)
})
return (
<box
width={width()}
height={3}
backgroundColor="#6a5acd"
/>
)
}
```
### Core
```typescript
import { createCliRenderer, Timeline, engine } from "@opentui/core"
const renderer = await createCliRenderer()
engine.attach(renderer)
const timeline = new Timeline({
duration: 2000,
autoplay: true,
})
timeline.add(
{ x: 0 },
{
x: 50,
duration: 2000,
ease: "easeOutQuad",
onUpdate: (anim) => {
box.setLeft(Math.round(anim.targets[0].x))
},
}
)
engine.addTimeline(timeline)
```
## Timeline Options
```typescript
const timeline = useTimeline({
duration: 2000, // Total duration in ms
loop: false, // Loop the timeline
autoplay: true, // Start automatically
onComplete: () => {}, // Called when timeline completes
onPause: () => {}, // Called when timeline pauses
})
```
## Timeline Methods
```typescript
// Add animation
timeline.add(target, properties, startTime?)
// Control playback
timeline.play() // Start/resume
timeline.pause() // Pause
timeline.restart() // Restart from beginning
// State
timeline.progress // Current progress (0-1)
timeline.duration // Total duration
```
## Animation Properties
```typescript
timeline.add(
{ value: 0 }, // Target object with initial values
{
value: 100, // Final value
duration: 1000, // Animation duration in ms
ease: "linear", // Easing function
delay: 0, // Delay before starting
onUpdate: (anim) => {
// Called each frame
const current = anim.targets[0].value
},
onComplete: () => {
// Called when this animation completes
},
},
0 // Start time in timeline (optional)
)
```
## Easing Functions
Available easing functions:
### Linear
| Name | Description |
|------|-------------|
| `linear` | Constant speed |
### Quad (Power of 2)
| Name | Description |
|------|-------------|
| `easeInQuad` | Slow start |
| `easeOutQuad` | Slow end |
| `easeInOutQuad` | Slow start and end |
### Cubic (Power of 3)
| Name | Description |
|------|-------------|
| `easeInCubic` | Slower start |
| `easeOutCubic` | Slower end |
| `easeInOutCubic` | Slower start and end |
### Quart (Power of 4)
| Name | Description |
|------|-------------|
| `easeInQuart` | Even slower start |
| `easeOutQuart` | Even slower end |
| `easeInOutQuart` | Even slower start and end |
### Expo (Exponential)
| Name | Description |
|------|-------------|
| `easeInExpo` | Exponential start |
| `easeOutExpo` | Exponential end |
| `easeInOutExpo` | Exponential start and end |
### Back (Overshoot)
| Name | Description |
|------|-------------|
| `easeInBack` | Pull back, then forward |
| `easeOutBack` | Overshoot, then settle |
| `easeInOutBack` | Both |
### Elastic
| Name | Description |
|------|-------------|
| `easeInElastic` | Elastic start |
| `easeOutElastic` | Elastic end (bouncy) |
| `easeInOutElastic` | Both |
### Bounce
| Name | Description |
|------|-------------|
| `easeInBounce` | Bounce at start |
| `easeOutBounce` | Bounce at end |
| `easeInOutBounce` | Both |
## Patterns
### Progress Bar
```tsx
function ProgressBar({ progress }: { progress: number }) {
const [width, setWidth] = useState(0)
const maxWidth = 50
const timeline = useTimeline()
useEffect(() => {
timeline.add(
{ value: width },
{
value: (progress / 100) * maxWidth,
duration: 300,
ease: "easeOutQuad",
onUpdate: (anim) => {
setWidth(Math.round(anim.targets[0].value))
},
}
)
}, [progress])
return (
<box flexDirection="column" gap={1}>
<text>Progress: {progress}%</text>
<box width={maxWidth} height={1} backgroundColor="#333">
<box width={width} height={1} backgroundColor="#00FF00" />
</box>
</box>
)
}
```
### Fade In
```tsx
function FadeIn({ children }) {
const [opacity, setOpacity] = useState(0)
const timeline = useTimeline()
useEffect(() => {
timeline.add(
{ opacity: 0 },
{
opacity: 1,
duration: 500,
ease: "easeOutQuad",
onUpdate: (anim) => {
setOpacity(anim.targets[0].opacity)
},
}
)
}, [])
return (
<box style={{ opacity }}>
{children}
</box>
)
}
```
### Looping Animation
```tsx
function Spinner() {
const [frame, setFrame] = useState(0)
const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
useEffect(() => {
const interval = setInterval(() => {
setFrame(f => (f + 1) % frames.length)
}, 80)
return () => clearInterval(interval)
}, [])
return <text>{frames[frame]} Loading...</text>
}
```
### Staggered Animation
```tsx
function StaggeredList({ items }) {
const [visibleCount, setVisibleCount] = useState(0)
useEffect(() => {
let count = 0
const interval = setInterval(() => {
count++
setVisibleCount(count)
if (count >= items.length) {
clearInterval(interval)
}
}, 100)
return () => clearInterval(interval)
}, [items.length])
return (
<box flexDirection="column">
{items.slice(0, visibleCount).map((item, i) => (
<text key={i}>{item}</text>
))}
</box>
)
}
```
### Slide In
```tsx
function SlideIn({ children, from = "left" }) {
const [offset, setOffset] = useState(from === "left" ? -20 : 20)
const timeline = useTimeline()
useEffect(() => {
timeline.add(
{ offset: from === "left" ? -20 : 20 },
{
offset: 0,
duration: 300,
ease: "easeOutCubic",
onUpdate: (anim) => {
setOffset(Math.round(anim.targets[0].offset))
},
}
)
}, [])
return (
<box position="relative" left={offset}>
{children}
</box>
)
}
```
## Performance Tips
### Batch Updates
Timeline automatically batches updates within the render loop.
### Use Integer Values
Round animated values for character-based positioning:
```typescript
onUpdate: (anim) => {
setX(Math.round(anim.targets[0].x))
}
```
### Clean Up Timelines
Hooks automatically clean up, but for core:
```typescript
// When done with timeline
engine.removeTimeline(timeline)
```
## Gotchas
### Terminal Refresh Rate
Terminal UIs typically refresh at 60 FPS max. Very fast animations may appear choppy.
### Character Grid
Animations are constrained to character cells. Sub-pixel positioning isn't possible.
### Cleanup in Effects
Always clean up intervals and timelines:
```tsx
useEffect(() => {
const interval = setInterval(...)
return () => clearInterval(interval)
}, [])
```
## See Also
- [React API](../react/api.md) - `useTimeline` hook reference
- [Solid API](../solid/api.md) - `useTimeline` hook reference
- [Core API](../core/api.md) - `AnimationEngine` and `Timeline` classes
- [Layout Patterns](../layout/patterns.md) - Animated positioning and transitions
@@ -0,0 +1,144 @@
# OpenTUI Components
Reference for all OpenTUI components, organized by category. Components are available in all three frameworks (Core, React, Solid) with slight API differences.
## When to Use
Use this reference when you need to find the right component category or compare naming across Core, React, and Solid.
## Component Categories
| Category | Components | File |
|----------|------------|------|
| Text & Display | text, ascii-font, styled text | [text-display.md](./text-display.md) |
| Containers | box, scrollbox, borders | [containers.md](./containers.md) |
| Inputs | input, textarea, select, tab-select | [inputs.md](./inputs.md) |
| Code & Diff | code, line-number, diff, markdown, text-table | [code-diff.md](./code-diff.md) |
## Component Chooser
```
Need a component?
├─ Styled text or ASCII art -> text-display.md
├─ Containers, borders, scrolling -> containers.md
├─ Forms or input controls -> inputs.md
└─ Code blocks, diffs, line numbers, markdown -> code-diff.md
```
## Component Naming
Components have different names across frameworks:
| Concept | Core (Class) | React (JSX) | Solid (JSX) |
|---------|--------------|-------------|-------------|
| Text | `TextRenderable` | `<text>` | `<text>` |
| Box | `BoxRenderable` | `<box>` | `<box>` |
| ScrollBox | `ScrollBoxRenderable` | `<scrollbox>` | `<scrollbox>` |
| Input | `InputRenderable` | `<input>` | `<input>` |
| Textarea | `TextareaRenderable` | `<textarea>` | `<textarea>` |
| Select | `SelectRenderable` | `<select>` | `<select>` |
| Tab Select | `TabSelectRenderable` | `<tab-select>` | `<tab_select>` |
| ASCII Font | `ASCIIFontRenderable` | `<ascii-font>` | `<ascii_font>` |
| Code | `CodeRenderable` | `<code>` | `<code>` |
| Line Number | `LineNumberRenderable` | `<line-number>` | `<line_number>` |
| Diff | `DiffRenderable` | `<diff>` | `<diff>` |
| Markdown | `MarkdownRenderable` | `<markdown>` | `<markdown>` |
| TextTable | `TextTableRenderable` | N/A (Core only) | N/A (Core only) |
**Note**: Solid uses underscores (`tab_select`) while React uses hyphens (`tab-select`). `TextTableRenderable` is used internally by `MarkdownRenderable` for table rendering and is also available as a standalone Core component.
## Common Properties
All components share these layout properties (see [Layout](../layout/REFERENCE.md)):
```tsx
// Positioning
position="relative" | "absolute"
left, top, right, bottom
// Dimensions
width, height
minWidth, maxWidth, minHeight, maxHeight
// Flexbox
flexDirection, flexGrow, flexShrink, flexBasis
justifyContent, alignItems, alignSelf
flexWrap, gap
// Spacing
padding, paddingTop, paddingRight, paddingBottom, paddingLeft
paddingX, paddingY // Axis shorthand (horizontal/vertical)
margin, marginTop, marginRight, marginBottom, marginLeft
marginX, marginY // Axis shorthand (horizontal/vertical)
// Display
display="flex" | "none"
overflow="visible" | "hidden" | "scroll"
zIndex
```
## Quick Examples
### Core (Imperative)
```typescript
import { createCliRenderer, TextRenderable, BoxRenderable } from "@opentui/core"
const renderer = await createCliRenderer()
const box = new BoxRenderable(renderer, {
id: "container",
border: true,
padding: 2,
})
const text = new TextRenderable(renderer, {
id: "greeting",
content: "Hello!",
fg: "#00FF00",
})
box.add(text)
renderer.root.add(box)
```
### React
```tsx
import { createCliRenderer } from "@opentui/core"
import { createRoot } from "@opentui/react"
function App() {
return (
<box border padding={2}>
<text fg="#00FF00">Hello!</text>
</box>
)
}
const renderer = await createCliRenderer()
createRoot(renderer).render(<App />)
```
### Solid
```tsx
import { render } from "@opentui/solid"
function App() {
return (
<box border padding={2}>
<text fg="#00FF00">Hello!</text>
</box>
)
}
render(() => <App />)
```
## See Also
- [Core API](../core/api.md) - Imperative component classes
- [React API](../react/api.md) - React component props
- [Solid API](../solid/api.md) - Solid component props
- [Layout](../layout/REFERENCE.md) - Layout system details
@@ -0,0 +1,672 @@
# Code & Diff Components
Components for displaying code with syntax highlighting and diffs in OpenTUI.
## Code Component
Display syntax-highlighted code blocks.
### Basic Usage
```tsx
// React
<code
code={`function hello() {
console.log("Hello, World!");
}`}
language="typescript"
/>
// Solid
<code
code={sourceCode}
language="javascript"
/>
// Core
const codeBlock = new CodeRenderable(renderer, {
id: "code",
code: sourceCode,
language: "typescript",
})
```
### Supported Languages
OpenTUI uses Tree-sitter for syntax highlighting. Common languages:
- `typescript`, `javascript`
- `python`
- `rust`
- `go`
- `json`
- `html`, `css`
- `markdown`
- `bash`, `shell`
### Styling
```tsx
<code
code={sourceCode}
language="typescript"
backgroundColor="#1a1a2e"
showLineNumbers
/>
```
### onHighlight Callback
Intercept and modify syntax highlights before rendering:
```tsx
// Core
const codeBlock = new CodeRenderable(renderer, {
id: "code",
code: sourceCode,
language: "typescript",
onHighlight: (highlights, context) => {
// Add custom highlights
highlights.push([10, 20, "custom.error", {}])
return highlights
},
})
// React/Solid
<code
code={sourceCode}
language="typescript"
onHighlight={(highlights, context) => {
// context: { content, filetype, syntaxStyle }
// Modify and return highlights array
return highlights.filter(h => h[2] !== "comment")
}}
/>
```
**Callback signature:**
- `highlights: SimpleHighlight[]` - Array of `[start, end, scope, metadata]`
- `context: { content, filetype, syntaxStyle }` - Highlighting context
- Return modified highlights array or `undefined` to use original
Supports async callbacks for fetching additional highlight data.
### onChunks Callback
Post-process rendered text chunks after syntax highlighting. Runs after `onHighlight` and receives fully resolved chunks:
```tsx
// Core
const codeBlock = new CodeRenderable(renderer, {
id: "code",
code: sourceCode,
language: "typescript",
onChunks: (chunks, context) => {
// Transform chunks (e.g., add link detection)
return chunks
},
})
// React/Solid
<code
code={sourceCode}
language="typescript"
onChunks={(chunks, context) => {
// context: { content, filetype, syntaxStyle, highlights }
return chunks
}}
/>
```
### Link Detection Utility
Auto-detect URLs in code and add clickable hyperlinks:
```typescript
import { detectLinks } from "@opentui/core"
<code
code={sourceCode}
language="typescript"
onChunks={(chunks, context) => detectLinks(chunks, context)}
/>
```
`detectLinks` examines Tree-sitter highlights to find URL tokens and sets `chunk.link` on matching chunks. Supports async usage.
## TextTable Component
Render data tables with borders, word wrapping, and selection support.
### Basic Usage
```typescript
// Core
import { TextTableRenderable, type TextTableContent } from "@opentui/core"
const content: TextTableContent = [
[[ { text: "Name" } ], [ { text: "Age" } ], [ { text: "Role" } ]],
[[ { text: "Alice" } ], [ { text: "30" } ], [ { text: "Engineer" } ]],
[[ { text: "Bob" } ], [ { text: "25" } ], [ { text: "Designer" } ]],
]
const table = new TextTableRenderable(renderer, {
id: "table",
content,
wrapMode: "word", // "none" | "char" | "word"
columnWidthMode: "content", // "content" | "fill"
cellPadding: 0,
border: true,
outerBorder: true,
borderStyle: "single", // single | double | rounded | bold
selectable: true, // Allow text selection
columnFitter: "balanced", // "proportional" | "balanced"
})
```
### Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `content` | `TextTableContent` | - | 2D array of cell content |
| `wrapMode` | `"none" \| "char" \| "word"` | `"none"` | Text wrapping in cells |
| `columnWidthMode` | `"content" \| "fill"` | `"content"` | Column sizing strategy |
| `cellPadding` | `number` | `0` | Padding inside cells |
| `border` | `boolean` | `true` | Show inner borders |
| `outerBorder` | `boolean` | `true` | Show outer borders |
| `borderStyle` | `string` | `"single"` | Border style |
| `borderColor` | `string \| RGBA` | - | Border color |
| `selectable` | `boolean` | `false` | Allow text selection |
| `columnFitter` | `"proportional" \| "balanced"` | `"proportional"` | Column width distribution |
### Cell Content Format
Each cell is an array of styled text chunks:
```typescript
type TextTableCellContent = { text: string; fg?: RGBA; bg?: RGBA }[]
type TextTableContent = TextTableCellContent[][] // rows -> cells -> chunks
```
### Selection
```typescript
table.getSelectedText() // Get selected text
table.hasSelection() // Check if text is selected
```
Columnar selection is supported: dragging vertically within a single column selects only that column's content.
## Line Number Component
Code display with line numbers, highlighting, and diagnostics.
### Basic Usage
```tsx
// React
<line-number
code={sourceCode}
language="typescript"
/>
// Solid (note underscore)
<line_number
code={sourceCode}
language="typescript"
/>
// Core
const codeView = new LineNumberRenderable(renderer, {
id: "code-view",
code: sourceCode,
language: "typescript",
})
```
### Line Number Options
```tsx
// React
<line-number
code={sourceCode}
language="typescript"
startLine={1} // Starting line number
showLineNumbers={true} // Display line numbers
/>
// Solid
<line_number
code={sourceCode}
language="typescript"
startLine={1}
showLineNumbers={true}
/>
```
### Line Highlighting
Highlight specific lines:
```tsx
// React
<line-number
code={sourceCode}
language="typescript"
highlightedLines={[5, 10, 15]} // Highlight these lines
/>
// Solid
<line_number
code={sourceCode}
language="typescript"
highlightedLines={[5, 10, 15]}
/>
```
### Diagnostics
Show errors, warnings, and info on specific lines:
```tsx
// React
<line-number
code={sourceCode}
language="typescript"
diagnostics={[
{ line: 3, severity: "error", message: "Unexpected token" },
{ line: 7, severity: "warning", message: "Unused variable" },
{ line: 12, severity: "info", message: "Consider using const" },
]}
/>
// Solid
<line_number
code={sourceCode}
language="typescript"
diagnostics={[
{ line: 3, severity: "error", message: "Unexpected token" },
]}
/>
```
**Diagnostic severity levels:**
- `error` - Red indicator
- `warning` - Yellow indicator
- `info` - Blue indicator
- `hint` - Gray indicator
### Diff Highlighting
Show added/removed lines:
```tsx
<line-number
code={sourceCode}
language="typescript"
addedLines={[5, 6, 7]} // Green background
removedLines={[10, 11]} // Red background
/>
```
## Diff Component
Unified or split diff viewer with syntax highlighting.
### Basic Usage
```tsx
// React
<diff
oldCode={originalCode}
newCode={modifiedCode}
language="typescript"
/>
// Solid
<diff
oldCode={originalCode}
newCode={modifiedCode}
language="typescript"
/>
// Core
const diffView = new DiffRenderable(renderer, {
id: "diff",
oldCode: originalCode,
newCode: modifiedCode,
language: "typescript",
})
```
### Display Modes
```tsx
// Unified diff (default)
<diff
oldCode={old}
newCode={new}
mode="unified"
/>
// Split/side-by-side diff
<diff
oldCode={old}
newCode={new}
mode="split"
/>
```
### Synchronized Scrolling (Split View)
In split view, enable synchronized scrolling between left and right panes:
```tsx
// React/Solid
<diff
oldCode={old}
newCode={new}
mode="split"
syncScroll // Scrolling one pane syncs the other
/>
// Core
const diffView = new DiffRenderable(renderer, {
id: "diff",
diff: unifiedDiff,
view: "split",
syncScroll: true,
})
// Toggle at runtime
diffView.syncScroll = true
diffView.syncScroll = false
```
### Options
```tsx
<diff
oldCode={originalCode}
newCode={modifiedCode}
language="typescript"
mode="unified"
showLineNumbers
context={3} // Lines of context around changes
/>
```
### Styling
```tsx
<diff
oldCode={old}
newCode={new}
addedLineColor="#2d4f2d" // Background for added lines
removedLineColor="#4f2d2d" // Background for removed lines
unchangedLineColor="transparent"
/>
```
### Line Highlighting API (Core)
Programmatically highlight specific lines in a diff:
```typescript
// Set a single line's color
diffView.setLineColor(5, "#2d4f2d")
diffView.setLineColor(5, { gutter: "#333", content: "#2d4f2d" })
// Clear a single line's color
diffView.clearLineColor(5)
// Set multiple lines at once
diffView.setLineColors(new Map([
[1, "#2d4f2d"],
[2, "#4f2d2d"],
]))
// Highlight a range
diffView.highlightLines(10, 20, "#2d4f2d")
diffView.clearHighlightLines(10, 20)
// Clear all line colors
diffView.clearAllLineColors()
```
The `LineNumberRenderable` also supports programmatic highlighting:
```typescript
lineNumberView.highlightLines(5, 10, "#2d4f2d")
lineNumberView.clearHighlightLines(5, 10)
```
```
## Markdown Component
Render markdown content with syntax highlighting for code blocks.
### Basic Usage
```tsx
// React
<markdown
content={markdownText}
syntaxStyle={mySyntaxStyle}
/>
// Solid
<markdown
content={markdownText}
syntaxStyle={mySyntaxStyle}
/>
// Core
import { MarkdownRenderable } from "@opentui/core"
const md = new MarkdownRenderable(renderer, {
id: "markdown",
content: "# Hello\n\nThis is **markdown**.",
syntaxStyle: mySyntaxStyle,
})
```
### Options
```tsx
<markdown
content={markdownText}
syntaxStyle={syntaxStyle}
treeSitterClient={client} // Optional: custom tree-sitter client
conceal={true} // Hide markdown syntax characters
streaming={true} // Enable streaming mode for incremental updates
tableOptions={{ // Customize markdown table rendering
widthMode: "full", // "content" | "full"
wrapMode: "word", // "none" | "char" | "word"
cellPadding: 0,
borders: true,
outerBorder: true,
borderStyle: "single",
borderColor: "#555",
selectable: true, // Tables are selectable by default
}}
/>
```
### Custom Node Rendering
```tsx
// Core
const md = new MarkdownRenderable(renderer, {
id: "markdown",
content: "# Custom Heading",
syntaxStyle,
renderNode: (node, ctx, defaultRender) => {
if (node.type === "heading") {
// Return custom renderable for headings
return new TextRenderable(ctx, {
content: `>> ${node.content} <<`,
})
}
return null // Use default rendering
},
})
```
### Streaming Mode
For real-time content like LLM output:
```tsx
const [content, setContent] = useState("")
// Append text as it arrives
useEffect(() => {
llmStream.on("token", (token) => {
setContent(c => c + token)
})
}, [])
<markdown
content={content}
syntaxStyle={syntaxStyle}
streaming={true} // Optimizes for incremental updates
/>
```
## Use Cases
### Code Editor
```tsx
function CodeEditor() {
const [code, setCode] = useState(`function hello() {
console.log("Hello!");
}`)
return (
<box flexDirection="column" height="100%">
<box height={1}>
<text>editor.ts</text>
</box>
<textarea
value={code}
onChange={setCode}
language="typescript"
showLineNumbers
flexGrow={1}
focused
/>
</box>
)
}
```
### Code Review
```tsx
function CodeReview({ oldCode, newCode }) {
return (
<box flexDirection="column" height="100%">
<box height={1} backgroundColor="#333">
<text>Changes in src/utils.ts</text>
</box>
<diff
oldCode={oldCode}
newCode={newCode}
language="typescript"
mode="split"
showLineNumbers
/>
</box>
)
}
```
### Syntax-Highlighted Preview
```tsx
function MarkdownPreview({ content }) {
// Extract code blocks from markdown
const codeBlocks = extractCodeBlocks(content)
return (
<scrollbox height={20}>
{codeBlocks.map((block, i) => (
<box key={i} marginBottom={1}>
<code
code={block.code}
language={block.language}
/>
</box>
))}
</scrollbox>
)
}
```
### Error Display
```tsx
function ErrorView({ errors, code }) {
const diagnostics = errors.map(err => ({
line: err.line,
severity: "error",
message: err.message,
}))
return (
<line-number
code={code}
language="typescript"
diagnostics={diagnostics}
highlightedLines={errors.map(e => e.line)}
/>
)
}
```
## Gotchas
### Solid Uses Underscores
```tsx
// React
<line-number />
// Solid
<line_number />
```
### Language Required for Highlighting
```tsx
// No highlighting (plain text)
<code code={text} />
// With highlighting
<code code={text} language="typescript" />
```
### Large Files
For very large files, consider:
- Pagination or virtual scrolling
- Loading only visible portion
- Using `scrollbox` wrapper
```tsx
<scrollbox height={30}>
<line-number
code={largeFile}
language="typescript"
/>
</scrollbox>
```
### Tree-sitter Loading
Syntax highlighting requires Tree-sitter grammars. If highlighting isn't working:
1. Check the language is supported
2. Verify grammars are installed
3. Check `OTUI_TREE_SITTER_WORKER_PATH` if using custom path
@@ -0,0 +1,417 @@
# Container Components
Components for grouping and organizing content in OpenTUI.
## Box Component
The primary container component with borders, backgrounds, and layout capabilities.
### Basic Usage
```tsx
// React/Solid
<box>
<text>Content inside box</text>
</box>
// Core
const box = new BoxRenderable(renderer, {
id: "container",
})
box.add(child)
```
### Borders
```tsx
<box border>
Simple border
</box>
<box
border
borderStyle="single" // single | double | rounded | bold | none
borderColor="#FFFFFF"
>
Styled border
</box>
// Individual borders
<box
borderTop
borderBottom
borderLeft={false}
borderRight={false}
>
Top and bottom only
</box>
```
**Border Styles:**
| Style | Appearance |
|-------|------------|
| `single` | `┌─┐│ │└─┘` |
| `double` | `╔═╗║ ║╚═╝` |
| `rounded` | `╭─╮│ │╰─╯` |
| `bold` | `┏━┓┃ ┃┗━┛` |
### Title
```tsx
<box
border
title="Settings"
titleAlignment="center" // left | center | right
>
Panel content
</box>
```
### Background
```tsx
<box backgroundColor="#1a1a2e">
Dark background
</box>
<box backgroundColor="transparent">
No background
</box>
```
### Layout
Boxes are flex containers by default:
```tsx
<box
flexDirection="row" // row | column | row-reverse | column-reverse
justifyContent="center" // flex-start | flex-end | center | space-between | space-around
alignItems="center" // flex-start | flex-end | center | stretch | baseline
gap={2} // Space between children
>
<text>Item 1</text>
<text>Item 2</text>
</box>
```
### Spacing
```tsx
<box
padding={2} // All sides
paddingTop={1}
paddingRight={2}
paddingBottom={1}
paddingLeft={2}
paddingX={2} // Horizontal (left + right)
paddingY={1} // Vertical (top + bottom)
margin={1}
marginTop={1}
marginX={2} // Horizontal (left + right)
marginY={1} // Vertical (top + bottom)
>
Spaced content
</box>
```
### Dimensions
```tsx
<box
width={40} // Fixed width
height={10} // Fixed height
width="50%" // Percentage of parent
minWidth={20} // Minimum width
maxWidth={80} // Maximum width
flexGrow={1} // Grow to fill space
>
Sized box
</box>
```
### Mouse Events
```tsx
<box
onMouseDown={(event) => {
console.log("Clicked at:", event.x, event.y)
}}
onMouseUp={(event) => {}}
onMouseMove={(event) => {}}
>
Clickable box
</box>
```
### Focusable Boxes
By default, Box elements are not focusable. Set the `focusable` prop to enable focus behavior:
```tsx
// Make a box focusable - it can receive focus via mouse click
<box focusable border>
<text>Click to focus</text>
</box>
// Controlled focus state
const [focused, setFocused] = useState(false)
<box
focusable
focused={focused}
border
borderColor={focused ? "#00ff00" : "#888"}
>
<text>{focused ? "Focused!" : "Not focused"}</text>
</box>
```
When a focusable Box is clicked, focus bubbles up from the click target to the nearest focusable parent. Use `event.preventDefault()` in `onMouseDown` to prevent auto-focus.
## ScrollBox Component
A scrollable container for content that exceeds the viewport.
### Basic Usage
```tsx
// React
<scrollbox height={10}>
{items.map((item, i) => (
<text key={i}>{item}</text>
))}
</scrollbox>
// Solid
<scrollbox height={10}>
<For each={items()}>
{(item) => <text>{item}</text>}
</For>
</scrollbox>
// Core
const scrollbox = new ScrollBoxRenderable(renderer, {
id: "list",
height: 10,
})
items.forEach(item => {
scrollbox.add(new TextRenderable(renderer, { content: item }))
})
```
### Focus for Keyboard Scrolling
```tsx
<scrollbox focused height={20}>
{/* Use arrow keys to scroll */}
</scrollbox>
```
### Scrollbar Styling
```tsx
// React
<scrollbox
style={{
rootOptions: {
backgroundColor: "#24283b",
},
wrapperOptions: {
backgroundColor: "#1f2335",
},
viewportOptions: {
backgroundColor: "#1a1b26",
},
contentOptions: {
backgroundColor: "#16161e",
},
scrollbarOptions: {
showArrows: true,
trackOptions: {
foregroundColor: "#7aa2f7",
backgroundColor: "#414868",
},
},
}}
>
{content}
</scrollbox>
```
### Scroll Position (Core)
```typescript
const scrollbox = new ScrollBoxRenderable(renderer, {
id: "list",
height: 20,
})
// Scroll programmatically
scrollbox.scrollTo(0) // Scroll to top
scrollbox.scrollTo(100) // Scroll to position
scrollbox.scrollBy(10) // Scroll relative
scrollbox.scrollToBottom() // Scroll to end
// Scroll a child into view (nearest alignment)
scrollbox.scrollChildIntoView("child-id") // Searches descendants by ID
```
`scrollChildIntoView(childId)` scrolls the minimum amount needed to make the identified descendant visible. It mirrors `Element.scrollIntoView({ block: "nearest" })` from the CSSOM View spec. Works with nested descendants and handles both horizontal and vertical scrolling.
## Composition Patterns
### Card Component
```tsx
function Card({ title, children }) {
return (
<box
border
borderStyle="rounded"
padding={2}
marginBottom={1}
>
{title && (
<text fg="#00FFFF" bold>
{title}
</text>
)}
<box marginTop={title ? 1 : 0}>
{children}
</box>
</box>
)
}
```
### Panel Component
```tsx
function Panel({ title, children, width = 40 }) {
return (
<box
border
borderStyle="double"
width={width}
backgroundColor="#1a1a2e"
>
{title && (
<box
borderBottom
padding={1}
backgroundColor="#2a2a4e"
>
<text bold>{title}</text>
</box>
)}
<box padding={2}>
{children}
</box>
</box>
)
}
```
### List Container
```tsx
function List({ items, renderItem }) {
return (
<scrollbox height={15} focused>
{items.map((item, i) => (
<box
key={i}
padding={1}
backgroundColor={i % 2 === 0 ? "#222" : "#333"}
>
{renderItem(item, i)}
</box>
))}
</scrollbox>
)
}
```
## Nesting Containers
```tsx
<box flexDirection="column" height="100%">
{/* Header */}
<box height={3} border>
<text>Header</text>
</box>
{/* Main area with sidebar */}
<box flexDirection="row" flexGrow={1}>
<box width={20} border>
<text>Sidebar</text>
</box>
<box flexGrow={1}>
<scrollbox height="100%">
{/* Scrollable content */}
</scrollbox>
</box>
</box>
{/* Footer */}
<box height={1}>
<text>Footer</text>
</box>
</box>
```
## Gotchas
### Percentage Dimensions Need Parent Size
```tsx
// WRONG - parent has no explicit size
<box>
<box width="50%">Won't work</box>
</box>
// CORRECT
<box width="100%">
<box width="50%">Works</box>
</box>
```
### FlexGrow Needs Sized Parent
```tsx
// WRONG
<box>
<box flexGrow={1}>Won't grow</box>
</box>
// CORRECT
<box height="100%">
<box flexGrow={1}>Will grow</box>
</box>
```
### ScrollBox Needs Height
```tsx
// WRONG - no height constraint
<scrollbox>
{items}
</scrollbox>
// CORRECT
<scrollbox height={20}>
{items}
</scrollbox>
```
### Borders Add to Size
Borders take up space inside the box:
```tsx
<box width={10} border>
{/* Inner content area is 8 chars (10 - 2 for borders) */}
</box>
```
@@ -0,0 +1,531 @@
# Input Components
Components for user input in OpenTUI.
## Input Component
Single-line text input field.
### Basic Usage
```tsx
// React
<input
value={value}
onChange={(newValue) => setValue(newValue)}
placeholder="Enter text..."
focused
/>
// Solid
<input
value={value()}
onInput={(newValue) => setValue(newValue)}
placeholder="Enter text..."
focused
/>
// Core
const input = new InputRenderable(renderer, {
id: "name",
placeholder: "Enter text...",
})
input.on(InputRenderableEvents.CHANGE, (value) => {
console.log("Value:", value)
})
input.focus()
```
### Styling
```tsx
<input
width={30}
backgroundColor="#1a1a1a"
textColor="#FFFFFF"
cursorColor="#00FF00"
focusedBackgroundColor="#2a2a2a"
placeholderColor="#666666"
/>
```
### Events
```tsx
// React
<input
onChange={(value) => console.log("Changed:", value)}
onFocus={() => console.log("Focused")}
onBlur={() => console.log("Blurred")}
/>
// Core
input.on(InputRenderableEvents.CHANGE, (value) => {})
input.on(InputRenderableEvents.FOCUS, () => {})
input.on(InputRenderableEvents.BLUR, () => {})
```
### Controlled Input
```tsx
// React
function ControlledInput() {
const [value, setValue] = useState("")
return (
<input
value={value}
onChange={setValue}
focused
/>
)
}
// Solid
function ControlledInput() {
const [value, setValue] = createSignal("")
return (
<input
value={value()}
onInput={setValue}
focused
/>
)
}
```
## Textarea Component
Multi-line text input field.
### Basic Usage
```tsx
// React
<textarea
value={text}
onChange={(newText) => setText(newText)}
placeholder="Enter multiple lines..."
width={40}
height={10}
focused
/>
// Solid
<textarea
value={text()}
onInput={(newText) => setText(newText)}
placeholder="Enter multiple lines..."
width={40}
height={10}
focused
/>
// Core
const textarea = new TextareaRenderable(renderer, {
id: "editor",
width: 40,
height: 10,
placeholder: "Enter text...",
})
```
### Features
```tsx
<textarea
showLineNumbers // Display line numbers
wrapText // Wrap long lines
readOnly // Disable editing
tabSize={2} // Tab character width
/>
```
### Syntax Highlighting
```tsx
<textarea
language="typescript"
value={code}
onChange={setCode}
/>
```
## Select Component
List selection for choosing from options.
### Basic Usage
```tsx
// React
<select
options={[
{ name: "Option 1", description: "First option", value: "1" },
{ name: "Option 2", description: "Second option", value: "2" },
{ name: "Option 3", description: "Third option", value: "3" },
]}
onSelect={(index, option) => {
console.log("Selected:", option.name) // Called when Enter is pressed
}}
focused
/>
// Solid
<select
options={[
{ name: "Option 1", description: "First option", value: "1" },
{ name: "Option 2", description: "Second option", value: "2" },
]}
onSelect={(index, option) => {
console.log("Selected:", option.name) // Called when Enter is pressed
}}
focused
/>
// Core
const select = new SelectRenderable(renderer, {
id: "menu",
options: [
{ name: "Option 1", description: "First option", value: "1" },
{ name: "Option 2", description: "Second option", value: "2" },
],
})
select.on(SelectRenderableEvents.ITEM_SELECTED, (index, option) => {
console.log("Selected:", option.name) // Called when Enter is pressed
})
select.focus()
```
### Option Format
```typescript
interface SelectOption {
name: string // Display text
description?: string // Optional description shown below
value?: any // Associated value
}
```
### Styling
```tsx
<select
height={8} // Visible height
selectedIndex={0} // Initially selected
showScrollIndicator // Show scroll arrows
selectedBackgroundColor="#333"
selectedTextColor="#fff"
highlightBackgroundColor="#444"
/>
```
### Navigation
Default keybindings:
- `Up` / `k` - Move up
- `Down` / `j` - Move down
- `Enter` - Select item
### Events
**Important**: `onSelect` and `onChange` serve different purposes:
| Event | Trigger | Use Case |
|-------|---------|----------|
| `onSelect` | **Enter key pressed** - user confirms selection | Perform action with selected item |
| `onChange` | **Arrow keys** - user navigates list | Preview, update UI as user browses |
```tsx
// React/Solid
<select
onSelect={(index, option) => {
// Called when Enter is pressed - selection confirmed
console.log("User selected:", option.name)
performAction(option)
}}
onChange={(index, option) => {
// Called when navigating with arrow keys
console.log("Browsing:", option.name)
showPreview(option)
}}
/>
// Core
select.on(SelectRenderableEvents.ITEM_SELECTED, (index, option) => {
// Called when Enter is pressed
})
select.on(SelectRenderableEvents.SELECTION_CHANGED, (index, option) => {
// Called when navigating with arrow keys
})
```
## Tab Select Component
Horizontal tab-based selection.
### Basic Usage
```tsx
// React
<tab-select
options={[
{ name: "Home", description: "Dashboard view" },
{ name: "Settings", description: "Configuration" },
{ name: "Help", description: "Documentation" },
]}
onSelect={(index, option) => {
console.log("Tab selected:", option.name) // Called when Enter is pressed
}}
focused
/>
// Solid (note underscore)
<tab_select
options={[
{ name: "Home", description: "Dashboard view" },
{ name: "Settings", description: "Configuration" },
]}
onSelect={(index, option) => {
console.log("Tab selected:", option.name) // Called when Enter is pressed
}}
focused
/>
// Core
const tabs = new TabSelectRenderable(renderer, {
id: "tabs",
options: [...],
tabWidth: 20,
})
tabs.on(TabSelectRenderableEvents.ITEM_SELECTED, (index, option) => {
console.log("Tab selected:", option.name) // Called when Enter is pressed
})
tabs.focus()
```
### Events
Same pattern as Select - `onSelect` for Enter key, `onChange` for navigation:
```tsx
<tab-select
onSelect={(index, option) => {
// Called when Enter is pressed - switch to tab
setActiveTab(index)
}}
onChange={(index, option) => {
// Called when navigating with arrow keys
showTabPreview(option)
}}
/>
```
### Styling
```tsx
// React
<tab-select
tabWidth={20} // Width of each tab
selectedIndex={0} // Initially selected tab
/>
// Solid
<tab_select
tabWidth={20}
selectedIndex={0}
/>
```
### Navigation
Default keybindings:
- `Left` / `[` - Previous tab
- `Right` / `]` - Next tab
- `Enter` - Select tab
## Focus Management
### Single Focused Input
```tsx
function SingleInput() {
return <input placeholder="I'm focused" focused />
}
```
### Multiple Inputs with Focus State
```tsx
// React
function Form() {
const [focusIndex, setFocusIndex] = useState(0)
const fields = ["name", "email", "message"]
useKeyboard((key) => {
if (key.name === "tab") {
setFocusIndex(i => (i + 1) % fields.length)
}
})
return (
<box flexDirection="column" gap={1}>
{fields.map((field, i) => (
<input
key={field}
placeholder={`Enter ${field}`}
focused={i === focusIndex}
/>
))}
</box>
)
}
```
### Focus Methods (Core)
```typescript
input.focus() // Give focus
input.blur() // Remove focus
input.isFocused() // Check focus state
```
## Form Patterns
### Login Form
```tsx
function LoginForm() {
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const [focusField, setFocusField] = useState<"username" | "password">("username")
useKeyboard((key) => {
if (key.name === "tab") {
setFocusField(f => f === "username" ? "password" : "username")
}
if (key.name === "enter") {
handleLogin()
}
})
return (
<box flexDirection="column" gap={1} border padding={2}>
<box flexDirection="row" gap={1}>
<text>Username:</text>
<input
value={username}
onChange={setUsername}
focused={focusField === "username"}
width={20}
/>
</box>
<box flexDirection="row" gap={1}>
<text>Password:</text>
<input
value={password}
onChange={setPassword}
focused={focusField === "password"}
width={20}
/>
</box>
</box>
)
}
```
### Search with Results
```tsx
function SearchableList({ items, onItemSelected }) {
const [query, setQuery] = useState("")
const [focusSearch, setFocusSearch] = useState(true)
const [preview, setPreview] = useState(null)
const filtered = items.filter(item =>
item.toLowerCase().includes(query.toLowerCase())
)
useKeyboard((key) => {
if (key.name === "tab") {
setFocusSearch(f => !f)
}
})
return (
<box flexDirection="column">
<input
value={query}
onChange={setQuery}
placeholder="Search..."
focused={focusSearch}
/>
<select
options={filtered.map(item => ({ name: item }))}
focused={!focusSearch}
height={10}
onSelect={(index, option) => {
// Enter pressed - confirm selection
onItemSelected(option)
}}
onChange={(index, option) => {
// Navigating - show preview
setPreview(option)
}}
/>
</box>
)
}
```
## Gotchas
### Focus Required
Inputs must be focused to receive keyboard input:
```tsx
// WRONG - won't receive input
<input placeholder="Type here" />
// CORRECT
<input placeholder="Type here" focused />
```
### Select Options Format
Options must be objects with `name` property:
```tsx
// WRONG
<select options={["a", "b", "c"]} />
// CORRECT
<select options={[
{ name: "A", description: "Option A" },
{ name: "B", description: "Option B" },
]} />
```
### Solid Uses Underscores
```tsx
// React
<tab-select />
// Solid
<tab_select />
```
### Value vs onInput (Solid)
Solid uses `onInput` instead of `onChange`:
```tsx
// React
<input value={value} onChange={setValue} />
// Solid
<input value={value()} onInput={setValue} />
```
@@ -0,0 +1,386 @@
# Text & Display Components
Components for displaying text content in OpenTUI.
## Text Component
The primary component for displaying styled text.
### Basic Usage
```tsx
// React/Solid
<text>Hello, World!</text>
// With content prop
<text content="Hello, World!" />
// Core
const text = new TextRenderable(renderer, {
id: "greeting",
content: "Hello, World!",
})
```
### Styling (React/Solid)
For React and Solid, use **nested modifier tags** for text styling:
```tsx
<text fg="#FFFFFF" bg="#000000">
<strong>Bold</strong>, <em>italic</em>, and <u>underlined</u>
</text>
```
> **Important**: Do NOT use `bold`, `italic`, `underline`, `dim`, `strikethrough` as props on `<text>` — they don't work. Always use nested tags like `<strong>`, `<em>`, `<u>`, or `<span>` with styling.
### Styling (Core) - Text Attributes
```typescript
import { TextRenderable, TextAttributes } from "@opentui/core"
const text = new TextRenderable(renderer, {
content: "Styled",
attributes: TextAttributes.BOLD | TextAttributes.UNDERLINE,
})
```
**Available attributes:**
- `TextAttributes.BOLD`
- `TextAttributes.DIM`
- `TextAttributes.ITALIC`
- `TextAttributes.UNDERLINE`
- `TextAttributes.BLINK`
- `TextAttributes.INVERSE`
- `TextAttributes.HIDDEN`
- `TextAttributes.STRIKETHROUGH`
### Text Selection
```tsx
<text selectable>
This text can be selected by the user
</text>
<text selectable={false}>
This text cannot be selected
</text>
```
For copy-on-selection and the full selection API, see `keyboard/REFERENCE.md` (selection).
## Text Modifiers
Inline styling elements that must be used inside `<text>`:
### Span
Inline styled text:
```tsx
<text>
Normal text with <span fg="red">red text</span> inline
</text>
```
### Bold/Strong
```tsx
<text>
<strong>Bold text</strong>
<b>Also bold</b>
</text>
```
### Italic/Emphasis
```tsx
<text>
<em>Italic text</em>
<i>Also italic</i>
</text>
```
### Underline
```tsx
<text>
<u>Underlined text</u>
</text>
```
### Line Break
```tsx
<text>
Line one
<br />
Line two
</text>
```
### Link
```tsx
<text>
Visit <a href="https://example.com">our website</a>
</text>
```
### Combined Modifiers
```tsx
<text>
<span fg="#00FF00">
<strong>Bold green</strong>
</span>
and
<span fg="#FF0000">
<em><u>italic underlined red</u></em>
</span>
</text>
```
## Styled Text Template (Core)
The `t` template literal for complex styling:
```typescript
import { t, bold, italic, underline, fg, bg, dim } from "@opentui/core"
const styled = t`
${bold("Bold")} and ${italic("italic")} text.
${fg("#FF0000")("Red text")} with ${bg("#0000FF")("blue background")}.
${dim("Dimmed")} and ${underline("underlined")}.
`
const text = new TextRenderable(renderer, {
content: styled,
})
```
### Style Functions
| Function | Description |
|----------|-------------|
| `bold(text)` | Bold text |
| `italic(text)` | Italic text |
| `underline(text)` | Underlined text |
| `dim(text)` | Dimmed text |
| `strikethrough(text)` | Strikethrough text |
| `fg(color)(text)` | Set foreground color |
| `bg(color)(text)` | Set background color |
## ASCII Font Component
Display large ASCII art text banners.
### Basic Usage
```tsx
// React
<ascii-font text="TITLE" font="tiny" />
// Solid
<ascii_font text="TITLE" font="tiny" />
// Core
const title = new ASCIIFontRenderable(renderer, {
id: "title",
text: "TITLE",
font: "tiny",
})
```
### Available Fonts
| Font | Description |
|------|-------------|
| `tiny` | Compact ASCII font |
| `block` | Block-style letters |
| `slick` | Sleek modern style |
| `shade` | Shaded 3D effect |
### Styling
```tsx
// React
<ascii-font
text="HELLO"
font="block"
color="#00FF00"
/>
// Core
import { RGBA } from "@opentui/core"
const title = new ASCIIFontRenderable(renderer, {
text: "HELLO",
font: "block",
color: RGBA.fromHex("#00FF00"),
})
```
### Example Output
```
Font: tiny
╭─╮╭─╮╭─╮╭╮╭╮╭─╮╶╮╶ ╶╮
│ ││─┘├┤ │╰╯││ │ │
╰─╯╵ ╰─╯╵ ╵╰─╯╶╯╶╰─╯
Font: block
█▀▀█ █▀▀█ █▀▀ █▀▀▄
█ █ █▀▀▀ █▀▀ █ █
▀▀▀▀ ▀ ▀▀▀ ▀ ▀
```
## Colors
### Color Formats
```tsx
// Hex colors
<text fg="#FF0000">Red</text>
<text fg="#F00">Short hex</text>
// Named colors
<text fg="red">Red</text>
<text fg="blue">Blue</text>
// Transparent
<text bg="transparent">No background</text>
```
### RGBA Class
The `RGBA` class from `@opentui/core` can be used in **all frameworks** (Core, React, Solid) for programmatic color manipulation:
```typescript
import { RGBA } from "@opentui/core"
// From hex string (most common)
const red = RGBA.fromHex("#FF0000")
const shortHex = RGBA.fromHex("#F00") // Short form supported
// From integers (0-255 range for each channel)
const green = RGBA.fromInts(0, 255, 0, 255) // r, g, b, a
const semiGreen = RGBA.fromInts(0, 255, 0, 128) // 50% transparent
// From normalized floats (0.0-1.0 range)
const blue = RGBA.fromValues(0.0, 0.0, 1.0, 1.0) // r, g, b, a
const overlay = RGBA.fromValues(0.1, 0.1, 0.1, 0.7) // Dark semi-transparent
// Common use cases
const backgroundColor = RGBA.fromHex("#1a1a2e")
const textColor = RGBA.fromHex("#FFFFFF")
const borderColor = RGBA.fromInts(122, 162, 247, 255) // Tokyo Night blue
const shadowColor = RGBA.fromValues(0.0, 0.0, 0.0, 0.5) // 50% black
```
**When to use each method:**
- `fromHex()` - When working with design specs or CSS colors
- `fromInts()` - When you have 8-bit color values (0-255)
- `fromValues()` - When doing color math or interpolation (normalized 0.0-1.0)
### Using RGBA in React/Solid
```tsx
// React or Solid - RGBA works with color props
import { RGBA } from "@opentui/core"
const primaryColor = RGBA.fromHex("#7aa2f7")
function MyComponent() {
return (
<box backgroundColor={primaryColor} borderColor={primaryColor}>
<text fg={RGBA.fromHex("#c0caf5")}>Styled with RGBA</text>
</box>
)
}
```
Most props that accept color strings (`"#FF0000"`, `"red"`) also accept `RGBA` objects directly.
## Text Wrapping
Text wraps based on parent container:
```tsx
<box width={40}>
<text>
This long text will wrap when it reaches the edge of the
40-character wide parent container.
</text>
</box>
```
## Dynamic Content
### React
```tsx
function Counter() {
const [count, setCount] = useState(0)
return <text>Count: {count}</text>
}
```
### Solid
```tsx
function Counter() {
const [count, setCount] = createSignal(0)
return <text>Count: {count()}</text>
}
```
### Core
```typescript
const text = new TextRenderable(renderer, {
id: "counter",
content: "Count: 0",
})
// Update later
text.setContent("Count: 1")
```
## Gotchas
### Text Modifiers Outside Text
```tsx
// WRONG - modifiers only work inside <text>
<box>
<strong>Won't work</strong>
</box>
// CORRECT
<box>
<text>
<strong>This works</strong>
</text>
</box>
```
### Empty Text
```tsx
// May cause layout issues
<text></text>
// Better - use space or conditional
<text>{content || " "}</text>
```
### Color Format
```tsx
// WRONG
<text fg="FF0000">Missing #</text>
// CORRECT
<text fg="#FF0000">With #</text>
```
@@ -0,0 +1,145 @@
# OpenTUI Core (@opentui/core)
The foundational library for building terminal user interfaces. Provides an imperative API with all primitives, giving you maximum control over rendering, state, and behavior.
## Overview
OpenTUI Core runs on Bun with native Zig bindings for performance-critical operations:
- **Renderer**: Manages terminal output, input events, and the rendering loop
- **Renderables**: Hierarchical UI building blocks with Yoga layout
- **Constructs**: Declarative wrappers for composing Renderables
- **FrameBuffer**: Low-level 2D rendering surface for custom graphics
## When to Use Core
Use the core imperative API when:
- Building a library or framework on top of OpenTUI
- Need maximum control over rendering and state
- Want smallest possible bundle size (no React/Solid runtime)
- Building performance-critical applications
- Integrating with existing imperative codebases
## When NOT to Use Core
| Scenario | Use Instead |
|----------|-------------|
| Familiar with React patterns | `@opentui/react` |
| Want fine-grained reactivity | `@opentui/solid` |
| Building typical applications | React or Solid reconciler |
| Rapid prototyping | React or Solid reconciler |
## Quick Start
### Using create-tui (Recommended)
```bash
bunx create-tui@latest -t core my-app
cd my-app
bun run src/index.ts
```
The CLI creates the `my-app` directory for you - it must **not already exist**.
**Agent guidance**: Always use autonomous mode with `-t <template>` flag. Never use interactive mode (`bunx create-tui@latest my-app` without `-t`) as it requires user prompts that agents cannot respond to.
### Manual Setup
```bash
mkdir my-tui && cd my-tui
bun init
bun install @opentui/core
```
```typescript
import { createCliRenderer, TextRenderable, BoxRenderable } from "@opentui/core"
const renderer = await createCliRenderer()
// Create a box container
const container = new BoxRenderable(renderer, {
id: "container",
width: 40,
height: 10,
border: true,
borderStyle: "rounded",
padding: 1,
})
// Create text inside the box
const greeting = new TextRenderable(renderer, {
id: "greeting",
content: "Hello, OpenTUI!",
fg: "#00FF00",
})
// Compose the tree
container.add(greeting)
renderer.root.add(container)
```
## Core Concepts
### Renderer
The `CliRenderer` orchestrates everything:
- Manages the terminal viewport and alternate screen
- Handles input events (keyboard, mouse, paste)
- Runs the rendering loop (configurable FPS)
- Provides the root node for the renderable tree
### Renderables vs Constructs
| Renderables (Imperative) | Constructs (Declarative) |
|--------------------------|--------------------------|
| `new TextRenderable(renderer, {...})` | `Text({...})` |
| Requires renderer at creation | Creates VNode, instantiated later |
| Direct mutation via methods | Chained calls recorded, replayed on instantiation |
| Full control | Cleaner composition |
### Storage Options
Renderables can be composed in two ways:
1. **Imperative**: Create instances, call `.add()` to compose
2. **Declarative (Constructs)**: Create VNodes, pass children as arguments
## Essential Commands
```bash
bun install @opentui/core # Install
bun run src/index.ts # Run directly (no build needed)
bun test # Run tests
```
## Runtime Requirements
OpenTUI runs on Bun and uses Zig for native builds.
```bash
# Package management
bun install @opentui/core
# Running
bun run src/index.ts
bun test
# Building (only needed for native code changes)
bun run build
```
**Zig** is required for building native components.
## In This Reference
- [Configuration](./configuration.md) - Renderer options, environment variables
- [API](./api.md) - Renderer, Renderables, types, utilities
- [Patterns](./patterns.md) - Composition, events, state management
- [Gotchas](./gotchas.md) - Common issues, debugging, limitations
## See Also
- [React](../react/REFERENCE.md) - React reconciler for declarative TUI
- [Solid](../solid/REFERENCE.md) - Solid reconciler for declarative TUI
- [Layout](../layout/REFERENCE.md) - Yoga/Flexbox layout system
- [Components](../components/REFERENCE.md) - Component reference by category
- [Keyboard](../keyboard/REFERENCE.md) - Input handling and shortcuts
- [Testing](../testing/REFERENCE.md) - Test renderer and snapshots
@@ -0,0 +1,543 @@
# Core API Reference
## Renderer
### createCliRenderer(config?)
Creates and initializes the CLI renderer.
```typescript
import { createCliRenderer, type CliRendererConfig } from "@opentui/core"
const renderer = await createCliRenderer({
targetFPS: 60, // Target frames per second
exitOnCtrlC: true, // Exit process on Ctrl+C
consoleOptions: { // Debug console overlay
position: ConsolePosition.BOTTOM,
sizePercent: 30,
startInDebugMode: false,
},
onDestroy: () => {}, // Cleanup callback
})
```
### CliRenderer Instance
```typescript
renderer.root // Root renderable node
renderer.width // Terminal width in columns
renderer.height // Terminal height in rows
renderer.keyInput // Keyboard event emitter
renderer.console // Console overlay controller
renderer.start() // Start render loop
renderer.stop() // Stop render loop
renderer.destroy() // Cleanup and exit alternate screen
renderer.requestRender() // Request a re-render
renderer.setCursorStyle(options) // Set cursor style
renderer.setCursorColor(color) // Set cursor color
renderer.setMousePointer(style) // Set mouse pointer shape
```
### Cursor & Mouse Pointer
```typescript
import { type CursorStyleOptions, type MousePointerStyle } from "@opentui/core"
// Set cursor style (options object)
renderer.setCursorStyle({
style: "block", // "block" | "line" | "underline" | "default"
blinking: true, // Cursor blink
color: RGBA.fromHex("#FF0000"), // Cursor color
cursor: "pointer", // Mouse pointer shape
})
// Set mouse pointer shape (OSC 22)
renderer.setMousePointer("pointer")
// Available: "default" | "pointer" | "text" | "crosshair" | "move" | "not-allowed"
```
### Renderer Events
```typescript
renderer.on("resize", (width, height) => {}) // Terminal resized
renderer.on("focus", () => {}) // Terminal window gained focus
renderer.on("blur", () => {}) // Terminal window lost focus
renderer.on("theme_mode", (mode) => {}) // "dark" | "light"
renderer.on("capabilities", (caps) => {}) // Terminal capabilities detected
renderer.on("selection", (selection) => {}) // Text selection finished (mouse-up)
renderer.on("destroy", () => {}) // Renderer destroyed
renderer.on("memory:snapshot", (snapshot) => {}) // Memory snapshot
renderer.on("debugOverlay:toggle", () => {}) // Debug overlay toggled
```
### Console Overlay
```typescript
renderer.console.show() // Show console overlay
renderer.console.hide() // Hide console overlay
renderer.console.toggle() // Toggle visibility/focus
renderer.console.clear() // Clear console contents
```
## Renderables
All renderables extend the base `Renderable` class and share common properties.
### Common Properties
```typescript
interface CommonProps {
id?: string // Unique identifier
// Positioning
position?: "relative" | "absolute"
left?: number | string
top?: number | string
right?: number | string
bottom?: number | string
// Dimensions
width?: number | string | "auto"
height?: number | string | "auto"
minWidth?: number
minHeight?: number
maxWidth?: number
maxHeight?: number
// Flexbox
flexDirection?: "row" | "column" | "row-reverse" | "column-reverse"
flexGrow?: number
flexShrink?: number
flexBasis?: number | string
flexWrap?: "nowrap" | "wrap" | "wrap-reverse"
justifyContent?: "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly"
alignItems?: "flex-start" | "flex-end" | "center" | "stretch" | "baseline"
alignSelf?: "auto" | "flex-start" | "flex-end" | "center" | "stretch" | "baseline"
alignContent?: "flex-start" | "flex-end" | "center" | "stretch" | "space-between" | "space-around"
// Spacing
padding?: number
paddingTop?: number
paddingRight?: number
paddingBottom?: number
paddingLeft?: number
margin?: number
marginTop?: number
marginRight?: number
marginBottom?: number
marginLeft?: number
gap?: number
// Display
display?: "flex" | "none"
overflow?: "visible" | "hidden" | "scroll"
zIndex?: number
}
```
### Renderable Methods
```typescript
renderable.add(child) // Add child renderable
renderable.remove(child) // Remove child renderable
renderable.getRenderable(id) // Find child by ID
renderable.focus() // Focus this renderable
renderable.blur() // Remove focus
renderable.destroy() // Destroy and cleanup
renderable.on(event, handler) // Add event listener
renderable.off(event, handler) // Remove event listener
renderable.emit(event, ...args) // Emit event
```
### TextRenderable
Display styled text content.
```typescript
import { TextRenderable, TextAttributes, t, bold, fg, underline } from "@opentui/core"
const text = new TextRenderable(renderer, {
id: "text",
content: "Hello World",
fg: "#FFFFFF", // Foreground color
bg: "#000000", // Background color
attributes: TextAttributes.BOLD | TextAttributes.UNDERLINE,
selectable: true, // Allow text selection
})
// Styled text with template literals
const styled = new TextRenderable(renderer, {
content: t`${bold("Bold")} and ${fg("#FF0000")(underline("red underlined"))}`,
})
```
**TextAttributes flags:**
- `TextAttributes.BOLD`
- `TextAttributes.DIM`
- `TextAttributes.ITALIC`
- `TextAttributes.UNDERLINE`
- `TextAttributes.BLINK`
- `TextAttributes.INVERSE`
- `TextAttributes.HIDDEN`
- `TextAttributes.STRIKETHROUGH`
### BoxRenderable
Container with borders and layout.
```typescript
import { BoxRenderable } from "@opentui/core"
const box = new BoxRenderable(renderer, {
id: "box",
width: 40,
height: 10,
backgroundColor: "#1a1a2e",
border: true,
borderStyle: "single" | "double" | "rounded" | "bold" | "none",
borderColor: "#FFFFFF",
title: "Panel Title",
titleAlignment: "left" | "center" | "right",
onMouseDown: (event) => {},
onMouseUp: (event) => {},
onMouseMove: (event) => {},
})
```
### InputRenderable
Single-line text input.
```typescript
import { InputRenderable, InputRenderableEvents } from "@opentui/core"
const input = new InputRenderable(renderer, {
id: "input",
width: 30,
placeholder: "Enter text...",
value: "", // Initial value
backgroundColor: "#1a1a1a",
textColor: "#FFFFFF",
cursorColor: "#00FF00",
focusedBackgroundColor: "#2a2a2a",
})
input.on(InputRenderableEvents.CHANGE, (value: string) => {
console.log("Value:", value)
})
input.focus() // Must be focused to receive input
```
### SelectRenderable
List selection component.
```typescript
import { SelectRenderable, SelectRenderableEvents } from "@opentui/core"
const select = new SelectRenderable(renderer, {
id: "select",
width: 30,
height: 10,
options: [
{ name: "Option 1", description: "First option", value: "1" },
{ name: "Option 2", description: "Second option", value: "2" },
],
selectedIndex: 0,
})
// Called when Enter is pressed - selection confirmed
select.on(SelectRenderableEvents.ITEM_SELECTED, (index, option) => {
console.log("Selected:", option.name)
performAction(option)
})
// Called when navigating with arrow keys
select.on(SelectRenderableEvents.SELECTION_CHANGED, (index, option) => {
console.log("Browsing:", option.name)
showPreview(option)
})
select.focus() // Navigate with up/down/j/k, select with enter
```
**Event distinction:**
- `ITEM_SELECTED` - Enter key pressed, user confirms selection
- `SELECTION_CHANGED` - Arrow keys, user navigating/browsing options
### TabSelectRenderable
Horizontal tab selection.
```typescript
import { TabSelectRenderable, TabSelectRenderableEvents } from "@opentui/core"
const tabs = new TabSelectRenderable(renderer, {
id: "tabs",
width: 60,
options: [
{ name: "Home", description: "Dashboard" },
{ name: "Settings", description: "Configuration" },
],
tabWidth: 20,
})
// Called when Enter is pressed - tab selected
tabs.on(TabSelectRenderableEvents.ITEM_SELECTED, (index, option) => {
console.log("Tab selected:", option.name)
switchToTab(index)
})
// Called when navigating with arrow keys
tabs.on(TabSelectRenderableEvents.SELECTION_CHANGED, (index, option) => {
console.log("Browsing tab:", option.name)
})
tabs.focus() // Navigate with left/right/[/], select with enter
```
**Event distinction** (same as SelectRenderable):
- `ITEM_SELECTED` - Enter key pressed, user confirms tab
- `SELECTION_CHANGED` - Arrow keys, user navigating tabs
### ScrollBoxRenderable
Scrollable container.
```typescript
import { ScrollBoxRenderable } from "@opentui/core"
const scrollbox = new ScrollBoxRenderable(renderer, {
id: "scrollbox",
width: 40,
height: 20,
showScrollbar: true,
scrollbarOptions: {
showArrows: true,
trackOptions: {
foregroundColor: "#7aa2f7",
backgroundColor: "#414868",
},
},
})
// Add content that exceeds viewport
for (let i = 0; i < 100; i++) {
scrollbox.add(new TextRenderable(renderer, {
id: `line-${i}`,
content: `Line ${i}`,
}))
}
scrollbox.focus() // Scroll with arrow keys
```
### ASCIIFontRenderable
ASCII art text.
```typescript
import { ASCIIFontRenderable, RGBA } from "@opentui/core"
const title = new ASCIIFontRenderable(renderer, {
id: "title",
text: "OPENTUI",
font: "tiny" | "block" | "slick" | "shade",
color: RGBA.fromHex("#FFFFFF"),
})
```
### FrameBufferRenderable
Low-level 2D rendering surface.
```typescript
import { FrameBufferRenderable, RGBA } from "@opentui/core"
const canvas = new FrameBufferRenderable(renderer, {
id: "canvas",
width: 50,
height: 20,
})
// Direct pixel manipulation
canvas.frameBuffer.fillRect(10, 5, 20, 8, RGBA.fromHex("#FF0000"))
canvas.frameBuffer.drawText("Custom", 12, 7, RGBA.fromHex("#FFFFFF"))
canvas.frameBuffer.setCell(x, y, char, fg, bg)
```
## Constructs (VNode API)
Declarative wrappers that create VNodes instead of direct instances.
```typescript
import { Text, Box, Input, Select, instantiate, delegate } from "@opentui/core"
// Create VNode tree
const ui = Box(
{ border: true, padding: 1 },
Text({ content: "Hello" }),
Input({ placeholder: "Type here..." }),
)
// Instantiate onto renderer
renderer.root.add(ui)
// Delegate focus to nested element
const form = delegate(
{ focus: "email-input" },
Box(
{},
Text({ content: "Email:" }),
Input({ id: "email-input", placeholder: "you@example.com" }),
),
)
form.focus() // Focuses the input, not the box
```
## Colors (RGBA)
The `RGBA` class is exported from `@opentui/core` but works across **all frameworks** (Core, React, Solid). Use it for programmatic color manipulation.
### Creating Colors
```typescript
import { RGBA, parseColor } from "@opentui/core"
// From hex string (most common)
RGBA.fromHex("#FF0000") // Full hex
RGBA.fromHex("#F00") // Short hex
// From integers (0-255 range)
RGBA.fromInts(255, 0, 0, 255) // r, g, b, a - fully opaque red
RGBA.fromInts(255, 0, 0, 128) // 50% transparent red
RGBA.fromInts(0, 0, 0, 0) // Fully transparent
// From normalized floats (0.0-1.0 range)
RGBA.fromValues(1.0, 0.0, 0.0, 1.0) // Fully opaque red
RGBA.fromValues(0.1, 0.1, 0.1, 0.7) // Dark gray, 70% opaque
RGBA.fromValues(0.0, 0.5, 1.0, 1.0) // Light blue
```
### Common Color Patterns
```typescript
// Theme colors
const primary = RGBA.fromHex("#7aa2f7") // Tokyo Night blue
const background = RGBA.fromHex("#1a1a2e")
const foreground = RGBA.fromHex("#c0caf5")
const error = RGBA.fromHex("#f7768e")
// Overlays and shadows
const modalOverlay = RGBA.fromValues(0.0, 0.0, 0.0, 0.5) // 50% black
const shadow = RGBA.fromInts(0, 0, 0, 77) // 30% black
// Borders
const activeBorder = RGBA.fromHex("#7aa2f7")
const inactiveBorder = RGBA.fromInts(65, 72, 104, 255)
```
### parseColor Utility
```typescript
// Accepts multiple formats
parseColor("#FF0000") // Hex string
parseColor("red") // CSS color name
parseColor("transparent") // Special values
parseColor(RGBA.fromHex("#F00")) // Pass-through RGBA objects
```
### When to Use Each Method
| Method | Use When |
|--------|----------|
| `fromHex()` | Working with design specs, CSS colors, config files |
| `fromInts()` | You have 8-bit values (0-255), common in graphics |
| `fromValues()` | Doing color interpolation, animations, math |
| `parseColor()` | Accepting user input or config that could be any format |
### Using RGBA in React/Solid
```tsx
// Import from @opentui/core, use in any framework
import { RGBA } from "@opentui/core"
// React or Solid component
function ThemedBox() {
const bg = RGBA.fromHex("#1a1a2e")
const border = RGBA.fromInts(122, 162, 247, 255)
return (
<box backgroundColor={bg} borderColor={border} border>
<text fg={RGBA.fromHex("#c0caf5")}>Works everywhere!</text>
</box>
)
}
```
Color props in React/Solid accept both string formats (`"#FF0000"`, `"red"`) and `RGBA` objects.
## Keyboard Input
```typescript
import { type KeyEvent } from "@opentui/core"
renderer.keyInput.on("keypress", (key: KeyEvent) => {
console.log(key.name) // "a", "escape", "f1", etc.
console.log(key.sequence) // Raw escape sequence
console.log(key.ctrl) // Ctrl held
console.log(key.shift) // Shift held
console.log(key.meta) // Alt held
console.log(key.option) // Option held (macOS)
console.log(key.eventType) // "press" | "release" | "repeat"
})
renderer.keyInput.on("paste", (event: PasteEvent) => {
const text = decodePasteBytes(event.bytes)
console.log("Pasted:", text)
})
```
## Animation Timeline
```typescript
import { Timeline, engine } from "@opentui/core"
const timeline = new Timeline({
duration: 2000,
loop: false,
autoplay: true,
})
timeline.add(
{ width: 0 },
{
width: 50,
duration: 1000,
ease: "easeOutQuad",
onUpdate: (anim) => {
box.setWidth(anim.targets[0].width)
},
},
)
engine.attach(renderer)
engine.addTimeline(timeline)
```
## Type Exports
```typescript
import type {
CliRenderer,
CliRendererConfig,
RenderContext,
KeyEvent,
Renderable,
// ... and more
} from "@opentui/core"
```
@@ -0,0 +1,168 @@
# Core Configuration
## Renderer Configuration
### createCliRenderer Options
```typescript
import { createCliRenderer, ConsolePosition } from "@opentui/core"
const renderer = await createCliRenderer({
// Rendering
targetFPS: 60, // Target frames per second (default: 60)
// Behavior
exitOnCtrlC: true, // Exit on Ctrl+C (default: true)
// Console overlay
consoleOptions: {
position: ConsolePosition.BOTTOM, // BOTTOM | TOP | LEFT | RIGHT
sizePercent: 30, // Percentage of screen
colorInfo: "#00FFFF",
colorWarn: "#FFFF00",
colorError: "#FF0000",
colorDebug: "#888888",
startInDebugMode: false,
},
// Lifecycle
onDestroy: () => {
// Cleanup callback
},
})
```
## Environment Variables
OpenTUI respects several environment variables for configuration and debugging.
### Debug & Development
| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `OTUI_DEBUG` | boolean | false | Enable debug mode, capture raw input |
| `OTUI_DEBUG_FFI` | boolean | false | Debug logging for FFI bindings |
| `OTUI_TRACE_FFI` | boolean | false | Tracing for FFI bindings |
| `OTUI_SHOW_STATS` | boolean | false | Show debug overlay at startup |
| `OTUI_DUMP_CAPTURES` | boolean | false | Dump captured output on exit |
### Console
| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `OTUI_USE_CONSOLE` | boolean | true | Enable console capture |
| `SHOW_CONSOLE` | boolean | false | Show console at startup |
### Rendering
| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `OTUI_NO_NATIVE_RENDER` | boolean | false | Disable ANSI output (for debugging) |
| `OTUI_USE_ALTERNATE_SCREEN` | boolean | true | Use alternate screen buffer |
| `OTUI_OVERRIDE_STDOUT` | boolean | true | Override stdout stream |
### Terminal Capabilities
| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `OPENTUI_NO_GRAPHICS` | boolean | false | Disable Kitty graphics protocol |
| `OPENTUI_FORCE_UNICODE` | boolean | false | Force Mode 2026 Unicode support |
| `OPENTUI_FORCE_WCWIDTH` | boolean | false | Use wcwidth for character width |
| `OPENTUI_FORCE_NOZWJ` | boolean | false | Disable ZWJ emoji joining |
| `OPENTUI_FORCE_EXPLICIT_WIDTH` | string | - | Force explicit width ("true"/"false") |
### Tree-sitter (Syntax Highlighting)
| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `OTUI_TS_STYLE_WARN` | boolean | false | Warn on missing syntax styles |
| `OTUI_TREE_SITTER_WORKER_PATH` | string | "" | Custom tree-sitter worker path |
### XDG Paths
| Variable | Type | Default | Description |
|----------|------|---------|-------------|
| `XDG_CONFIG_HOME` | string | "" | User config directory |
| `XDG_DATA_HOME` | string | "" | User data directory |
## Usage Examples
### Development Mode
```bash
# Show debug overlay and console
OTUI_SHOW_STATS=true SHOW_CONSOLE=true bun run src/index.ts
# Debug FFI issues
OTUI_DEBUG_FFI=true OTUI_TRACE_FFI=true bun run src/index.ts
# Disable native rendering for testing
OTUI_NO_NATIVE_RENDER=true bun run src/index.ts
```
### Terminal Compatibility
```bash
# Force wcwidth for problematic terminals
OPENTUI_FORCE_WCWIDTH=true bun run src/index.ts
# Disable graphics for SSH sessions
OPENTUI_NO_GRAPHICS=true bun run src/index.ts
```
## Project Setup
### package.json
```json
{
"name": "my-tui-app",
"type": "module",
"scripts": {
"start": "bun run src/index.ts",
"dev": "bun --watch run src/index.ts",
"test": "bun test"
},
"dependencies": {
"@opentui/core": "latest"
},
"devDependencies": {
"@types/bun": "latest",
"typescript": "latest"
}
}
```
### tsconfig.json
```json
{
"compilerOptions": {
"lib": ["ESNext"],
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"types": ["bun-types"]
},
"include": ["src/**/*"]
}
```
> **Note**: OpenTUI uses `NodeNext` module resolution. All internal imports use `.js` extensions. If you use `bundler` resolution, imports still work but `NodeNext` is recommended for compatibility.
## Building Native Code
Native code changes require rebuilding:
```bash
# From repo root (if developing OpenTUI itself)
bun run build
# Zig is required for native compilation
# Install: https://ziglang.org/learn/getting-started/
```
**Note**: TypeScript changes do NOT require building. Bun runs TypeScript directly.
@@ -0,0 +1,393 @@
# Core Gotchas
## Runtime Environment
### Use Bun, Not Node.js
OpenTUI is built for Bun. Always use Bun commands:
```bash
# CORRECT
bun install @opentui/core
bun run src/index.ts
bun test
# WRONG
npm install @opentui/core
node src/index.ts
npx jest
```
### Bun APIs to Use
Prefer Bun's built-in APIs for your application code:
```typescript
// CORRECT - Bun APIs
Bun.serve({ ... }) // Instead of express
Bun.$`ls -la` // Instead of execa
import { Database } from "bun:sqlite" // Instead of better-sqlite3
// WRONG - Node.js patterns
import express from "express"
```
> **Note**: OpenTUI itself uses `node:fs` internally for file I/O (for broader compatibility), but your application code should still prefer Bun APIs where available.
### Avoid process.exit()
**Never use `process.exit()` directly** - it prevents proper terminal cleanup and can leave the terminal in a broken state (alternate screen mode, raw input mode, etc.).
```typescript
// WRONG - Terminal may be left in broken state
if (error) {
console.error("Fatal error")
process.exit(1)
}
// CORRECT - Use renderer.destroy() for cleanup
if (error) {
console.error("Fatal error")
await renderer.destroy()
process.exit(1) // Only after destroy
}
// BETTER - Let destroy handle exit
const renderer = await createCliRenderer({
exitOnCtrlC: true, // Handles Ctrl+C properly
})
// For programmatic exit
renderer.destroy() // Cleans up and exits
```
`renderer.destroy()` restores the terminal to its original state before exiting.
### Environment Variables
Bun auto-loads `.env` files. Don't use dotenv:
```typescript
// CORRECT
const apiKey = process.env.API_KEY
// WRONG
import dotenv from "dotenv"
dotenv.config()
```
## Debugging TUIs
### Cannot See console.log Output
OpenTUI captures console output for the debug overlay. You can't see logs in the terminal while the TUI is running.
**Solutions:**
1. **Use the console overlay:**
```typescript
const renderer = await createCliRenderer()
renderer.console.show()
console.log("This appears in the overlay")
```
2. **Toggle with keyboard:**
```typescript
renderer.keyInput.on("keypress", (key) => {
if (key.name === "f12") {
renderer.console.toggle()
}
})
```
3. **Write to a file:**
```typescript
import { appendFileSync } from "node:fs"
function debugLog(msg: string) {
appendFileSync("debug.log", `${new Date().toISOString()} ${msg}\n`)
}
```
4. **Disable console capture:**
```bash
OTUI_USE_CONSOLE=false bun run src/index.ts
```
### Reproduce Issues in Tests
Don't guess at bugs. Create a reproducible test:
```typescript
import { test, expect } from "bun:test"
import { createTestRenderer } from "@opentui/core/testing"
test("reproduces the issue", async () => {
const { renderer, snapshot } = await createTestRenderer({
width: 40,
height: 10,
})
// Setup that reproduces the bug
const box = new BoxRenderable(renderer, { ... })
renderer.root.add(box)
// Verify with snapshot
expect(snapshot()).toMatchSnapshot()
})
```
## Focus Management
### Components Must Be Focused
Input components only receive keyboard input when focused:
```typescript
const input = new InputRenderable(renderer, {
id: "input",
placeholder: "Type here...",
})
renderer.root.add(input)
// WRONG - input won't receive keystrokes
// (no focus call)
// CORRECT
input.focus()
```
### Focus in Nested Components
When a component is inside a container, focus the component directly:
```typescript
const container = new BoxRenderable(renderer, { id: "container" })
const input = new InputRenderable(renderer, { id: "input" })
container.add(input)
renderer.root.add(container)
// WRONG
container.focus()
// CORRECT
input.focus()
// Or use getRenderable
container.getRenderable("input")?.focus()
// Or use delegate (constructs)
const form = delegate(
{ focus: "input" },
Box({}, Input({ id: "input" })),
)
form.focus() // Routes to the input
```
## Build Requirements
### Zig is Required
Native code compilation requires Zig:
```bash
# Install Zig first
# macOS
brew install zig
# Linux
# Download from https://ziglang.org/download/
# Then build
bun run build
```
### When to Build
- **TypeScript changes**: NO build needed (Bun runs TS directly)
- **Native code changes**: Build required
```bash
# Only needed when changing native (Zig) code
cd packages/core
bun run build
```
## Common Errors
### "Cannot read properties of undefined"
Usually means a renderable wasn't added to the tree:
```typescript
// WRONG - not added to tree
const text = new TextRenderable(renderer, { content: "Hello" })
// text.someMethod() // May fail
// CORRECT
const text = new TextRenderable(renderer, { content: "Hello" })
renderer.root.add(text)
text.someMethod()
```
### Layout Not Updating
Yoga layout is calculated lazily. Force a recalculation:
```typescript
// After changing layout properties
box.setWidth(newWidth)
renderer.requestRender()
```
### Text Overflow/Clipping
Text doesn't wrap by default. Set explicit width:
```typescript
// May overflow
const text = new TextRenderable(renderer, {
content: "Very long text that might overflow the terminal...",
})
// Contained within width
const text = new TextRenderable(renderer, {
content: "Very long text that might overflow the terminal...",
width: 40, // Will clip or wrap based on parent
})
```
### Colors Not Showing
Check terminal capability and color format:
```typescript
// CORRECT formats
fg: "#FF0000" // Hex
fg: "red" // CSS color name
fg: RGBA.fromHex("#FF0000")
// WRONG
fg: "FF0000" // Missing #
fg: 0xFF0000 // Number (not supported)
```
## Performance
### Avoid Frequent Re-renders
Batch updates when possible:
```typescript
// WRONG - multiple render calls
item1.setContent("...")
item2.setContent("...")
item3.setContent("...")
// BETTER - single render after all updates
// (OpenTUI batches automatically, but be mindful)
items.forEach((item, i) => {
item.setContent(data[i])
})
```
### Minimize Tree Depth
Deep nesting impacts layout calculation:
```typescript
// Avoid unnecessary wrappers
// WRONG
Box({}, Box({}, Box({}, Text({ content: "Hello" }))))
// CORRECT
Box({}, Text({ content: "Hello" }))
```
### Use display: none
Hide elements instead of removing/re-adding:
```typescript
// For toggling visibility
element.setDisplay("none") // Hidden
element.setDisplay("flex") // Visible
// Instead of
parent.remove(element)
parent.add(element)
```
## Testing
### Test Runner
Use Bun's test runner:
```typescript
import { test, expect, beforeEach, afterEach } from "bun:test"
test("my test", () => {
expect(1 + 1).toBe(2)
})
```
### Test from Package Directories
Run tests from the specific package directory:
```bash
# CORRECT
cd packages/core
bun test
# For native tests
cd packages/core
bun run test:native
```
### Filter Tests
```bash
# Bun test filter
bun test --filter "component name"
# Native test filter
bun run test:native -Dtest-filter="test name"
```
## Keyboard Handling
### Key Names
Common key names for `KeyEvent.name`:
```typescript
// Letters/numbers
"a", "b", ..., "z"
"1", "2", ..., "0"
// Special keys
"escape", "enter", "return", "tab", "backspace", "delete"
"up", "down", "left", "right"
"home", "end", "pageup", "pagedown"
"f1", "f2", ..., "f12"
"space"
// Modifiers (check boolean properties)
key.ctrl // Ctrl held
key.shift // Shift held
key.meta // Alt held
key.option // Option held (macOS)
```
### Key Event Types
```typescript
renderer.keyInput.on("keypress", (key) => {
// eventType: "press" | "release" | "repeat"
if (key.eventType === "repeat") {
// Key being held down
}
})
```
@@ -0,0 +1,449 @@
# Core Patterns
## Composition Patterns
### Imperative Composition
Create renderables and compose with `.add()`:
```typescript
import { createCliRenderer, BoxRenderable, TextRenderable } from "@opentui/core"
const renderer = await createCliRenderer()
// Create parent
const container = new BoxRenderable(renderer, {
id: "container",
flexDirection: "column",
padding: 1,
})
// Create children
const header = new TextRenderable(renderer, {
id: "header",
content: "Header",
fg: "#00FF00",
})
const body = new TextRenderable(renderer, {
id: "body",
content: "Body content",
})
// Compose tree
container.add(header)
container.add(body)
renderer.root.add(container)
```
### Declarative Composition (Constructs)
Use VNode functions for cleaner composition:
```typescript
import { createCliRenderer, Box, Text, Input, delegate } from "@opentui/core"
const renderer = await createCliRenderer()
// Compose as function calls
const ui = Box(
{ flexDirection: "column", padding: 1 },
Text({ content: "Header", fg: "#00FF00" }),
Box(
{ flexDirection: "row", gap: 2 },
Text({ content: "Name:" }),
Input({ id: "name", placeholder: "Enter name..." }),
),
)
renderer.root.add(ui)
```
### Reusable Components
Create factory functions for reusable UI pieces:
```typescript
// Imperative factory
function createLabeledInput(
renderer: RenderContext,
props: { id: string; label: string; placeholder: string }
) {
const container = new BoxRenderable(renderer, {
id: `${props.id}-container`,
flexDirection: "row",
gap: 1,
})
container.add(new TextRenderable(renderer, {
id: `${props.id}-label`,
content: props.label,
}))
container.add(new InputRenderable(renderer, {
id: `${props.id}-input`,
placeholder: props.placeholder,
width: 20,
}))
return container
}
// Declarative factory
function LabeledInput(props: { id: string; label: string; placeholder: string }) {
return delegate(
{ focus: `${props.id}-input` },
Box(
{ flexDirection: "row", gap: 1 },
Text({ content: props.label }),
Input({
id: `${props.id}-input`,
placeholder: props.placeholder,
width: 20,
}),
),
)
}
```
### Focus Delegation
Route focus calls to nested elements:
```typescript
import { delegate, Box, Input, Text } from "@opentui/core"
const form = delegate(
{
focus: "email-input", // Route .focus() to this child
blur: "email-input", // Route .blur() to this child
},
Box(
{ border: true, padding: 1 },
Text({ content: "Email:" }),
Input({ id: "email-input", placeholder: "you@example.com" }),
),
)
// This focuses the input inside, not the box
form.focus()
```
## Event Handling
### Keyboard Events
```typescript
const renderer = await createCliRenderer()
// Global keyboard handler
renderer.keyInput.on("keypress", (key) => {
if (key.name === "escape") {
renderer.destroy()
process.exit(0)
}
if (key.ctrl && key.name === "c") {
// Ctrl+C handling (if exitOnCtrlC is false)
}
if (key.name === "tab") {
// Tab navigation
focusNext()
}
})
// Paste events
renderer.keyInput.on("paste", (event) => {
const text = decodePasteBytes(event.bytes)
currentInput?.setValue(currentInput.value + text)
})
```
### Component Events
```typescript
import { InputRenderable, InputRenderableEvents } from "@opentui/core"
const input = new InputRenderable(renderer, {
id: "search",
placeholder: "Search...",
})
input.on(InputRenderableEvents.CHANGE, (value) => {
performSearch(value)
})
// Select events
const select = new SelectRenderable(renderer, {
id: "menu",
options: [...],
})
select.on(SelectRenderableEvents.ITEM_SELECTED, (index, option) => {
handleSelection(option)
})
select.on(SelectRenderableEvents.SELECTION_CHANGED, (index, option) => {
showPreview(option)
})
```
### Mouse Events
```typescript
const button = new BoxRenderable(renderer, {
id: "button",
border: true,
onMouseDown: (event) => {
button.setBackgroundColor("#444444")
},
onMouseUp: (event) => {
button.setBackgroundColor("#222222")
handleClick()
},
onMouseMove: (event) => {
// Hover effect
},
})
```
## State Management
### Local State
Manage state in closures or objects:
```typescript
// Closure-based state
function createCounter(renderer: RenderContext) {
let count = 0
const display = new TextRenderable(renderer, {
id: "count",
content: `Count: ${count}`,
})
const increment = () => {
count++
display.setContent(`Count: ${count}`)
}
return { display, increment }
}
// Class-based state
class CounterWidget {
private count = 0
private display: TextRenderable
constructor(renderer: RenderContext) {
this.display = new TextRenderable(renderer, {
id: "count",
content: this.formatCount(),
})
}
private formatCount() {
return `Count: ${this.count}`
}
increment() {
this.count++
this.display.setContent(this.formatCount())
}
getRenderable() {
return this.display
}
}
```
### Focus Management
Track and manage focus across components:
```typescript
class FocusManager {
private focusables: Renderable[] = []
private currentIndex = 0
register(renderable: Renderable) {
this.focusables.push(renderable)
}
focusNext() {
this.focusables[this.currentIndex]?.blur()
this.currentIndex = (this.currentIndex + 1) % this.focusables.length
this.focusables[this.currentIndex]?.focus()
}
focusPrevious() {
this.focusables[this.currentIndex]?.blur()
this.currentIndex = (this.currentIndex - 1 + this.focusables.length) % this.focusables.length
this.focusables[this.currentIndex]?.focus()
}
}
// Usage
const focusManager = new FocusManager()
focusManager.register(input1)
focusManager.register(input2)
focusManager.register(select1)
renderer.keyInput.on("keypress", (key) => {
if (key.name === "tab") {
key.shift ? focusManager.focusPrevious() : focusManager.focusNext()
}
})
```
## Lifecycle Patterns
### Cleanup
Always clean up resources:
```typescript
const renderer = await createCliRenderer()
// Track intervals/timeouts
const intervals: Timer[] = []
intervals.push(setInterval(() => {
updateClock()
}, 1000))
// Cleanup on exit
process.on("SIGINT", () => {
intervals.forEach(clearInterval)
renderer.destroy()
process.exit(0)
})
// Or use onDestroy callback
const renderer = await createCliRenderer({
onDestroy: () => {
intervals.forEach(clearInterval)
},
})
```
### Dynamic Updates
Update UI based on external data:
```typescript
async function createDashboard(renderer: RenderContext) {
const statsText = new TextRenderable(renderer, {
id: "stats",
content: "Loading...",
})
// Poll for updates
const updateStats = async () => {
const data = await fetchStats()
statsText.setContent(`CPU: ${data.cpu}% | Memory: ${data.memory}%`)
}
// Initial load
await updateStats()
// Periodic updates
setInterval(updateStats, 5000)
return statsText
}
```
## Layout Patterns
### Responsive Layout
Adapt to terminal size:
```typescript
const renderer = await createCliRenderer()
const mainPanel = new BoxRenderable(renderer, {
id: "main",
width: "100%",
height: "100%",
flexDirection: renderer.width > 80 ? "row" : "column",
})
// Listen for resize
process.stdout.on("resize", () => {
mainPanel.setFlexDirection(renderer.width > 80 ? "row" : "column")
})
```
### Split Panels
```typescript
function createSplitView(renderer: RenderContext, ratio = 0.3) {
const container = new BoxRenderable(renderer, {
id: "split",
flexDirection: "row",
width: "100%",
height: "100%",
})
const left = new BoxRenderable(renderer, {
id: "left",
width: `${ratio * 100}%`,
border: true,
})
const right = new BoxRenderable(renderer, {
id: "right",
flexGrow: 1,
border: true,
})
container.add(left)
container.add(right)
return { container, left, right }
}
```
## Debugging Patterns
### Console Overlay
Use the built-in console for debugging:
```typescript
const renderer = await createCliRenderer({
consoleOptions: {
startInDebugMode: true,
},
})
// Show console
renderer.console.show()
// All console methods work
console.log("Debug info")
console.warn("Warning")
console.error("Error")
// Toggle with keyboard
renderer.keyInput.on("keypress", (key) => {
if (key.name === "f12") {
renderer.console.toggle()
}
})
```
### State Inspection
```typescript
function debugState(label: string, state: unknown) {
console.log(`[${label}]`, JSON.stringify(state, null, 2))
}
// In your update logic
debugState("form", { name: nameInput.value, email: emailInput.value })
```
@@ -0,0 +1,617 @@
# Keyboard Input Handling
How to handle keyboard input in OpenTUI applications.
## Overview
OpenTUI provides keyboard input handling through:
- **Core**: `renderer.keyInput` EventEmitter
- **React**: `useKeyboard()` hook
- **Solid**: `useKeyboard()` hook
## When to Use
Use this reference when you need keyboard shortcuts, focus-aware input handling, or custom keybindings.
## KeyEvent Object
All keyboard handlers receive a `KeyEvent` object:
```typescript
interface KeyEvent {
name: string // Key name: "a", "escape", "f1", etc.
sequence: string // Raw escape sequence
ctrl: boolean // Ctrl modifier held
shift: boolean // Shift modifier held
meta: boolean // Alt modifier held
option: boolean // Option modifier held (macOS)
eventType: "press" | "release" | "repeat"
repeated: boolean // Key is being held (repeat event)
}
```
## Basic Usage
### Core
```typescript
import { createCliRenderer, type KeyEvent } from "@opentui/core"
const renderer = await createCliRenderer()
renderer.keyInput.on("keypress", (key: KeyEvent) => {
if (key.name === "escape") {
renderer.destroy()
return
}
if (key.ctrl && key.name === "s") {
saveDocument()
}
})
```
### React
```tsx
import { useKeyboard, useRenderer } from "@opentui/react"
function App() {
const renderer = useRenderer()
useKeyboard((key) => {
if (key.name === "escape") {
renderer.destroy()
}
})
return <text>Press ESC to exit</text>
}
```
### Solid
```tsx
import { useKeyboard, useRenderer } from "@opentui/solid"
function App() {
const renderer = useRenderer()
useKeyboard((key) => {
if (key.name === "escape") {
renderer.destroy()
}
})
return <text>Press ESC to exit</text>
}
```
## Key Names
### Alphabetic Keys
Lowercase: `a`, `b`, `c`, ... `z`
With Shift: Check `key.shift && key.name === "a"` for uppercase
### Numeric Keys
`0`, `1`, `2`, ... `9`
### Function Keys
`f1`, `f2`, `f3`, ... `f12`
### Special Keys
| Key Name | Description |
|----------|-------------|
| `escape` | Escape key |
| `enter` | Enter/Return |
| `return` | Enter/Return (alias) |
| `tab` | Tab key |
| `backspace` | Backspace |
| `delete` | Delete key |
| `space` | Spacebar |
### Arrow Keys
| Key Name | Description |
|----------|-------------|
| `up` | Up arrow |
| `down` | Down arrow |
| `left` | Left arrow |
| `right` | Right arrow |
### Navigation Keys
| Key Name | Description |
|----------|-------------|
| `home` | Home key |
| `end` | End key |
| `pageup` | Page Up |
| `pagedown` | Page Down |
| `insert` | Insert key |
## Modifier Keys
Check modifier properties on `KeyEvent`:
```typescript
renderer.keyInput.on("keypress", (key) => {
if (key.ctrl && key.name === "c") {
// Ctrl+C
}
if (key.shift && key.name === "tab") {
// Shift+Tab
}
if (key.meta && key.name === "s") {
// Alt+S (meta = Alt on most systems)
}
if (key.option && key.name === "a") {
// Option+A (macOS)
}
})
```
### Modifier Combinations
```typescript
// Ctrl+Shift+S
if (key.ctrl && key.shift && key.name === "s") {
saveAs()
}
// Ctrl+Alt+Delete (careful with system shortcuts!)
if (key.ctrl && key.meta && key.name === "delete") {
// ...
}
```
## Event Types
### Press Events (Default)
Normal key press:
```typescript
renderer.keyInput.on("keypress", (key) => {
if (key.eventType === "press") {
// Initial key press
}
})
```
### Repeat Events
Key held down:
```typescript
renderer.keyInput.on("keypress", (key) => {
if (key.eventType === "repeat" || key.repeated) {
// Key is being held
}
})
```
### Release Events
Key released (opt-in):
```tsx
// React
useKeyboard(
(key) => {
if (key.eventType === "release") {
// Key released
}
},
{ release: true } // Enable release events
)
// Solid
useKeyboard(
(key) => {
if (key.eventType === "release") {
// Key released
}
},
{ release: true }
)
```
## Patterns
### Navigation Menu
```tsx
function Menu() {
const [selectedIndex, setSelectedIndex] = useState(0)
const items = ["Home", "Settings", "Help", "Quit"]
useKeyboard((key) => {
switch (key.name) {
case "up":
case "k":
setSelectedIndex(i => Math.max(0, i - 1))
break
case "down":
case "j":
setSelectedIndex(i => Math.min(items.length - 1, i + 1))
break
case "enter":
handleSelect(items[selectedIndex])
break
}
})
return (
<box flexDirection="column">
{items.map((item, i) => (
<text
key={item}
fg={i === selectedIndex ? "#00FF00" : "#FFFFFF"}
>
{i === selectedIndex ? "> " : " "}{item}
</text>
))}
</box>
)
}
```
### Modal Escape
```tsx
function Modal({ onClose, children }) {
useKeyboard((key) => {
if (key.name === "escape") {
onClose()
}
})
return (
<box border padding={2}>
{children}
</box>
)
}
```
### Vim-style Modes
```tsx
function Editor() {
const [mode, setMode] = useState<"normal" | "insert">("normal")
const [content, setContent] = useState("")
useKeyboard((key) => {
if (mode === "normal") {
switch (key.name) {
case "i":
setMode("insert")
break
case "escape":
// Already in normal mode
break
case "j":
moveCursorDown()
break
case "k":
moveCursorUp()
break
}
} else if (mode === "insert") {
if (key.name === "escape") {
setMode("normal")
}
// Input component handles text in insert mode
}
})
return (
<box flexDirection="column">
<text>Mode: {mode}</text>
<textarea
value={content}
onChange={setContent}
focused={mode === "insert"}
/>
</box>
)
}
```
### Game Controls
```tsx
function Game() {
const [pressed, setPressed] = useState(new Set<string>())
useKeyboard(
(key) => {
setPressed(keys => {
const newKeys = new Set(keys)
if (key.eventType === "release") {
newKeys.delete(key.name)
} else {
newKeys.add(key.name)
}
return newKeys
})
},
{ release: true }
)
// Game logic uses pressed set
useEffect(() => {
if (pressed.has("up") || pressed.has("w")) {
moveUp()
}
if (pressed.has("down") || pressed.has("s")) {
moveDown()
}
}, [pressed])
return <text>WASD or arrows to move</text>
}
```
### Keyboard Shortcuts Help
```tsx
function ShortcutsHelp() {
const shortcuts = [
{ keys: "Ctrl+S", action: "Save" },
{ keys: "Ctrl+Q", action: "Quit" },
{ keys: "Ctrl+F", action: "Find" },
{ keys: "Tab", action: "Next field" },
{ keys: "Shift+Tab", action: "Previous field" },
]
return (
<box border title="Keyboard Shortcuts" padding={1}>
{shortcuts.map(({ keys, action }) => (
<box key={keys} flexDirection="row">
<text width={15} fg="#00FFFF">{keys}</text>
<text>{action}</text>
</box>
))}
</box>
)
}
```
## Paste Events
Handle pasted content. Paste events deliver raw bytes, not decoded text.
### PasteEvent Object
```typescript
import { type PasteEvent } from "@opentui/core"
interface PasteEvent {
type: "paste" // Always "paste"
bytes: Uint8Array // Raw pasted bytes
metadata?: PasteMetadata // Optional metadata
preventDefault(): void // Prevent default paste handling
defaultPrevented: boolean // Whether preventDefault was called
}
interface PasteMetadata {
mimeType?: string // MIME type if available
kind?: PasteKind // Paste kind
}
```
### Decoding Paste Bytes
Use `decodePasteBytes` to convert raw bytes to a string, and `stripAnsiSequences` to remove ANSI escape codes:
```typescript
import { decodePasteBytes, stripAnsiSequences } from "@opentui/core"
const text = decodePasteBytes(event.bytes) // Decode UTF-8
const clean = stripAnsiSequences(decodePasteBytes(event.bytes)) // Decode + strip ANSI
```
### Core
```typescript
import { type PasteEvent, decodePasteBytes } from "@opentui/core"
renderer.keyInput.on("paste", (event: PasteEvent) => {
const text = decodePasteBytes(event.bytes)
console.log("Pasted:", text)
})
```
### Solid
Solid provides a dedicated `usePaste` hook:
```tsx
import { usePaste } from "@opentui/solid"
import { decodePasteBytes } from "@opentui/core"
function App() {
usePaste((event) => {
const text = decodePasteBytes(event.bytes)
console.log("Pasted:", text)
})
return <text>Paste something</text>
}
```
> **Note**: `usePaste` is **Solid-only**. React does not have this hook - handle paste via the Core event emitter or input component's `onChange`.
## Text Selection
Text selection is renderer-managed. The renderer owns a single `Selection` object, walks the renderable tree to find selectable children, and emits a `"selection"` event when the user finishes selecting (mouse-up). The `Selection` object aggregates text from all selected renderables automatically.
### Making Renderables Selectable
A renderable must have `selectable` set to `true` to participate in selection. Text-based renderables (`TextRenderable`, `TextareaRenderable`, `ASCIIFontRenderable`, `TextTableRenderable`) support this:
```tsx
// React / Solid
<text selectable>This text can be selected</text>
// Core
const text = new TextRenderable(renderer, {
id: "label",
content: "This text can be selected",
selectable: true,
})
```
### Copy-on-Selection (Core)
Listen to the renderer's `"selection"` event. The `Selection` object's `getSelectedText()` returns text aggregated from all selected renderables in reading order:
```typescript
import type { Selection } from "@opentui/core"
renderer.on("selection", (selection: Selection) => {
const text = selection.getSelectedText()
if (text) {
renderer.copyToClipboardOSC52(text)
}
})
```
> **Important**: Call `selection.getSelectedText()` on the `Selection` object from the event -- not `renderer.root.getSelectedText()`. Individual renderables only return their own selected text. The `Selection` object aggregates across the tree.
### Copy-on-Selection (Solid)
```tsx
import { useSelectionHandler } from "@opentui/solid"
function App() {
useSelectionHandler((selection) => {
const text = selection.getSelectedText()
if (text) {
renderer.copyToClipboardOSC52(text)
}
})
return <text selectable>Select this text</text>
}
```
> **Note**: `useSelectionHandler` is **Solid-only**. React does not have this hook -- use the Core `renderer.on("selection", ...)` event.
### Selection Object
The `Selection` object passed to the event callback:
```typescript
selection.getSelectedText() // Aggregated text from all selected renderables
selection.bounds // { startX, startY, endX, endY } bounding rect
selection.selectedRenderables // Renderable[] with active selections
selection.isActive // Whether selection is still active
```
Individual renderables also expose:
```typescript
renderable.hasSelection() // Does this renderable have selected text?
renderable.getSelectedText() // Selected text in this renderable only
```
### How Selection Traversal Works
When the user drags to select, the renderer:
1. Identifies the selection container (common ancestor of start and end points)
2. Walks all `selectable` descendants within the selection bounds
3. Calls `onSelectionChanged(selection)` on each, which computes local selection
4. Tracks which renderables have active selections in `selection.selectedRenderables`
This means selection works across multiple renderables. Dragging across two `<text selectable>` elements selects text in both, and `selection.getSelectedText()` joins them with newlines.
## Clipboard API (OSC 52)
Copy text to the system clipboard using OSC 52 escape sequences. Works over SSH and in most modern terminal emulators.
```typescript
// Copy to clipboard
const success = renderer.copyToClipboardOSC52("text to copy")
// Check if OSC 52 is supported
if (renderer.isOsc52Supported()) {
renderer.copyToClipboardOSC52("Hello!")
}
// Clear clipboard
renderer.clearClipboardOSC52()
// Target specific clipboard (X11)
import { ClipboardTarget } from "@opentui/core"
renderer.copyToClipboardOSC52("text", ClipboardTarget.Primary) // X11 primary
renderer.copyToClipboardOSC52("text", ClipboardTarget.Clipboard) // System clipboard (default)
```
## Focus and Input Components
Input components (`<input>`, `<textarea>`, `<select>`) capture keyboard events when focused:
```tsx
<input focused /> // Receives keyboard input
// Global useKeyboard still fires, but input consumes characters
```
To prevent conflicts, check if an input is focused before handling global shortcuts:
```tsx
function App() {
const renderer = useRenderer()
const [inputFocused, setInputFocused] = useState(false)
useKeyboard((key) => {
if (inputFocused) return // Let input handle it
// Global shortcuts
if (key.name === "escape") {
renderer.destroy()
}
})
return (
<input
focused={inputFocused}
onFocus={() => setInputFocused(true)}
onBlur={() => setInputFocused(false)}
/>
)
}
```
## Gotchas
### Terminal Limitations
Some key combinations are captured by the terminal or OS:
- `Ctrl+C` often sends SIGINT (use `exitOnCtrlC: false` to handle)
- `Ctrl+Z` suspends the process
- Some function keys may be intercepted
### SSH and Remote Sessions
Key detection may vary over SSH. Test on target environments.
### Multiple Handlers
Multiple `useKeyboard` calls all receive events. Coordinate handlers to prevent conflicts.
## See Also
- [React API](../react/api.md) - `useKeyboard` hook reference
- [Solid API](../solid/api.md) - `useKeyboard` hook reference
- [Input Components](../components/inputs.md) - Focus management with input, textarea, select
- [Testing](../testing/REFERENCE.md) - Simulating key presses in tests
@@ -0,0 +1,337 @@
# OpenTUI Layout System
OpenTUI uses the Yoga layout engine, providing CSS Flexbox-like capabilities for positioning and sizing components in the terminal.
## Overview
Key concepts:
- **Flexbox model**: Familiar CSS Flexbox properties
- **Yoga engine**: Facebook's cross-platform layout engine
- **Terminal units**: Dimensions are in character cells (columns x rows)
- **Percentage support**: Relative sizing based on parent
## Flex Container Properties
### flexDirection
Controls the main axis direction:
```tsx
// Row (default) - children flow horizontally
<box flexDirection="row">
<text>1</text>
<text>2</text>
<text>3</text>
</box>
// Output: 1 2 3
// Column - children flow vertically
<box flexDirection="column">
<text>1</text>
<text>2</text>
<text>3</text>
</box>
// Output:
// 1
// 2
// 3
// Reverse variants
<box flexDirection="row-reverse">...</box> // 3 2 1
<box flexDirection="column-reverse">...</box> // Bottom to top
```
### justifyContent
Aligns children along the main axis:
```tsx
<box flexDirection="row" width={40} justifyContent="flex-start">
{/* Children at start (left for row) */}
</box>
<box flexDirection="row" width={40} justifyContent="flex-end">
{/* Children at end (right for row) */}
</box>
<box flexDirection="row" width={40} justifyContent="center">
{/* Children centered */}
</box>
<box flexDirection="row" width={40} justifyContent="space-between">
{/* First at start, last at end, rest evenly distributed */}
</box>
<box flexDirection="row" width={40} justifyContent="space-around">
{/* Equal space around each child */}
</box>
<box flexDirection="row" width={40} justifyContent="space-evenly">
{/* Equal space between all children and edges */}
</box>
```
### alignItems
Aligns children along the cross axis:
```tsx
<box flexDirection="row" height={10} alignItems="flex-start">
{/* Children at top */}
</box>
<box flexDirection="row" height={10} alignItems="flex-end">
{/* Children at bottom */}
</box>
<box flexDirection="row" height={10} alignItems="center">
{/* Children vertically centered */}
</box>
<box flexDirection="row" height={10} alignItems="stretch">
{/* Children stretch to fill height */}
</box>
<box flexDirection="row" height={10} alignItems="baseline">
{/* Children aligned by text baseline */}
</box>
```
### flexWrap
Controls whether children wrap to new lines:
```tsx
<box flexDirection="row" flexWrap="nowrap" width={20}>
{/* Children overflow (default) */}
</box>
<box flexDirection="row" flexWrap="wrap" width={20}>
{/* Children wrap to next row */}
</box>
<box flexDirection="row" flexWrap="wrap-reverse" width={20}>
{/* Children wrap upward */}
</box>
```
### gap
Space between children:
```tsx
<box flexDirection="row" gap={2}>
<text>A</text>
<text>B</text>
<text>C</text>
</box>
// Output: A B C (2 spaces between)
```
## Flex Item Properties
### flexGrow
How much a child should grow relative to siblings:
```tsx
<box flexDirection="row" width={30}>
<box flexGrow={1}><text>1</text></box>
<box flexGrow={2}><text>2</text></box>
<box flexGrow={1}><text>1</text></box>
</box>
// Widths: 7.5 | 15 | 7.5 (1:2:1 ratio)
```
### flexShrink
How much a child should shrink when space is limited:
```tsx
<box flexDirection="row" width={20}>
<box width={15} flexShrink={1}><text>Shrinks</text></box>
<box width={15} flexShrink={0}><text>Fixed</text></box>
</box>
```
### flexBasis
Initial size before growing/shrinking:
```tsx
<box flexDirection="row">
<box flexBasis={20} flexGrow={1}>Starts at 20, can grow</box>
<box flexBasis="50%">Half of parent</box>
</box>
```
### alignSelf
Override parent's alignItems for this child:
```tsx
<box flexDirection="row" height={10} alignItems="center">
<text>Centered</text>
<text alignSelf="flex-start">Top</text>
<text alignSelf="flex-end">Bottom</text>
</box>
```
## Dimensions
### Fixed Dimensions
```tsx
<box width={40} height={10}>
{/* Exactly 40 columns by 10 rows */}
</box>
```
### Percentage Dimensions
Parent must have explicit size:
```tsx
<box width="100%" height="100%">
<box width="50%" height="50%">
{/* Half of parent */}
</box>
</box>
```
### Min/Max Constraints
```tsx
<box
minWidth={20}
maxWidth={60}
minHeight={5}
maxHeight={20}
>
{/* Constrained sizing */}
</box>
```
## Spacing
### Padding (inside)
```tsx
// All sides
<box padding={2}>Content</box>
// Individual sides
<box
paddingTop={1}
paddingRight={2}
paddingBottom={1}
paddingLeft={2}
>
Content
</box>
```
### Margin (outside)
```tsx
// All sides
<box margin={1}>Content</box>
// Individual sides
<box
marginTop={1}
marginRight={2}
marginBottom={1}
marginLeft={2}
>
Content
</box>
```
## Positioning
### Relative (default)
Element flows in normal document order:
```tsx
<box position="relative">
{/* Normal flow */}
</box>
```
### Absolute
Element positioned relative to nearest positioned ancestor:
```tsx
<box position="relative" width="100%" height="100%">
<box
position="absolute"
left={10}
top={5}
width={20}
height={5}
>
Positioned at (10, 5)
</box>
</box>
```
### Position Properties
```tsx
<box
position="absolute"
left={10} // From left edge
top={5} // From top edge
right={10} // From right edge
bottom={5} // From bottom edge
>
Content
</box>
```
## Display
### Visibility Control
```tsx
// Visible (default)
<box display="flex">Visible</box>
// Hidden (removed from layout)
<box display="none">Hidden</box>
```
## Overflow
```tsx
<box overflow="visible">
{/* Content can extend beyond bounds (default) */}
</box>
<box overflow="hidden">
{/* Content clipped at bounds */}
</box>
<box overflow="scroll">
{/* Scrollable when content exceeds bounds */}
</box>
```
## Z-Index
Control stacking order for overlapping elements:
```tsx
<box position="relative">
<box position="absolute" zIndex={1}>Behind</box>
<box position="absolute" zIndex={2}>In front</box>
</box>
```
## See Also
- [Layout Patterns](./patterns.md) - Common layout recipes
- [Components/Containers](../components/containers.md) - Box and ScrollBox details
@@ -0,0 +1,444 @@
# Layout Patterns
Common layout recipes for terminal user interfaces.
## Full-Screen App
Fill the entire terminal:
```tsx
function App() {
return (
<box width="100%" height="100%">
{/* Content fills terminal */}
</box>
)
}
```
## Header/Content/Footer
Classic app layout:
```tsx
function AppLayout() {
return (
<box flexDirection="column" width="100%" height="100%">
{/* Header - fixed height */}
<box height={3} borderStyle="single" borderBottom>
<text>Header</text>
</box>
{/* Content - fills remaining space */}
<box flexGrow={1}>
<text>Main Content</text>
</box>
{/* Footer - fixed height */}
<box height={1}>
<text>Status: Ready</text>
</box>
</box>
)
}
```
## Sidebar Layout
```tsx
function SidebarLayout() {
return (
<box flexDirection="row" width="100%" height="100%">
{/* Sidebar - fixed width */}
<box width={25} borderStyle="single" borderRight>
<text>Sidebar</text>
</box>
{/* Main - fills remaining space */}
<box flexGrow={1}>
<text>Main Content</text>
</box>
</box>
)
}
```
## Resizable Sidebar
Responsive based on terminal width:
```tsx
function ResponsiveSidebar() {
const dims = useTerminalDimensions() // React: useTerminalDimensions()
const showSidebar = dims.width > 60
const sidebarWidth = Math.min(30, Math.floor(dims.width * 0.3))
return (
<box flexDirection="row" width="100%" height="100%">
{showSidebar && (
<box width={sidebarWidth} border>
<text>Sidebar</text>
</box>
)}
<box flexGrow={1}>
<text>Main</text>
</box>
</box>
)
}
```
## Centered Content
### Horizontally Centered
```tsx
<box width="100%" justifyContent="center">
<box width={40}>
<text>Centered horizontally</text>
</box>
</box>
```
### Vertically Centered
```tsx
<box height="100%" alignItems="center">
<text>Centered vertically</text>
</box>
```
### Both Axes
```tsx
<box
width="100%"
height="100%"
justifyContent="center"
alignItems="center"
>
<box width={40} height={10} border>
<text>Centered both ways</text>
</box>
</box>
```
## Modal/Dialog
Centered overlay:
```tsx
function Modal({ children, visible }) {
if (!visible) return null
return (
<box
position="absolute"
left={0}
top={0}
width="100%"
height="100%"
justifyContent="center"
alignItems="center"
backgroundColor="rgba(0,0,0,0.5)"
>
<box
width={50}
height={15}
border
borderStyle="double"
backgroundColor="#1a1a2e"
padding={2}
>
{children}
</box>
</box>
)
}
```
## Grid Layout
Using flexWrap:
```tsx
function Grid({ items, columns = 3 }) {
const itemWidth = `${Math.floor(100 / columns)}%`
return (
<box flexDirection="row" flexWrap="wrap" width="100%">
{items.map((item, i) => (
<box key={i} width={itemWidth} padding={1}>
<text>{item}</text>
</box>
))}
</box>
)
}
```
## Split Panels
### Horizontal Split
```tsx
function HorizontalSplit({ ratio = 0.5 }) {
return (
<box flexDirection="row" width="100%" height="100%">
<box width={`${ratio * 100}%`} border>
<text>Left Panel</text>
</box>
<box flexGrow={1} border>
<text>Right Panel</text>
</box>
</box>
)
}
```
### Vertical Split
```tsx
function VerticalSplit({ ratio = 0.5 }) {
return (
<box flexDirection="column" width="100%" height="100%">
<box height={`${ratio * 100}%`} border>
<text>Top Panel</text>
</box>
<box flexGrow={1} border>
<text>Bottom Panel</text>
</box>
</box>
)
}
```
## Form Layout
Label + Input pairs:
```tsx
function FormField({ label, children }) {
return (
<box flexDirection="row" marginBottom={1}>
<box width={15}>
<text>{label}:</text>
</box>
<box flexGrow={1}>
{children}
</box>
</box>
)
}
function LoginForm() {
return (
<box flexDirection="column" padding={2} border width={50}>
<FormField label="Username">
<input placeholder="Enter username" />
</FormField>
<FormField label="Password">
<input placeholder="Enter password" />
</FormField>
<box marginTop={2} justifyContent="flex-end">
<box border padding={1}>
<text>Login</text>
</box>
</box>
</box>
)
}
```
## Navigation Tabs
```tsx
function TabBar({ tabs, activeIndex, onSelect }) {
return (
<box flexDirection="row" borderBottom>
{tabs.map((tab, i) => (
<box
key={i}
padding={1}
backgroundColor={i === activeIndex ? "#333" : "transparent"}
onMouseDown={() => onSelect(i)}
>
<text fg={i === activeIndex ? "#fff" : "#888"}>
{tab}
</text>
</box>
))}
</box>
)
}
```
## Sticky Footer
Footer always at bottom:
```tsx
function StickyFooterLayout() {
return (
<box flexDirection="column" width="100%" height="100%">
{/* Content area */}
<box flexGrow={1} flexDirection="column">
{/* Your content here */}
<text>Content that might be short</text>
</box>
{/* Footer pushed to bottom */}
<box height={1}>
<text fg="#888">Press ? for help | q to quit</text>
</box>
</box>
)
}
```
## Absolute Positioning Overlay
Tooltip or popup:
```tsx
function Tooltip({ x, y, children }) {
return (
<box
position="absolute"
left={x}
top={y}
border
backgroundColor="#333"
padding={1}
zIndex={100}
>
{children}
</box>
)
}
```
## Responsive Breakpoints
Different layouts based on terminal size:
```tsx
function ResponsiveApp() {
const { width, height } = useTerminalDimensions()
// Define breakpoints
const isSmall = width < 60
const isMedium = width >= 60 && width < 100
const isLarge = width >= 100
if (isSmall) {
// Mobile-like: stacked layout
return (
<box flexDirection="column">
<Navigation />
<Content />
</box>
)
}
if (isMedium) {
// Tablet-like: sidebar + content
return (
<box flexDirection="row">
<box width={20}><Navigation /></box>
<box flexGrow={1}><Content /></box>
</box>
)
}
// Large: full layout
return (
<box flexDirection="row">
<box width={25}><Navigation /></box>
<box flexGrow={1}><Content /></box>
<box width={30}><Sidebar /></box>
</box>
)
}
```
## Equal Height Columns
```tsx
function EqualColumns() {
return (
<box flexDirection="row" alignItems="stretch" height={20}>
<box flexGrow={1} border>
<text>Short content</text>
</box>
<box flexGrow={1} border>
<text>
Longer content that
spans multiple lines
and takes up space
</text>
</box>
<box flexGrow={1} border>
<text>Medium content</text>
</box>
</box>
)
}
```
## Spacing Utilities
Consistent spacing patterns:
```tsx
// Spacer component
function Spacer({ size = 1 }) {
return <box height={size} width={size} />
}
// Divider component
function Divider() {
return <box height={1} width="100%" backgroundColor="#333" />
}
// Usage
<box flexDirection="column">
<text>Section 1</text>
<Spacer size={2} />
<Divider />
<Spacer size={2} />
<text>Section 2</text>
</box>
```
### Axis Shorthand Props
Use `paddingX`/`paddingY` and `marginX`/`marginY` for horizontal/vertical spacing:
```tsx
// Horizontal padding (left + right)
<box paddingX={4}>
<text>4 chars padding left and right</text>
</box>
// Vertical padding (top + bottom)
<box paddingY={2}>
<text>2 lines padding top and bottom</text>
</box>
// Horizontal margin for centering-like effect
<box marginX={10}>
<text>Indented content</text>
</box>
// Combined for card-like spacing
<box paddingX={3} paddingY={1} marginY={1} border>
<text>Nicely spaced card</text>
</box>
```
These are shorthand for:
- `paddingX={n}` = `paddingLeft={n}` + `paddingRight={n}`
- `paddingY={n}` = `paddingTop={n}` + `paddingBottom={n}`
- `marginX={n}` = `marginLeft={n}` + `marginRight={n}`
- `marginY={n}` = `marginTop={n}` + `marginBottom={n}`
@@ -0,0 +1,174 @@
# OpenTUI React (@opentui/react)
A React reconciler for building terminal user interfaces with familiar React patterns. Write TUIs using JSX, hooks, and component composition.
## Overview
OpenTUI React provides:
- **Custom reconciler**: React components render to OpenTUI renderables
- **JSX intrinsics**: `<text>`, `<box>`, `<input>`, etc.
- **Hooks**: `useKeyboard`, `useRenderer`, `useTimeline`, etc.
- **Full React compatibility**: useState, useEffect, context, and more
## When to Use React
Use the React reconciler when:
- You're familiar with React patterns
- You want declarative UI composition
- You need React's ecosystem (context, state management libraries)
- Building applications with complex state
- Team knows React already
## When NOT to Use React
| Scenario | Use Instead |
|----------|-------------|
| Maximum performance critical | `@opentui/core` (imperative) |
| Fine-grained reactivity | `@opentui/solid` |
| Smallest bundle size | `@opentui/core` |
| Building a framework/library | `@opentui/core` |
## Quick Start
```bash
bunx create-tui@latest -t react my-app
cd my-app
bun run src/index.tsx
```
The CLI creates the `my-app` directory for you - it must **not already exist**.
**Agent guidance**: Always use autonomous mode with `-t <template>` flag. Never use interactive mode (`bunx create-tui@latest my-app` without `-t`) as it requires user prompts that agents cannot respond to.
Or manual setup:
```bash
mkdir my-tui && cd my-tui
bun init
bun install @opentui/react @opentui/core react
```
```tsx
import { createCliRenderer } from "@opentui/core"
import { createRoot } from "@opentui/react"
import { useState } from "react"
function App() {
const [count, setCount] = useState(0)
return (
<box border padding={2}>
<text>Count: {count}</text>
<box
border
onMouseDown={() => setCount(c => c + 1)}
>
<text>Click me!</text>
</box>
</box>
)
}
const renderer = await createCliRenderer()
createRoot(renderer).render(<App />)
```
## Core Concepts
### JSX Elements
React maps JSX intrinsic elements to OpenTUI renderables:
```tsx
// These are not HTML elements!
<text>Hello</text> // TextRenderable
<box border>Content</box> // BoxRenderable
<input placeholder="..." /> // InputRenderable
<select options={[...]} /> // SelectRenderable
```
### Text Modifiers
Inside `<text>`, use modifier elements:
```tsx
<text>
<strong>Bold</strong>, <em>italic</em>, and <u>underlined</u>
<span fg="red">Colored text</span>
<br />
New line with <a href="https://example.com">link</a>
</text>
```
### Styling
Two approaches to styling:
```tsx
// Direct props
<box backgroundColor="blue" padding={2} border>
<text fg="#00FF00">Green text</text>
</box>
// Style prop
<box style={{ backgroundColor: "blue", padding: 2, border: true }}>
<text style={{ fg: "#00FF00" }}>Green text</text>
</box>
```
## Available Components
### Layout & Display
- `<text>` - Styled text content
- `<box>` - Container with borders and layout
- `<scrollbox>` - Scrollable container
- `<ascii-font>` - ASCII art text
### Input
- `<input>` - Single-line text input
- `<textarea>` - Multi-line text input
- `<select>` - List selection
- `<tab-select>` - Tab-based selection
### Code & Diff
- `<code>` - Syntax-highlighted code
- `<line-number>` - Code with line numbers
- `<diff>` - Unified or split diff viewer
### Text Modifiers (inside `<text>`)
- `<span>` - Inline styled text
- `<strong>`, `<b>` - Bold
- `<em>`, `<i>` - Italic
- `<u>` - Underline
- `<br>` - Line break
- `<a>` - Link
## Essential Hooks
```tsx
import {
useRenderer,
useKeyboard,
useOnResize,
useTerminalDimensions,
useTimeline,
} from "@opentui/react"
```
See [API Reference](./api.md) for detailed hook documentation.
## In This Reference
- [Configuration](./configuration.md) - Project setup, tsconfig, bundling
- [API](./api.md) - Components, hooks, createRoot
- [Patterns](./patterns.md) - State management, keyboard handling, forms
- [Gotchas](./gotchas.md) - Common issues, debugging, limitations
## See Also
- [Core](../core/REFERENCE.md) - Underlying imperative API
- [Solid](../solid/REFERENCE.md) - Alternative declarative approach
- [Components](../components/REFERENCE.md) - Component reference by category
- [Layout](../layout/REFERENCE.md) - Flexbox layout system
- [Keyboard](../keyboard/REFERENCE.md) - Input handling and shortcuts
- [Testing](../testing/REFERENCE.md) - Test renderer and snapshots
@@ -0,0 +1,436 @@
# React API Reference
## Rendering
### createRoot(renderer)
Creates a React root for rendering.
```tsx
import { createCliRenderer } from "@opentui/core"
import { createRoot } from "@opentui/react"
const renderer = await createCliRenderer({
exitOnCtrlC: false, // Handle Ctrl+C yourself
})
const root = createRoot(renderer)
root.render(<App />)
```
## Hooks
### useRenderer()
Access the OpenTUI renderer instance.
```tsx
import { useRenderer } from "@opentui/react"
import { useEffect } from "react"
function App() {
const renderer = useRenderer()
useEffect(() => {
// Access renderer properties
console.log(`Terminal: ${renderer.width}x${renderer.height}`)
// Show debug console
renderer.console.show()
// Access theme mode (dark/light based on terminal settings)
console.log(`Theme: ${renderer.themeMode}`) // "dark" | "light" | null
}, [renderer])
return <text>Hello</text>
}
// Listen for theme mode changes
function ThemedApp() {
const renderer = useRenderer()
const [theme, setTheme] = useState(renderer.themeMode ?? "dark")
useEffect(() => {
const handler = (mode: "dark" | "light") => setTheme(mode)
renderer.on("theme_mode", handler)
return () => renderer.off("theme_mode", handler)
}, [renderer])
return (
<box backgroundColor={theme === "dark" ? "#1a1a2e" : "#ffffff"}>
<text fg={theme === "dark" ? "#fff" : "#000"}>
Current theme: {theme}
</text>
</box>
)
}
```
### useKeyboard(handler, options?)
Handle keyboard events.
```tsx
import { useKeyboard, useRenderer } from "@opentui/react"
function App() {
const renderer = useRenderer()
useKeyboard((key) => {
if (key.name === "escape") {
renderer.destroy() // Never use process.exit() directly!
}
if (key.ctrl && key.name === "s") {
saveDocument()
}
})
return <text>Press ESC to exit</text>
}
// With release events
function GameControls() {
const [pressed, setPressed] = useState(new Set<string>())
useKeyboard(
(event) => {
setPressed(keys => {
const newKeys = new Set(keys)
if (event.eventType === "release") {
newKeys.delete(event.name)
} else {
newKeys.add(event.name)
}
return newKeys
})
},
{ release: true } // Include release events
)
return <text>Pressed: {Array.from(pressed).join(", ")}</text>
}
```
**Options:**
- `release?: boolean` - Include key release events (default: false)
**KeyEvent properties:**
- `name: string` - Key name ("a", "escape", "f1", etc.)
- `sequence: string` - Raw escape sequence
- `ctrl: boolean` - Ctrl modifier
- `shift: boolean` - Shift modifier
- `meta: boolean` - Alt modifier
- `option: boolean` - Option modifier (macOS)
- `eventType: "press" | "release" | "repeat"`
- `repeated: boolean` - Key is being held
### useOnResize(callback)
Handle terminal resize events.
```tsx
import { useOnResize } from "@opentui/react"
function App() {
useOnResize((width, height) => {
console.log(`Resized to ${width}x${height}`)
})
return <text>Resize the terminal</text>
}
```
### useTerminalDimensions()
Get reactive terminal dimensions.
```tsx
import { useTerminalDimensions } from "@opentui/react"
function ResponsiveLayout() {
const { width, height } = useTerminalDimensions()
return (
<box flexDirection={width > 80 ? "row" : "column"}>
<box flexGrow={1}>
<text>Width: {width}</text>
</box>
<box flexGrow={1}>
<text>Height: {height}</text>
</box>
</box>
)
}
```
### useTimeline(options?)
Create animations with the timeline system.
```tsx
import { useTimeline } from "@opentui/react"
import { useEffect, useState } from "react"
function AnimatedBox() {
const [width, setWidth] = useState(0)
const timeline = useTimeline({
duration: 2000,
loop: false,
})
useEffect(() => {
timeline.add(
{ width: 0 },
{
width: 50,
duration: 2000,
ease: "easeOutQuad",
onUpdate: (anim) => {
setWidth(Math.round(anim.targets[0].width))
},
}
)
}, [timeline])
return <box style={{ width, height: 3, backgroundColor: "#6a5acd" }} />
}
```
**Options:**
- `duration?: number` - Default duration (ms)
- `loop?: boolean` - Loop the timeline
- `autoplay?: boolean` - Auto-start (default: true)
- `onComplete?: () => void` - Completion callback
- `onPause?: () => void` - Pause callback
**Timeline methods:**
- `add(target, properties, startTime?)` - Add animation
- `play()` - Start playback
- `pause()` - Pause playback
- `restart()` - Restart from beginning
## Components
### Text Component
```tsx
<text
content="Hello" // Or use children
fg="#FFFFFF" // Foreground color
bg="#000000" // Background color
selectable={true} // Allow text selection
>
{/* Use nested modifier tags for styling */}
<span fg="red">Red</span>
<strong>Bold</strong>
<em>Italic</em>
<u>Underline</u>
<br />
<a href="https://...">Link</a>
</text>
```
> **Note**: Do NOT use `bold`, `italic`, `underline` as props on `<text>`. Use nested modifier tags like `<strong>`, `<em>`, `<u>` instead.
### Box Component
```tsx
<box
// Borders
border // Enable border
borderStyle="single" // single | double | rounded | bold
borderColor="#FFFFFF"
title="Title"
titleAlignment="center" // left | center | right
// Colors
backgroundColor="#1a1a2e"
// Layout (see layout/REFERENCE.md)
flexDirection="row"
justifyContent="center"
alignItems="center"
gap={2}
// Spacing
padding={2}
paddingTop={1}
paddingX={2} // Horizontal (left + right)
paddingY={1} // Vertical (top + bottom)
margin={1}
marginX={2} // Horizontal (left + right)
marginY={1} // Vertical (top + bottom)
// Dimensions
width={40}
height={10}
flexGrow={1}
// Focus
focusable // Allow box to receive focus
focused={isFocused} // Controlled focus state
// Events
onMouseDown={(e) => {}}
onMouseUp={(e) => {}}
onMouseMove={(e) => {}}
>
{children}
</box>
```
### Scrollbox Component
```tsx
<scrollbox
focused // Enable keyboard scrolling
style={{
rootOptions: { backgroundColor: "#24283b" },
wrapperOptions: { backgroundColor: "#1f2335" },
viewportOptions: { backgroundColor: "#1a1b26" },
contentOptions: { backgroundColor: "#16161e" },
scrollbarOptions: {
showArrows: true,
trackOptions: {
foregroundColor: "#7aa2f7",
backgroundColor: "#414868",
},
},
}}
>
{/* Scrollable content */}
{items.map((item, i) => (
<box key={i}>
<text>{item}</text>
</box>
))}
</scrollbox>
```
### Input Component
```tsx
<input
value={value}
onChange={(newValue) => setValue(newValue)}
placeholder="Enter text..."
focused // Start focused
width={30}
backgroundColor="#1a1a1a"
textColor="#FFFFFF"
cursorColor="#00FF00"
focusedBackgroundColor="#2a2a2a"
/>
```
### Textarea Component
```tsx
<textarea
value={text}
onChange={(newValue) => setText(newValue)}
placeholder="Enter multiple lines..."
focused
width={40}
height={10}
showLineNumbers
wrapText
/>
```
### Select Component
```tsx
<select
options={[
{ name: "Option 1", description: "First option", value: "1" },
{ name: "Option 2", description: "Second option", value: "2" },
]}
onChange={(index, option) => setSelected(option)}
selectedIndex={0}
focused
showScrollIndicator
height={8}
/>
```
### Tab Select Component
```tsx
<tab-select
options={[
{ name: "Home", description: "Dashboard" },
{ name: "Settings", description: "Configuration" },
]}
onChange={(index, option) => setTab(option)}
tabWidth={20}
focused
/>
```
### ASCII Font Component
```tsx
<ascii-font
text="TITLE"
font="tiny" // tiny | block | slick | shade
color="#FFFFFF"
/>
```
### Code Component
```tsx
<code
code={sourceCode}
language="typescript"
showLineNumbers
highlightLines={[1, 5, 10]}
/>
```
### Line Number Component
```tsx
<line-number
code={sourceCode}
language="typescript"
startLine={1}
highlightedLines={[5]}
diagnostics={[
{ line: 3, severity: "error", message: "Syntax error" }
]}
/>
```
### Diff Component
```tsx
<diff
oldCode={originalCode}
newCode={modifiedCode}
language="typescript"
mode="unified" // unified | split
syncScroll // Sync scroll between split view panes
showLineNumbers
/>
```
## Type Exports
```tsx
import type {
// Component props
TextProps,
BoxProps,
InputProps,
SelectProps,
// Hook types
KeyEvent,
// From core
CliRenderer,
} from "@opentui/react"
```
@@ -0,0 +1,302 @@
# React Configuration
## Project Setup
### Quick Start
```bash
bunx create-tui@latest -t react my-app
cd my-app && bun install
```
The CLI creates the `my-app` directory for you - it must **not already exist**.
Options: `--no-git` (skip git init), `--no-install` (skip bun install)
### Manual Setup
```bash
mkdir my-tui && cd my-tui
bun init
bun install @opentui/react @opentui/core react
```
## TypeScript Configuration
### tsconfig.json
```json
{
"compilerOptions": {
"lib": ["ESNext", "DOM"],
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"jsx": "react-jsx",
"jsxImportSource": "@opentui/react",
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"types": ["bun-types"]
},
"include": ["src/**/*"]
}
```
**Critical settings:**
- `jsx: "react-jsx"` - Use the new JSX transform
- `jsxImportSource: "@opentui/react"` - Import JSX runtime from OpenTUI
- `module` / `moduleResolution: "NodeNext"` - Recommended for OpenTUI compatibility
### Why DOM lib?
The `DOM` lib is needed for React types. OpenTUI's JSX types extend React's.
## Package Configuration
### package.json
```json
{
"name": "my-tui-app",
"type": "module",
"scripts": {
"start": "bun run src/index.tsx",
"dev": "bun --watch run src/index.tsx",
"test": "bun test",
"build": "bun build src/index.tsx --outdir=dist --target=bun"
},
"dependencies": {
"@opentui/core": "latest",
"@opentui/react": "latest",
"react": ">=19.0.0"
},
"devDependencies": {
"@types/bun": "latest",
"@types/react": ">=19.0.0",
"typescript": "latest"
}
}
```
## Project Structure
Recommended structure:
```
my-tui-app/
├── src/
│ ├── components/
│ │ ├── Header.tsx
│ │ ├── Sidebar.tsx
│ │ └── MainContent.tsx
│ ├── hooks/
│ │ └── useAppState.ts
│ ├── App.tsx
│ └── index.tsx
├── package.json
└── tsconfig.json
```
### Entry Point (src/index.tsx)
```tsx
import { createCliRenderer } from "@opentui/core"
import { createRoot } from "@opentui/react"
import { App } from "./App"
const renderer = await createCliRenderer({
exitOnCtrlC: true,
})
createRoot(renderer).render(<App />)
```
### App Component (src/App.tsx)
```tsx
import { Header } from "./components/Header"
import { Sidebar } from "./components/Sidebar"
import { MainContent } from "./components/MainContent"
export function App() {
return (
<box flexDirection="column" width="100%" height="100%">
<Header />
<box flexDirection="row" flexGrow={1}>
<Sidebar />
<MainContent />
</box>
</box>
)
}
```
## Renderer Configuration
### createCliRenderer Options
```tsx
import { createCliRenderer, ConsolePosition } from "@opentui/core"
const renderer = await createCliRenderer({
// Rendering
targetFPS: 60,
// Behavior
exitOnCtrlC: true, // Set false to handle Ctrl+C yourself
autoFocus: true, // Auto-focus elements on click (default: true)
useMouse: true, // Enable mouse support (default: true)
// Debug console
consoleOptions: {
position: ConsolePosition.BOTTOM,
sizePercent: 30,
startInDebugMode: false,
},
// Cleanup
onDestroy: () => {
// Cleanup code
},
})
```
## Building for Distribution
### Bundling with Bun
```typescript
// build.ts
await Bun.build({
entrypoints: ["./src/index.tsx"],
outdir: "./dist",
target: "bun",
minify: true,
})
```
Run: `bun run build.ts`
### Creating Executables
```typescript
// build.ts
await Bun.build({
entrypoints: ["./src/index.tsx"],
outdir: "./dist",
target: "bun",
compile: {
target: "bun-darwin-arm64", // or bun-linux-x64, etc.
outfile: "my-app",
},
})
```
## Environment Variables
Create `.env` for development:
```env
# Debug settings
OTUI_SHOW_STATS=false
SHOW_CONSOLE=false
# App settings
API_URL=https://api.example.com
```
Bun auto-loads `.env` files. Access via `process.env`:
```tsx
const apiUrl = process.env.API_URL
```
## React DevTools
OpenTUI React supports React DevTools for debugging.
### Setup
1. Install DevTools as a dev dependency (must use version 7):
```bash
bun add react-devtools-core@7 -d
```
2. Run DevTools standalone app:
```bash
npx react-devtools@7
```
3. Start your app with `DEV=true` environment variable:
```bash
DEV=true bun run src/index.tsx
```
**Important**: Auto-connect to DevTools ONLY happens when `DEV=true` is set. Without this environment variable, the DevTools connection code is not loaded.
### How It Works
OpenTUI checks for `process.env["DEV"] === "true"` at startup. When true, it dynamically imports `react-devtools-core` and connects to the standalone DevTools app.
## Testing Configuration
### Test Setup
```typescript
// src/test-utils.tsx
import { createTestRenderer } from "@opentui/core/testing"
import { createRoot } from "@opentui/react"
export async function renderForTest(
element: React.ReactElement,
options = { width: 80, height: 24 }
) {
const testSetup = await createTestRenderer(options)
createRoot(testSetup.renderer).render(element)
return testSetup
}
```
### Test Example
```typescript
// src/components/Counter.test.tsx
import { test, expect } from "bun:test"
import { renderForTest } from "../test-utils"
import { Counter } from "./Counter"
test("Counter renders initial value", async () => {
const { snapshot } = await renderForTest(<Counter initialValue={5} />)
expect(snapshot()).toContain("Count: 5")
})
```
## Common Issues
### JSX Types Not Working
Ensure `jsxImportSource` is set:
```json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@opentui/react"
}
}
```
### React Version Mismatch
Ensure React 19+:
```bash
bun install react@19 @types/react@19
```
### Module Resolution Errors
Use `moduleResolution: "bundler"` for Bun compatibility.
@@ -0,0 +1,443 @@
# React Gotchas
## Critical
### Never use `process.exit()` directly
**This is the most common mistake.** Using `process.exit()` leaves the terminal in a broken state (cursor hidden, raw mode, alternate screen).
```tsx
// WRONG - Terminal left in broken state
process.exit(0)
// CORRECT - Use renderer.destroy()
import { useRenderer } from "@opentui/react"
function App() {
const renderer = useRenderer()
const handleExit = () => {
renderer.destroy() // Cleans up and exits properly
}
}
```
`renderer.destroy()` restores the terminal (exits alternate screen, restores cursor, etc.) before exiting.
### Signal Handling
OpenTUI automatically handles cleanup for these signals:
- `SIGINT` (Ctrl+C), `SIGTERM`, `SIGQUIT` - Standard termination
- `SIGHUP` - Terminal closed/hangup
- `SIGBREAK` - Ctrl+Break (Windows)
- `SIGPIPE` - Broken pipe (output closed)
- `SIGBUS`, `SIGFPE` - Hardware errors
This ensures terminal state is restored even on unexpected termination. If you need custom signal handling, use `exitOnCtrlC: false` and handle signals yourself while still calling `renderer.destroy()`.
## JSX Configuration
### Missing jsxImportSource
**Symptom**: JSX elements have wrong types, components don't render
```
// Error: Property 'text' does not exist on type 'JSX.IntrinsicElements'
```
**Fix**: Configure tsconfig.json:
```json
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@opentui/react"
}
}
```
### HTML Elements vs TUI Elements
OpenTUI's JSX elements are **not** HTML elements:
```tsx
// WRONG - These are HTML concepts
<div>Not supported</div>
<button>Not supported</button>
<span>Only works inside <text></span>
// CORRECT - OpenTUI elements
<box>Container</box>
<text>Display text</text>
<text><span>Inline styled</span></text>
```
## Component Issues
### Text Modifiers Outside Text
Text modifiers only work inside `<text>`:
```tsx
// WRONG
<box>
<strong>This won't work</strong>
</box>
// CORRECT
<box>
<text>
<strong>This works</strong>
</text>
</box>
```
### Focus Not Working
Components must be explicitly focused:
```tsx
// WRONG - Won't receive keyboard input
<input placeholder="Type here..." />
// CORRECT
<input placeholder="Type here..." focused />
// Or manage focus state
const [isFocused, setIsFocused] = useState(true)
<input placeholder="Type here..." focused={isFocused} />
```
### Select Not Responding
Select requires focus and proper options format:
```tsx
// WRONG - Missing required properties
<select options={["a", "b", "c"]} />
// CORRECT
<select
options={[
{ name: "Option A", description: "First option", value: "a" },
{ name: "Option B", description: "Second option", value: "b" },
]}
onSelect={(index, option) => {
// Called when Enter is pressed
console.log("Selected:", option.name)
}}
focused
/>
```
### Select Events Confusion
Remember: `onSelect` fires on Enter (selection confirmed), `onChange` fires on navigation:
```tsx
// WRONG - expecting onChange to fire on Enter
<select
options={options}
onChange={(i, opt) => submitForm(opt)} // This fires on arrow keys!
/>
// CORRECT
<select
options={options}
onSelect={(i, opt) => submitForm(opt)} // Enter pressed - submit
onChange={(i, opt) => showPreview(opt)} // Arrow keys - preview
/>
```
## Hook Issues
### useKeyboard Not Firing
Multiple `useKeyboard` hooks can conflict:
```tsx
// Both handlers fire - may cause issues
function App() {
useKeyboard((key) => { /* parent handler */ })
return <ChildWithKeyboard />
}
function ChildWithKeyboard() {
useKeyboard((key) => { /* child handler */ })
return <text>Child</text>
}
```
**Solution**: Use a single keyboard handler or implement event stopping:
```tsx
function App() {
const [handled, setHandled] = useState(false)
useKeyboard((key) => {
if (handled) {
setHandled(false)
return
}
// Handle at app level
})
return <Child onKeyHandled={() => setHandled(true)} />
}
```
### useEffect Cleanup
Always clean up intervals and listeners:
```tsx
// WRONG - Memory leak
useEffect(() => {
setInterval(() => updateData(), 1000)
}, [])
// CORRECT
useEffect(() => {
const interval = setInterval(() => updateData(), 1000)
return () => clearInterval(interval) // Cleanup!
}, [])
```
## Styling Issues
### Colors Not Applying
Check color format:
```tsx
// CORRECT formats
<text fg="#FF0000">Red</text>
<text fg="red">Red</text>
<box backgroundColor="#1a1a2e">Box</box>
// WRONG
<text fg="FF0000">Missing #</text>
<text color="#FF0000">Wrong prop name (use fg)</text>
```
### Layout Not Working
Ensure parent has dimensions:
```tsx
// WRONG - Parent has no height
<box flexDirection="column">
<box flexGrow={1}>Won't grow</box>
</box>
// CORRECT
<box flexDirection="column" height="100%">
<box flexGrow={1}>Will grow</box>
</box>
```
### Percentage Widths Not Working
Parent must have explicit dimensions:
```tsx
// WRONG
<box>
<box width="50%">Won't work</box>
</box>
// CORRECT
<box width="100%">
<box width="50%">Works</box>
</box>
```
## Performance Issues
### Too Many Re-renders
Avoid inline objects/functions in props:
```tsx
// WRONG - New object every render
<box style={{ padding: 2 }}>Content</box>
// BETTER - Use direct props
<box padding={2}>Content</box>
// OR memoize style objects
const style = useMemo(() => ({ padding: 2 }), [])
<box style={style}>Content</box>
```
### Heavy Components
Use React.memo for expensive components:
```tsx
const ExpensiveList = React.memo(function ExpensiveList({
items
}: {
items: Item[]
}) {
return (
<box flexDirection="column">
{items.map(item => (
<text key={item.id}>{item.name}</text>
))}
</box>
)
})
```
### State Updates During Render
Don't update state during render:
```tsx
// WRONG
function Component({ value }: { value: number }) {
const [count, setCount] = useState(0)
// This causes infinite loop!
if (value > 10) {
setCount(value)
}
return <text>{count}</text>
}
// CORRECT
function Component({ value }: { value: number }) {
const [count, setCount] = useState(0)
useEffect(() => {
if (value > 10) {
setCount(value)
}
}, [value])
return <text>{count}</text>
}
```
## Debugging
### Console Not Visible
OpenTUI captures console output. Show the overlay:
```tsx
import { useRenderer } from "@opentui/react"
import { useEffect } from "react"
function App() {
const renderer = useRenderer()
useEffect(() => {
renderer.console.show()
console.log("Now you can see this!")
}, [renderer])
return <box>{/* ... */}</box>
}
```
### Component Not Rendering
Check if component is in the tree:
```tsx
// WRONG - Conditional returns nothing
function MaybeComponent({ show }: { show: boolean }) {
if (!show) return // Returns undefined!
return <text>Visible</text>
}
// CORRECT
function MaybeComponent({ show }: { show: boolean }) {
if (!show) return null // Explicit null
return <text>Visible</text>
}
```
### Events Not Firing
Check event handler names:
```tsx
// WRONG
<box onClick={() => {}}>Click</box> // No onClick in TUI
// CORRECT
<box onMouseDown={() => {}}>Click</box>
<box onMouseUp={() => {}}>Click</box>
```
## Runtime Issues
### Use Bun, Not Node
```bash
# WRONG
node src/index.tsx
npm run start
# CORRECT
bun run src/index.tsx
bun run start
```
### Async Top-level
Bun supports top-level await, but be careful:
```tsx
// index.tsx - This works in Bun
const renderer = await createCliRenderer()
createRoot(renderer).render(<App />)
// If you need to handle errors
try {
const renderer = await createCliRenderer()
createRoot(renderer).render(<App />)
} catch (error) {
console.error("Failed to initialize:", error)
process.exit(1)
}
```
## Common Error Messages
### "Cannot read properties of undefined (reading 'root')"
Renderer not initialized:
```tsx
// WRONG
const renderer = createCliRenderer() // Missing await!
createRoot(renderer).render(<App />)
// CORRECT
const renderer = await createCliRenderer()
createRoot(renderer).render(<App />)
```
### "Invalid hook call"
Hooks called outside component:
```tsx
// WRONG
const dimensions = useTerminalDimensions() // Outside component!
function App() {
return <text>{dimensions.width}</text>
}
// CORRECT
function App() {
const dimensions = useTerminalDimensions()
return <text>{dimensions.width}</text>
}
```
@@ -0,0 +1,501 @@
# React Patterns
## State Management
### Local State with useState
```tsx
import { useState } from "react"
function Counter() {
const [count, setCount] = useState(0)
return (
<box flexDirection="row" gap={2}>
<text>Count: {count}</text>
<box border onMouseDown={() => setCount(c => c - 1)}>
<text>-</text>
</box>
<box border onMouseDown={() => setCount(c => c + 1)}>
<text>+</text>
</box>
</box>
)
}
```
### Complex State with useReducer
```tsx
import { useReducer } from "react"
type State = {
items: string[]
selectedIndex: number
}
type Action =
| { type: "ADD_ITEM"; item: string }
| { type: "REMOVE_ITEM"; index: number }
| { type: "SELECT"; index: number }
function reducer(state: State, action: Action): State {
switch (action.type) {
case "ADD_ITEM":
return { ...state, items: [...state.items, action.item] }
case "REMOVE_ITEM":
return {
...state,
items: state.items.filter((_, i) => i !== action.index),
}
case "SELECT":
return { ...state, selectedIndex: action.index }
}
}
function ItemList() {
const [state, dispatch] = useReducer(reducer, {
items: [],
selectedIndex: 0,
})
// Use state and dispatch...
}
```
### Context for Global State
```tsx
import { createContext, useContext, useState, ReactNode } from "react"
type Theme = "dark" | "light"
const ThemeContext = createContext<{
theme: Theme
setTheme: (theme: Theme) => void
} | null>(null)
function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>("dark")
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
)
}
function useTheme() {
const context = useContext(ThemeContext)
if (!context) throw new Error("useTheme must be used within ThemeProvider")
return context
}
// Usage
function App() {
return (
<ThemeProvider>
<ThemedBox />
</ThemeProvider>
)
}
function ThemedBox() {
const { theme } = useTheme()
return (
<box backgroundColor={theme === "dark" ? "#1a1a2e" : "#f0f0f0"}>
<text fg={theme === "dark" ? "#fff" : "#000"}>
Current theme: {theme}
</text>
</box>
)
}
```
## Focus Management
### Focus State
```tsx
import { useState } from "react"
import { useKeyboard } from "@opentui/react"
function FocusableForm() {
const [focusIndex, setFocusIndex] = useState(0)
const fields = ["name", "email", "message"]
useKeyboard((key) => {
if (key.name === "tab") {
setFocusIndex(i => (i + 1) % fields.length)
}
if (key.shift && key.name === "tab") {
setFocusIndex(i => (i - 1 + fields.length) % fields.length)
}
})
return (
<box flexDirection="column" gap={1}>
{fields.map((field, i) => (
<input
key={field}
placeholder={`Enter ${field}...`}
focused={i === focusIndex}
/>
))}
</box>
)
}
```
### Ref-based Focus
```tsx
import { useRef, useEffect } from "react"
function AutoFocusInput() {
const inputRef = useRef<any>(null)
useEffect(() => {
// Focus on mount
inputRef.current?.focus()
}, [])
return <input ref={inputRef} placeholder="Auto-focused" />
}
```
## Keyboard Navigation
### Global Shortcuts
```tsx
import { useKeyboard, useRenderer } from "@opentui/react"
function App() {
const renderer = useRenderer()
useKeyboard((key) => {
// Quit on Escape or Ctrl+C - use renderer.destroy(), never process.exit()
if (key.name === "escape" || (key.ctrl && key.name === "c")) {
renderer.destroy()
return
}
// Toggle help on ?
if (key.name === "?" || (key.shift && key.name === "/")) {
setShowHelp(h => !h)
}
// Vim-style navigation
if (key.name === "j") moveDown()
if (key.name === "k") moveUp()
})
return <box>{/* ... */}</box>
}
```
### Component-level Shortcuts
```tsx
function Editor() {
const [mode, setMode] = useState<"normal" | "insert">("normal")
useKeyboard((key) => {
if (mode === "normal") {
if (key.name === "i") setMode("insert")
if (key.name === "escape") setMode("normal")
} else {
if (key.name === "escape") setMode("normal")
// Handle text input in insert mode
}
})
return (
<box>
<text>Mode: {mode}</text>
<textarea focused={mode === "insert"} />
</box>
)
}
```
## Form Handling
### Controlled Inputs
```tsx
import { useState } from "react"
function LoginForm() {
const [username, setUsername] = useState("")
const [password, setPassword] = useState("")
const handleSubmit = () => {
console.log("Login:", { username, password })
}
return (
<box flexDirection="column" gap={1} padding={2} border>
<text>Login</text>
<box flexDirection="row" gap={1}>
<text>Username:</text>
<input
value={username}
onChange={setUsername}
width={20}
/>
</box>
<box flexDirection="row" gap={1}>
<text>Password:</text>
<input
value={password}
onChange={setPassword}
width={20}
/>
</box>
<box border onMouseDown={handleSubmit}>
<text>Submit</text>
</box>
</box>
)
}
```
### Form Validation
```tsx
function ValidatedForm() {
const [email, setEmail] = useState("")
const [error, setError] = useState("")
const validateEmail = (value: string) => {
if (!value.includes("@")) {
setError("Invalid email address")
} else {
setError("")
}
setEmail(value)
}
return (
<box flexDirection="column" gap={1}>
<input
value={email}
onChange={validateEmail}
placeholder="Email"
/>
{error && <text fg="red">{error}</text>}
</box>
)
}
```
## Responsive Design
### Terminal-size Responsive
```tsx
import { useTerminalDimensions } from "@opentui/react"
function ResponsiveLayout() {
const { width } = useTerminalDimensions()
// Stack vertically on narrow terminals
const isNarrow = width < 80
return (
<box flexDirection={isNarrow ? "column" : "row"}>
<box flexGrow={isNarrow ? 0 : 1} height={isNarrow ? 10 : "100%"}>
<text>Sidebar</text>
</box>
<box flexGrow={1}>
<text>Main Content</text>
</box>
</box>
)
}
```
### Dynamic Layouts
```tsx
function DynamicGrid({ items }: { items: string[] }) {
const { width } = useTerminalDimensions()
const columns = Math.max(1, Math.floor(width / 20))
return (
<box flexDirection="row" flexWrap="wrap">
{items.map((item, i) => (
<box key={i} width={`${100 / columns}%`} padding={1}>
<text>{item}</text>
</box>
))}
</box>
)
}
```
## Async Data Loading
### Loading States
```tsx
import { useState, useEffect } from "react"
function DataDisplay() {
const [data, setData] = useState<string[] | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
async function load() {
try {
const response = await fetch("https://api.example.com/data")
const json = await response.json()
setData(json.items)
} catch (e) {
setError(e instanceof Error ? e.message : "Unknown error")
} finally {
setLoading(false)
}
}
load()
}, [])
if (loading) {
return <text>Loading...</text>
}
if (error) {
return <text fg="red">Error: {error}</text>
}
return (
<box flexDirection="column">
{data?.map((item, i) => (
<text key={i}>{item}</text>
))}
</box>
)
}
```
## Animation Patterns
### Simple Animations
```tsx
import { useState, useEffect } from "react"
import { useTimeline } from "@opentui/react"
function ProgressBar() {
const [progress, setProgress] = useState(0)
const timeline = useTimeline({ duration: 3000 })
useEffect(() => {
timeline.add(
{ value: 0 },
{
value: 100,
duration: 3000,
ease: "linear",
onUpdate: (anim) => {
setProgress(Math.round(anim.targets[0].value))
},
}
)
}, [])
return (
<box flexDirection="column" gap={1}>
<text>Progress: {progress}%</text>
<box width={50} height={1} backgroundColor="#333">
<box
width={`${progress}%`}
height={1}
backgroundColor="#00ff00"
/>
</box>
</box>
)
}
```
### Interval-based Updates
```tsx
function Clock() {
const [time, setTime] = useState(new Date())
useEffect(() => {
const interval = setInterval(() => {
setTime(new Date())
}, 1000)
return () => clearInterval(interval)
}, [])
return <text>{time.toLocaleTimeString()}</text>
}
```
## Component Composition
### Render Props
```tsx
function Focusable({
children
}: {
children: (focused: boolean) => React.ReactNode
}) {
const [focused, setFocused] = useState(false)
return (
<box
onMouseDown={() => setFocused(true)}
onMouseUp={() => setFocused(false)}
>
{children(focused)}
</box>
)
}
// Usage
<Focusable>
{(focused) => (
<text fg={focused ? "#00ff00" : "#ffffff"}>
{focused ? "Focused!" : "Click me"}
</text>
)}
</Focusable>
```
### Higher-Order Components
```tsx
function withBorder<P extends object>(
Component: React.ComponentType<P>,
borderStyle: string = "single"
) {
return function BorderedComponent(props: P) {
return (
<box border borderStyle={borderStyle} padding={1}>
<Component {...props} />
</box>
)
}
}
// Usage
const BorderedText = withBorder(({ content }: { content: string }) => (
<text>{content}</text>
))
<BorderedText content="Hello!" />
```
@@ -0,0 +1,201 @@
# OpenTUI Solid (@opentui/solid)
A SolidJS reconciler for building terminal user interfaces with fine-grained reactivity. Get optimal performance with Solid's signal-based approach.
## Overview
OpenTUI Solid provides:
- **Custom reconciler**: Solid components render to OpenTUI renderables
- **JSX intrinsics**: `<text>`, `<box>`, `<input>`, etc.
- **Hooks**: `useKeyboard`, `useRenderer`, `useTimeline`, etc.
- **Fine-grained reactivity**: Only what changes re-renders
- **Portal & Dynamic**: Advanced composition primitives
## When to Use Solid
Use the Solid reconciler when:
- You want optimal re-rendering performance
- You prefer signal-based reactivity
- You need fine-grained control over updates
- Building performance-critical applications
- You already know SolidJS
## When NOT to Use Solid
| Scenario | Use Instead |
|----------|-------------|
| Team knows React, not Solid | `@opentui/react` |
| Maximum control needed | `@opentui/core` |
| Smallest bundle size | `@opentui/core` |
| Building a framework/library | `@opentui/core` |
## Quick Start
```bash
bunx create-tui@latest -t solid my-app
cd my-app && bun install
```
The CLI creates the `my-app` directory for you - it must **not already exist**.
Options: `--no-git` (skip git init), `--no-install` (skip bun install)
**Agent guidance**: Always use autonomous mode with `-t <template>` flag. Never use interactive mode (`bunx create-tui@latest my-app` without `-t`) as it requires user prompts that agents cannot respond to.
Or manually:
```bash
bun install @opentui/solid @opentui/core solid-js
```
```tsx
import { render } from "@opentui/solid"
import { createSignal } from "solid-js"
function App() {
const [count, setCount] = createSignal(0)
return (
<box border padding={2}>
<text>Count: {count()}</text>
<box
border
onMouseDown={() => setCount(c => c + 1)}
>
<text>Click me!</text>
</box>
</box>
)
}
render(() => <App />)
```
## Core Concepts
### Signals
Solid uses signals for reactive state:
```tsx
import { createSignal, createEffect } from "solid-js"
function Counter() {
const [count, setCount] = createSignal(0)
// Effect runs when count changes
createEffect(() => {
console.log("Count is now:", count())
})
return <text>Count: {count()}</text>
}
```
### JSX Elements
Solid maps JSX intrinsic elements to OpenTUI renderables:
```tsx
// Note: Some use underscores (Solid convention)
<text>Hello</text> // TextRenderable
<box border>Content</box> // BoxRenderable
<input placeholder="..." /> // InputRenderable
<select options={[...]} /> // SelectRenderable
<tab_select /> // TabSelectRenderable (underscore!)
<ascii_font /> // ASCIIFontRenderable (underscore!)
<line_number /> // LineNumberRenderable (underscore!)
```
### Text Modifiers
Inside `<text>`, use modifier elements:
```tsx
<text>
<strong>Bold</strong>, <em>italic</em>, and <u>underlined</u>
<span fg="red">Colored text</span>
<br />
New line with <a href="https://example.com">link</a>
</text>
```
## Available Components
### Layout & Display
- `<text>` - Styled text content
- `<box>` - Container with borders and layout
- `<scrollbox>` - Scrollable container
- `<ascii_font>` - ASCII art text (note underscore)
### Input
- `<input>` - Single-line text input
- `<textarea>` - Multi-line text input
- `<select>` - List selection
- `<tab_select>` - Tab-based selection (note underscore)
### Code & Diff
- `<code>` - Syntax-highlighted code
- `<line_number>` - Code with line numbers (note underscore)
- `<diff>` - Unified or split diff viewer
### Text Modifiers (inside `<text>`)
- `<span>` - Inline styled text
- `<strong>`, `<b>` - Bold
- `<em>`, `<i>` - Italic
- `<u>` - Underline
- `<br>` - Line break
- `<a>` - Link
## Special Components
### Portal
Render children to a different mount node:
```tsx
import { Portal } from "@opentui/solid"
function Overlay() {
return (
<Portal mount={renderer.root}>
<box position="absolute" left={10} top={5} border>
<text>Overlay content</text>
</box>
</Portal>
)
}
```
### Dynamic
Render components dynamically:
```tsx
import { Dynamic } from "@opentui/solid"
function DynamicInput(props: { multiline: boolean }) {
return (
<Dynamic
component={props.multiline ? "textarea" : "input"}
placeholder="Enter text..."
/>
)
}
```
## In This Reference
- [Configuration](./configuration.md) - Project setup, tsconfig, bunfig, building
- [API](./api.md) - Components, hooks, render function
- [Patterns](./patterns.md) - Signals, stores, control flow, composition
- [Gotchas](./gotchas.md) - Common issues, debugging, limitations
## See Also
- [Core](../core/REFERENCE.md) - Underlying imperative API
- [React](../react/REFERENCE.md) - Alternative declarative approach
- [Components](../components/REFERENCE.md) - Component reference by category
- [Layout](../layout/REFERENCE.md) - Flexbox layout system
- [Keyboard](../keyboard/REFERENCE.md) - Input handling and shortcuts
- [Testing](../testing/REFERENCE.md) - Test renderer and snapshots
@@ -0,0 +1,564 @@
# Solid API Reference
## Rendering
### render(node, rendererOrConfig?)
Renders a Solid component tree into a CLI renderer.
```tsx
import { render } from "@opentui/solid"
// Simple usage - creates renderer automatically
render(() => <App />)
// With config
render(() => <App />, {
exitOnCtrlC: false,
targetFPS: 60,
})
// With existing renderer
import { createCliRenderer } from "@opentui/core"
const renderer = await createCliRenderer()
render(() => <App />, renderer)
```
### testRender(node, options?)
Create a test renderer for snapshots and tests.
```tsx
import { testRender } from "@opentui/solid"
const testSetup = await testRender(() => <App />, {
width: 40,
height: 10,
})
// Access test utilities
testSetup.snapshot() // Get current render
testSetup.renderer // Access renderer
```
### extend(components)
Register custom renderables as JSX intrinsic elements.
```tsx
import { extend } from "@opentui/solid"
import { CustomRenderable } from "./custom"
extend({
custom: CustomRenderable,
})
// Now usable in JSX
<custom prop="value" />
```
### getComponentCatalogue()
Returns the current component catalogue.
```tsx
import { getComponentCatalogue } from "@opentui/solid"
const catalogue = getComponentCatalogue()
console.log(Object.keys(catalogue))
```
## Hooks
### useRenderer()
Access the OpenTUI renderer instance.
```tsx
import { useRenderer } from "@opentui/solid"
import { onMount } from "solid-js"
function App() {
const renderer = useRenderer()
onMount(() => {
console.log(`Terminal: ${renderer.width}x${renderer.height}`)
renderer.console.show()
// Access theme mode (dark/light based on terminal settings)
console.log(`Theme: ${renderer.themeMode}`) // "dark" | "light" | null
})
return <text>Hello</text>
}
// Listen for theme mode changes
function ThemedApp() {
const renderer = useRenderer()
const [theme, setTheme] = createSignal(renderer.themeMode ?? "dark")
onMount(() => {
renderer.on("theme_mode", (mode: "dark" | "light") => setTheme(mode))
})
return (
<box backgroundColor={theme() === "dark" ? "#1a1a2e" : "#ffffff"}>
<text fg={theme() === "dark" ? "#fff" : "#000"}>
Current theme: {theme()}
</text>
</box>
)
}
```
### useKeyboard(handler, options?)
Handle keyboard events.
```tsx
import { useKeyboard, useRenderer } from "@opentui/solid"
function App() {
const renderer = useRenderer()
useKeyboard((key) => {
if (key.name === "escape") {
renderer.destroy() // Never use process.exit() directly!
}
if (key.ctrl && key.name === "s") {
saveDocument()
}
})
return <text>Press ESC to exit</text>
}
// With release events
function GameControls() {
const [pressed, setPressed] = createSignal(new Set<string>())
useKeyboard(
(event) => {
setPressed(keys => {
const newKeys = new Set(keys)
if (event.eventType === "release") {
newKeys.delete(event.name)
} else {
newKeys.add(event.name)
}
return newKeys
})
},
{ release: true }
)
return <text>Pressed: {Array.from(pressed()).join(", ")}</text>
}
```
### usePaste(handler)
Handle paste events. Receives a `PasteEvent` with raw bytes.
```tsx
import { usePaste } from "@opentui/solid"
import { decodePasteBytes } from "@opentui/core"
function PasteHandler() {
usePaste((event) => {
const text = decodePasteBytes(event.bytes)
console.log("Pasted:", text)
})
return <text>Paste something</text>
}
```
### onResize(callback)
Handle terminal resize events.
```tsx
import { onResize } from "@opentui/solid"
function App() {
onResize((width, height) => {
console.log(`Resized to ${width}x${height}`)
})
return <text>Resize the terminal</text>
}
```
### useTerminalDimensions()
Get reactive terminal dimensions.
```tsx
import { useTerminalDimensions } from "@opentui/solid"
function ResponsiveLayout() {
const dimensions = useTerminalDimensions()
return (
<box flexDirection={dimensions().width > 80 ? "row" : "column"}>
<text>Width: {dimensions().width}</text>
<text>Height: {dimensions().height}</text>
</box>
)
}
```
### onFocus(callback) / onBlur(callback)
Handle terminal window focus and blur events. Solid-only hooks.
```tsx
import { onFocus, onBlur } from "@opentui/solid"
function App() {
onFocus(() => {
console.log("Terminal window gained focus")
})
onBlur(() => {
console.log("Terminal window lost focus")
})
return <text>Focus/blur tracking</text>
}
```
These hooks fire when the terminal emulator window gains or loses operating system focus. The renderer deduplicates events (won't re-emit the same focus state).
### useSelectionHandler(handler)
Handle text selection events. Fires when the user finishes a mouse selection (mouse-up). Solid-only hook - React does not have this.
```tsx
import { useSelectionHandler } from "@opentui/solid"
import type { Selection } from "@opentui/core"
function SelectableText() {
const [selected, setSelected] = createSignal("")
const renderer = useRenderer()
useSelectionHandler((selection: Selection) => {
const text = selection.getSelectedText()
if (text) {
setSelected(text)
renderer.copyToClipboardOSC52(text)
}
})
return (
<box flexDirection="column">
<text selectable>Select this text with your mouse</text>
<text fg="#888">Selected: {selected()}</text>
</box>
)
}
```
The `Selection` object aggregates selected text from all selectable renderables in the tree. See `keyboard/REFERENCE.md` (selection) for full details on the selection API and traversal model.
### useTimeline(options?)
Create animations with the timeline system.
```tsx
import { useTimeline } from "@opentui/solid"
import { createSignal, onMount } from "solid-js"
function AnimatedBox() {
const [width, setWidth] = createSignal(0)
const timeline = useTimeline({
duration: 2000,
loop: false,
})
onMount(() => {
timeline.add(
{ width: 0 },
{
width: 50,
duration: 2000,
ease: "easeOutQuad",
onUpdate: (anim) => {
setWidth(Math.round(anim.targets[0].width))
},
}
)
})
return <box style={{ width: width(), height: 3, backgroundColor: "#6a5acd" }} />
}
```
## Components
### Text Component
```tsx
<text
content="Hello" // Or use children
fg="#FFFFFF" // Foreground color
bg="#000000" // Background color
selectable={true} // Allow text selection
>
{/* Use nested modifier tags for styling */}
<span fg="red">Red</span>
<strong>Bold</strong>
<em>Italic</em>
<u>Underline</u>
<br />
<a href="https://...">Link</a>
</text>
```
> **Note**: Do NOT use `bold`, `italic`, `underline` as props on `<text>`. Use nested modifier tags like `<strong>`, `<em>`, `<u>` instead.
### Box Component
```tsx
<box
// Borders
border // Enable border
borderStyle="single" // single | double | rounded | bold
borderColor="#FFFFFF"
title="Title"
titleAlignment="center" // left | center | right
// Colors
backgroundColor="#1a1a2e"
// Layout
flexDirection="row"
justifyContent="center"
alignItems="center"
gap={2}
// Spacing
padding={2}
paddingX={2} // Horizontal (left + right)
paddingY={1} // Vertical (top + bottom)
margin={1}
marginX={2} // Horizontal (left + right)
marginY={1} // Vertical (top + bottom)
// Dimensions
width={40}
height={10}
flexGrow={1}
// Focus
focusable // Allow box to receive focus
focused={isFocused()} // Controlled focus state
// Events
onMouseDown={(e) => {}}
onMouseUp={(e) => {}}
>
{children}
</box>
```
### Scrollbox Component
```tsx
<scrollbox
focused // Enable keyboard scrolling
style={{
scrollbarOptions: {
showArrows: true,
trackOptions: {
foregroundColor: "#7aa2f7",
backgroundColor: "#414868",
},
},
}}
>
<For each={items()}>
{(item) => <text>{item}</text>}
</For>
</scrollbox>
```
### Input Component
```tsx
<input
value={value()}
onInput={(newValue) => setValue(newValue)}
placeholder="Enter text..."
focused
width={30}
/>
```
### Textarea Component
```tsx
<textarea
value={text()}
onInput={(newValue) => setText(newValue)}
placeholder="Enter multiple lines..."
focused
width={40}
height={10}
/>
```
### Select Component
```tsx
<select
options={[
{ name: "Option 1", description: "First", value: "1" },
{ name: "Option 2", description: "Second", value: "2" },
]}
onChange={(index, option) => setSelected(option)}
selectedIndex={0}
focused
/>
```
### Tab Select Component (Note: underscore)
```tsx
<tab_select
options={[
{ name: "Home", description: "Dashboard" },
{ name: "Settings", description: "Configuration" },
]}
onChange={(index, option) => setTab(option)}
tabWidth={20}
focused
/>
```
### ASCII Font Component (Note: underscore)
```tsx
<ascii_font
text="TITLE"
font="tiny" // tiny | block | slick | shade
color="#FFFFFF"
/>
```
### Code Component
```tsx
<code
code={sourceCode}
language="typescript"
/>
```
### Line Number Component (Note: underscore)
```tsx
<line_number
code={sourceCode}
language="typescript"
startLine={1}
highlightedLines={[5]}
/>
```
### Diff Component
```tsx
<diff
oldCode={originalCode}
newCode={modifiedCode}
language="typescript"
mode="unified" // unified | split
syncScroll // Sync scroll between split view panes
/>
```
## Control Flow
Solid's control flow components work with OpenTUI:
### For
```tsx
import { For } from "solid-js"
<For each={items()}>
{(item, index) => (
<box key={index()}>
<text>{item.name}</text>
</box>
)}
</For>
```
### Show
```tsx
import { Show } from "solid-js"
<Show when={isVisible()} fallback={<text>Hidden</text>}>
<text>Visible content</text>
</Show>
```
### Switch/Match
```tsx
import { Switch, Match } from "solid-js"
<Switch>
<Match when={status() === "loading"}>
<text>Loading...</text>
</Match>
<Match when={status() === "error"}>
<text fg="red">Error!</text>
</Match>
<Match when={status() === "success"}>
<text fg="green">Success!</text>
</Match>
</Switch>
```
### Index
```tsx
import { Index } from "solid-js"
<Index each={items()}>
{(item, index) => (
<text>{index}: {item().name}</text>
)}
</Index>
```
## Special Components
### Portal
```tsx
import { Portal } from "@opentui/solid"
<Portal mount={targetNode}>
<box>Portal content</box>
</Portal>
```
### Dynamic
```tsx
import { Dynamic } from "@opentui/solid"
<Dynamic
component={isMultiline() ? "textarea" : "input"}
placeholder="Enter text..."
focused
/>
```
@@ -0,0 +1,316 @@
# Solid Configuration
## Project Setup
### Quick Start
```bash
bunx create-tui@latest -t solid my-app
cd my-app && bun install
```
The CLI creates the `my-app` directory for you - it must **not already exist**.
Options: `--no-git` (skip git init), `--no-install` (skip bun install)
### Manual Setup
```bash
mkdir my-tui && cd my-tui
bun init
bun install @opentui/solid @opentui/core solid-js
```
## TypeScript Configuration
### tsconfig.json
```json
{
"compilerOptions": {
"lib": ["ESNext"],
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"jsx": "preserve",
"jsxImportSource": "@opentui/solid",
"strict": true,
"skipLibCheck": true,
"noEmit": true,
"types": ["bun-types"]
},
"include": ["src/**/*"]
}
```
**Critical settings:**
- `jsx: "preserve"` - Let Solid's compiler handle JSX
- `jsxImportSource: "@opentui/solid"` - Import JSX runtime from OpenTUI Solid
- `module` / `moduleResolution: "NodeNext"` - Recommended for OpenTUI compatibility
## Bun Configuration
### bunfig.toml
**Required** for the Solid compiler:
```toml
preload = ["@opentui/solid/preload"]
```
This loads the Solid JSX transform before your code runs.
## Package Configuration
### package.json
```json
{
"name": "my-tui-app",
"type": "module",
"scripts": {
"start": "bun run src/index.tsx",
"dev": "bun --watch run src/index.tsx",
"test": "bun test",
"build": "bun run build.ts"
},
"dependencies": {
"@opentui/core": "latest",
"@opentui/solid": "latest",
"solid-js": "latest"
},
"devDependencies": {
"@types/bun": "latest",
"typescript": "latest"
}
}
```
## Project Structure
Recommended structure:
```
my-tui-app/
├── src/
│ ├── components/
│ │ ├── Header.tsx
│ │ ├── Sidebar.tsx
│ │ └── MainContent.tsx
│ ├── stores/
│ │ └── appStore.ts
│ ├── App.tsx
│ └── index.tsx
├── bunfig.toml # Required!
├── package.json
└── tsconfig.json
```
### Entry Point (src/index.tsx)
```tsx
import { render } from "@opentui/solid"
import { App } from "./App"
render(() => <App />)
```
### App Component (src/App.tsx)
```tsx
import { Header } from "./components/Header"
import { Sidebar } from "./components/Sidebar"
import { MainContent } from "./components/MainContent"
export function App() {
return (
<box flexDirection="column" width="100%" height="100%">
<Header />
<box flexDirection="row" flexGrow={1}>
<Sidebar />
<MainContent />
</box>
</box>
)
}
```
## Renderer Configuration
### render() Options
```tsx
import { render } from "@opentui/solid"
import { ConsolePosition } from "@opentui/core"
render(() => <App />, {
// Rendering
targetFPS: 60,
// Behavior
exitOnCtrlC: true,
autoFocus: true, // Auto-focus elements on click (default: true)
useMouse: true, // Enable mouse support (default: true)
// Debug console
consoleOptions: {
position: ConsolePosition.BOTTOM,
sizePercent: 30,
startInDebugMode: false,
},
// Cleanup
onDestroy: () => {
// Cleanup code
},
})
```
### Using Existing Renderer
```tsx
import { render } from "@opentui/solid"
import { createCliRenderer } from "@opentui/core"
const renderer = await createCliRenderer({
exitOnCtrlC: false,
})
render(() => <App />, renderer)
```
## Building for Distribution
### Build Script (build.ts)
```typescript
import solidPlugin from "@opentui/solid/bun-plugin"
await Bun.build({
entrypoints: ["./src/index.tsx"],
outdir: "./dist",
target: "bun",
minify: true,
plugins: [solidPlugin],
})
console.log("Build complete!")
```
Run: `bun run build.ts`
### Creating Executables
```typescript
import solidPlugin from "@opentui/solid/bun-plugin"
await Bun.build({
entrypoints: ["./src/index.tsx"],
target: "bun",
plugins: [solidPlugin],
compile: {
target: "bun-darwin-arm64", // or bun-linux-x64, etc.
outfile: "my-app",
},
})
```
**Available targets:**
- `bun-darwin-arm64` - macOS Apple Silicon
- `bun-darwin-x64` - macOS Intel
- `bun-linux-x64` - Linux x64
- `bun-linux-arm64` - Linux ARM64
- `bun-windows-x64` - Windows x64
## Environment Variables
Create `.env` for development:
```env
# Debug settings
OTUI_SHOW_STATS=false
SHOW_CONSOLE=false
# App settings
API_URL=https://api.example.com
```
Bun auto-loads `.env` files:
```tsx
const apiUrl = process.env.API_URL
```
## Testing Configuration
### Test Setup
```typescript
// src/test-utils.tsx
import { testRender } from "@opentui/solid"
export async function renderForTest(
Component: () => JSX.Element,
options = { width: 80, height: 24 }
) {
return await testRender(Component, options)
}
```
### Test Example
```typescript
// src/components/Counter.test.tsx
import { test, expect } from "bun:test"
import { renderForTest } from "../test-utils"
import { Counter } from "./Counter"
test("Counter renders initial value", async () => {
const { snapshot } = await renderForTest(() => <Counter initialValue={5} />)
expect(snapshot()).toContain("Count: 5")
})
```
## Common Configuration Issues
### Missing bunfig.toml
**Symptom**: JSX not transformed, syntax errors
**Fix**: Create `bunfig.toml` with preload:
```toml
preload = ["@opentui/solid/preload"]
```
### Wrong JSX Settings
**Symptom**: JSX compiles to React calls
**Fix**: Ensure tsconfig has:
```json
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "@opentui/solid"
}
}
```
### Build Missing Plugin
**Symptom**: Built output has untransformed JSX
**Fix**: Add Solid plugin to build:
```typescript
import solidPlugin from "@opentui/solid/bun-plugin"
await Bun.build({
// ...
plugins: [solidPlugin],
})
```
@@ -0,0 +1,427 @@
# Solid Gotchas
## Critical
### Never use `process.exit()` directly
**This is the most common mistake.** Using `process.exit()` leaves the terminal in a broken state (cursor hidden, raw mode, alternate screen).
```tsx
// WRONG - Terminal left in broken state
process.exit(0)
// CORRECT - Use renderer.destroy()
import { useRenderer } from "@opentui/solid"
function App() {
const renderer = useRenderer()
const handleExit = () => {
renderer.destroy() // Cleans up and exits properly
}
}
```
`renderer.destroy()` restores the terminal (exits alternate screen, restores cursor, etc.) before exiting.
## Configuration Issues
### Missing bunfig.toml
**Symptom**: JSX syntax errors, components not rendering
```
SyntaxError: Unexpected token '<'
```
**Fix**: Create `bunfig.toml` in project root:
```toml
preload = ["@opentui/solid/preload"]
```
### Wrong JSX Settings
**Symptom**: JSX compiles to React, errors about React not found
**Fix**: Ensure tsconfig.json has:
```json
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "@opentui/solid"
}
}
```
### Build Without Plugin
**Symptom**: Built bundle has raw JSX
**Fix**: Add Solid plugin to build:
```typescript
import solidPlugin from "@opentui/solid/bun-plugin"
await Bun.build({
// ...
plugins: [solidPlugin],
})
```
## Reactivity Issues
### Accessing Signals Without Calling
**Symptom**: Value never updates, shows `[Function]`
```tsx
// WRONG - Missing ()
const [count, setCount] = createSignal(0)
<text>Count: {count}</text> // Shows [Function]
// CORRECT
<text>Count: {count()}</text>
```
### Breaking Reactivity with Destructuring
**Symptom**: Props stop being reactive
```tsx
// WRONG - Breaks reactivity
function Component(props: { value: number }) {
const { value } = props // Destructured once, never updates!
return <text>{value}</text>
}
// CORRECT - Keep props reactive
function Component(props: { value: number }) {
return <text>{props.value}</text>
}
// OR use splitProps
function Component(props: { value: number; other: string }) {
const [local, rest] = splitProps(props, ["value"])
return <text>{local.value}</text>
}
```
### Effects Not Running
**Symptom**: createEffect doesn't trigger
```tsx
// WRONG - Signal not accessed in effect
const [count, setCount] = createSignal(0)
createEffect(() => {
console.log("Count changed") // Never runs after initial!
})
// CORRECT - Access the signal
createEffect(() => {
console.log("Count:", count()) // Runs when count changes
})
```
## HTML Entity Decoding
Solid's reconciler automatically decodes HTML entities in JSX text content. This means `&lt;`, `&gt;`, `&amp;`, etc. render as their literal characters:
```tsx
// These render correctly in Solid
<text>Use &lt;box&gt; for containers</text> // Displays: Use <box> for containers
<text>A &amp; B</text> // Displays: A & B
```
This applies to text nodes, the `content` prop, and the `text` prop.
## Component Naming
### Underscore vs Hyphen
Solid uses underscores for multi-word component names:
```tsx
// WRONG - React-style naming
<tab-select /> // Error!
<ascii-font /> // Error!
<line-number /> // Error!
// CORRECT - Solid naming
<tab_select />
<ascii_font />
<line_number />
```
**Component mapping:**
| Concept | React | Solid |
|---------|-------|-------|
| Tab Select | `<tab-select>` | `<tab_select>` |
| ASCII Font | `<ascii-font>` | `<ascii_font>` |
| Line Number | `<line-number>` | `<line_number>` |
## Focus Issues
### Focus Not Working
Components need explicit focus:
```tsx
// WRONG
<input placeholder="Type here..." />
// CORRECT
<input placeholder="Type here..." focused />
```
### Select Not Responding
```tsx
// WRONG
<select options={["a", "b"]} />
// CORRECT
<select
options={[
{ name: "A", description: "Option A", value: "a" },
{ name: "B", description: "Option B", value: "b" },
]}
onSelect={(index, option) => {
// Called when Enter is pressed
console.log("Selected:", option.name)
}}
focused
/>
```
### Select Events Confusion
Remember: `onSelect` fires on Enter (selection confirmed), `onChange` fires on navigation:
```tsx
// WRONG - expecting onChange to fire on Enter
<select
options={options()}
onChange={(i, opt) => submitForm(opt)} // This fires on arrow keys!
/>
// CORRECT
<select
options={options()}
onSelect={(i, opt) => submitForm(opt)} // Enter pressed - submit
onChange={(i, opt) => showPreview(opt)} // Arrow keys - preview
/>
```
## Control Flow Issues
### For vs Index
Use `For` for arrays of objects, `Index` for primitives:
```tsx
// For objects - item is reactive
<For each={objects()}>
{(obj) => <text>{obj.name}</text>}
</For>
// For primitives - use Index, item() is reactive
<Index each={strings()}>
{(str, index) => <text>{index}: {str()}</text>}
</Index>
```
### Missing Fallback
Show requires fallback for proper rendering:
```tsx
// May cause issues
<Show when={data()}>
<Component />
</Show>
// Better - explicit fallback
<Show when={data()} fallback={<text>Loading...</text>}>
<Component />
</Show>
```
## Cleanup Issues
### Forgetting onCleanup
**Symptom**: Memory leaks, multiple intervals running
```tsx
// WRONG - Interval never cleared
function Timer() {
const [time, setTime] = createSignal(0)
setInterval(() => setTime(t => t + 1), 1000)
return <text>{time()}</text>
}
// CORRECT
function Timer() {
const [time, setTime] = createSignal(0)
const interval = setInterval(() => setTime(t => t + 1), 1000)
onCleanup(() => clearInterval(interval))
return <text>{time()}</text>
}
```
### Effect Cleanup
```tsx
createEffect(() => {
const subscription = subscribe(data())
// WRONG - No cleanup
// subscription stays active
// CORRECT
onCleanup(() => subscription.unsubscribe())
})
```
## Store Issues
### Mutating Store Directly
**Symptom**: Changes don't trigger updates
```tsx
const [state, setState] = createStore({ items: [] })
// WRONG - Direct mutation
state.items.push(newItem) // Won't trigger updates!
// CORRECT - Use setState
setState("items", items => [...items, newItem])
```
### Nested Updates
```tsx
const [state, setState] = createStore({
user: { profile: { name: "John" } }
})
// WRONG
state.user.profile.name = "Jane"
// CORRECT
setState("user", "profile", "name", "Jane")
```
## Debugging
### Console Not Visible
OpenTUI captures console output:
```tsx
import { useRenderer } from "@opentui/solid"
import { onMount } from "solid-js"
function App() {
const renderer = useRenderer()
onMount(() => {
renderer.console.show()
console.log("Now visible!")
})
return <box>{/* ... */}</box>
}
```
### Tracking Reactivity
Use `createEffect` to debug:
```tsx
createEffect(() => {
console.log("State:", {
count: count(),
items: items(),
})
})
```
## Runtime Issues
### Use Bun
```bash
# WRONG
node src/index.tsx
npm run start
# CORRECT
bun run src/index.tsx
bun run start
```
### Async render()
The render function is async when creating a renderer:
```tsx
// This is fine - Bun supports top-level await
render(() => <App />)
// If you need the renderer
import { createCliRenderer } from "@opentui/core"
import { render } from "@opentui/solid"
const renderer = await createCliRenderer()
render(() => <App />, renderer)
```
## Common Error Messages
### "Cannot read properties of undefined"
Usually a missing reactive access:
```tsx
// Check if signal is being called
<text>{count()}</text> // Note the ()
// Check if props are being accessed correctly
<text>{props.value}</text> // Not destructured
```
### "JSX element has no corresponding closing tag"
Check component naming:
```tsx
// Wrong
<tab-select></tab-select>
// Correct
<tab_select></tab_select>
```
### "store is not a function"
Stores aren't called like signals:
```tsx
const [store, setStore] = createStore({ count: 0 })
// WRONG
<text>{store().count}</text>
// CORRECT
<text>{store.count}</text>
```
@@ -0,0 +1,560 @@
# Solid Patterns
## Reactive State
### Signals
Basic reactive state with signals:
```tsx
import { createSignal } from "solid-js"
function Counter() {
const [count, setCount] = createSignal(0)
return (
<box flexDirection="row" gap={2}>
<text>Count: {count()}</text>
<box border onMouseDown={() => setCount(c => c - 1)}>
<text>-</text>
</box>
<box border onMouseDown={() => setCount(c => c + 1)}>
<text>+</text>
</box>
</box>
)
}
```
### Derived State
Compute values from signals:
```tsx
import { createSignal, createMemo } from "solid-js"
function PriceCalculator() {
const [quantity, setQuantity] = createSignal(1)
const [price, setPrice] = createSignal(9.99)
// Derived value - only recalculates when dependencies change
const total = createMemo(() => quantity() * price())
const formatted = createMemo(() => `$${total().toFixed(2)}`)
return (
<box flexDirection="column">
<text>Quantity: {quantity()}</text>
<text>Price: ${price()}</text>
<text>Total: {formatted()}</text>
</box>
)
}
```
### Effects
React to state changes:
```tsx
import { createSignal, createEffect, onCleanup } from "solid-js"
function AutoSave() {
const [content, setContent] = createSignal("")
createEffect(() => {
const text = content()
// Debounced save
const timeout = setTimeout(() => {
saveToFile(text)
}, 1000)
// Cleanup on next run or disposal
onCleanup(() => clearTimeout(timeout))
})
return (
<textarea
value={content()}
onInput={setContent}
placeholder="Auto-saves after 1 second..."
/>
)
}
```
## Stores
### createStore for Complex State
```tsx
import { createStore } from "solid-js/store"
interface AppState {
user: { name: string; email: string } | null
items: Array<{ id: number; name: string; done: boolean }>
settings: { theme: "dark" | "light" }
}
function App() {
const [state, setState] = createStore<AppState>({
user: null,
items: [],
settings: { theme: "dark" },
})
const addItem = (name: string) => {
setState("items", items => [
...items,
{ id: Date.now(), name, done: false }
])
}
const toggleItem = (id: number) => {
setState("items", item => item.id === id, "done", done => !done)
}
const setTheme = (theme: "dark" | "light") => {
setState("settings", "theme", theme)
}
return (
<box backgroundColor={state.settings.theme === "dark" ? "#1a1a2e" : "#f0f0f0"}>
<For each={state.items}>
{(item) => (
<text
fg={item.done ? "#888" : "#fff"}
onMouseDown={() => toggleItem(item.id)}
>
{item.done ? "[x]" : "[ ]"} {item.name}
</text>
)}
</For>
</box>
)
}
```
### Store with Context
Share state across components:
```tsx
import { createStore } from "solid-js/store"
import { createContext, useContext, ParentComponent } from "solid-js"
interface Store {
count: number
items: string[]
}
type StoreContextValue = [
Store,
{
increment: () => void
addItem: (item: string) => void
}
]
const StoreContext = createContext<StoreContextValue>()
const StoreProvider: ParentComponent = (props) => {
const [state, setState] = createStore<Store>({
count: 0,
items: [],
})
const actions = {
increment: () => setState("count", c => c + 1),
addItem: (item: string) => setState("items", i => [...i, item]),
}
return (
<StoreContext.Provider value={[state, actions]}>
{props.children}
</StoreContext.Provider>
)
}
function useStore() {
const context = useContext(StoreContext)
if (!context) throw new Error("useStore must be used within StoreProvider")
return context
}
// Usage
function Counter() {
const [state, { increment }] = useStore()
return (
<box onMouseDown={increment}>
<text>Count: {state.count}</text>
</box>
)
}
```
## Control Flow
### Conditional Rendering with Show
```tsx
import { Show, createSignal } from "solid-js"
function ToggleableContent() {
const [visible, setVisible] = createSignal(false)
return (
<box flexDirection="column">
<box border onMouseDown={() => setVisible(v => !v)}>
<text>Toggle</text>
</box>
<Show
when={visible()}
fallback={<text fg="#888">Content is hidden</text>}
>
<text fg="#0f0">Content is visible!</text>
</Show>
</box>
)
}
```
### Lists with For
```tsx
import { For, createSignal } from "solid-js"
function TodoList() {
const [todos, setTodos] = createSignal([
{ id: 1, text: "Learn Solid", done: false },
{ id: 2, text: "Build TUI", done: false },
])
const toggle = (id: number) => {
setTodos(todos =>
todos.map(t =>
t.id === id ? { ...t, done: !t.done } : t
)
)
}
return (
<box flexDirection="column">
<For each={todos()}>
{(todo) => (
<box onMouseDown={() => toggle(todo.id)}>
<text fg={todo.done ? "#888" : "#fff"}>
{todo.done ? "[x]" : "[ ]"} {todo.text}
</text>
</box>
)}
</For>
</box>
)
}
```
### Index for Primitive Arrays
Use `Index` when array items are primitives:
```tsx
import { Index, createSignal } from "solid-js"
function StringList() {
const [items, setItems] = createSignal(["apple", "banana", "cherry"])
return (
<box flexDirection="column">
<Index each={items()}>
{(item, index) => (
<text>{index}: {item()}</text>
)}
</Index>
</box>
)
}
```
### Switch/Match for Multiple Conditions
```tsx
import { Switch, Match, createSignal } from "solid-js"
type Status = "idle" | "loading" | "success" | "error"
function StatusDisplay() {
const [status, setStatus] = createSignal<Status>("idle")
return (
<Switch>
<Match when={status() === "idle"}>
<text>Ready</text>
</Match>
<Match when={status() === "loading"}>
<text fg="#ff0">Loading...</text>
</Match>
<Match when={status() === "success"}>
<text fg="#0f0">Success!</text>
</Match>
<Match when={status() === "error"}>
<text fg="#f00">Error occurred</text>
</Match>
</Switch>
)
}
```
## Focus Management
### Focus State
```tsx
import { createSignal } from "solid-js"
import { useKeyboard } from "@opentui/solid"
function FocusableForm() {
const [focusIndex, setFocusIndex] = createSignal(0)
const fields = ["name", "email", "message"]
useKeyboard((key) => {
if (key.name === "tab") {
setFocusIndex(i => (i + 1) % fields.length)
}
if (key.shift && key.name === "tab") {
setFocusIndex(i => (i - 1 + fields.length) % fields.length)
}
})
return (
<box flexDirection="column" gap={1}>
<Index each={fields}>
{(field, i) => (
<input
placeholder={`Enter ${field()}...`}
focused={i === focusIndex()}
/>
)}
</Index>
</box>
)
}
```
## Keyboard Navigation
### Global Shortcuts
```tsx
import { useKeyboard } from "@opentui/solid"
function App() {
const renderer = useRenderer()
useKeyboard((key) => {
if (key.name === "escape") {
renderer.destroy() // Never use process.exit() directly!
}
if (key.ctrl && key.name === "s") {
save()
}
// Vim-style
if (key.name === "j") moveDown()
if (key.name === "k") moveUp()
})
return <box>{/* ... */}</box>
}
```
## Responsive Design
### Terminal-size Responsive
```tsx
import { useTerminalDimensions } from "@opentui/solid"
function ResponsiveLayout() {
const dims = useTerminalDimensions()
return (
<box flexDirection={dims().width > 80 ? "row" : "column"}>
<box flexGrow={1}>
<text>Panel 1</text>
</box>
<box flexGrow={1}>
<text>Panel 2</text>
</box>
</box>
)
}
```
## Async Data
### Resources
```tsx
import { createResource, Suspense } from "solid-js"
async function fetchData() {
const response = await fetch("https://api.example.com/data")
return response.json()
}
function DataDisplay() {
const [data] = createResource(fetchData)
return (
<Suspense fallback={<text>Loading...</text>}>
<Show when={data()}>
{(items) => (
<For each={items()}>
{(item) => <text>{item.name}</text>}
</For>
)}
</Show>
</Suspense>
)
}
```
### Error Handling
```tsx
import { createResource, Show, ErrorBoundary } from "solid-js"
function SafeDataDisplay() {
const [data] = createResource(fetchData)
return (
<ErrorBoundary fallback={(err) => <text fg="red">Error: {err.message}</text>}>
<Show
when={!data.loading}
fallback={<text>Loading...</text>}
>
<Show
when={!data.error}
fallback={<text fg="red">Failed to load</text>}
>
<For each={data()}>
{(item) => <text>{item.name}</text>}
</For>
</Show>
</Show>
</ErrorBoundary>
)
}
```
## Component Composition
### Props and Children
```tsx
import { ParentComponent, JSX } from "solid-js"
interface PanelProps {
title: string
children: JSX.Element
}
const Panel: ParentComponent<{ title: string }> = (props) => {
return (
<box border padding={1} flexDirection="column">
<text fg="#0ff">{props.title}</text>
<box marginTop={1}>
{props.children}
</box>
</box>
)
}
// Usage
<Panel title="Settings">
<text>Panel content here</text>
</Panel>
```
### Spread Props
```tsx
import { splitProps } from "solid-js"
interface ButtonProps {
label: string
onClick: () => void
// ...rest goes to box
}
function Button(props: ButtonProps) {
const [local, rest] = splitProps(props, ["label", "onClick"])
return (
<box border onMouseDown={local.onClick} {...rest}>
<text>{local.label}</text>
</box>
)
}
```
## Animation
### With Timeline
```tsx
import { createSignal, onMount } from "solid-js"
import { useTimeline } from "@opentui/solid"
function AnimatedProgress() {
const [width, setWidth] = createSignal(0)
const timeline = useTimeline({
duration: 2000,
})
onMount(() => {
timeline.add(
{ value: 0 },
{
value: 50,
duration: 2000,
ease: "easeOutQuad",
onUpdate: (anim) => {
setWidth(Math.round(anim.targets[0].value))
},
}
)
})
return (
<box flexDirection="column" gap={1}>
<text>Progress: {width()}%</text>
<box width={50} height={1} backgroundColor="#333">
<box width={width()} height={1} backgroundColor="#0f0" />
</box>
</box>
)
}
```
### Interval-based
```tsx
import { createSignal, onCleanup } from "solid-js"
function Clock() {
const [time, setTime] = createSignal(new Date())
const interval = setInterval(() => {
setTime(new Date())
}, 1000)
onCleanup(() => clearInterval(interval))
return <text>{time().toLocaleTimeString()}</text>
}
```
@@ -0,0 +1,614 @@
# Testing OpenTUI Applications
How to test terminal user interfaces built with OpenTUI.
## Overview
OpenTUI provides:
- **Test Renderer**: Headless renderer for testing
- **Snapshot Testing**: Verify visual output
- **Interaction Testing**: Simulate user input
## When to Use
Use this reference when you need snapshot tests, interaction testing, or renderer-based regression checks.
## Test Setup
### Bun Test Runner
OpenTUI uses Bun's built-in test runner:
```typescript
import { test, expect, beforeEach, afterEach } from "bun:test"
```
### Test Renderer
Create a test renderer for headless testing:
```typescript
import { createTestRenderer } from "@opentui/core/testing"
const testSetup = await createTestRenderer({
width: 80, // Terminal width
height: 24, // Terminal height
})
```
## Core Testing
### Basic Test
```typescript
import { test, expect } from "bun:test"
import { createTestRenderer } from "@opentui/core/testing"
import { TextRenderable } from "@opentui/core"
test("renders text", async () => {
const testSetup = await createTestRenderer({
width: 40,
height: 10,
})
const text = new TextRenderable(testSetup.renderer, {
id: "greeting",
content: "Hello, World!",
})
testSetup.renderer.root.add(text)
await testSetup.renderOnce()
expect(testSetup.captureCharFrame()).toContain("Hello, World!")
})
```
### Snapshot Testing
```typescript
import { test, expect, afterEach } from "bun:test"
import { createTestRenderer } from "@opentui/core/testing"
import { BoxRenderable, TextRenderable } from "@opentui/core"
let testSetup: Awaited<ReturnType<typeof createTestRenderer>>
afterEach(() => {
if (testSetup) {
testSetup.renderer.destroy()
}
})
test("component matches snapshot", async () => {
testSetup = await createTestRenderer({
width: 40,
height: 10,
})
const box = new BoxRenderable(testSetup.renderer, {
id: "box",
border: true,
width: 20,
height: 5,
})
box.add(new TextRenderable(testSetup.renderer, {
content: "Content",
}))
testSetup.renderer.root.add(box)
await testSetup.renderOnce()
expect(testSetup.captureCharFrame()).toMatchSnapshot()
})
```
## React Testing
### Test Utilities
React provides a built-in `testRender` utility via the `@opentui/react/test-utils` subpath export:
```tsx
import { testRender } from "@opentui/react/test-utils"
```
This utility:
- Creates a headless test renderer
- Sets up the React Act environment automatically
- Handles proper unmounting on destroy
- Returns the standard test setup object
### Basic Component Test
```tsx
import { test, expect } from "bun:test"
import { testRender } from "@opentui/react/test-utils"
function Greeting({ name }: { name: string }) {
return <text>Hello, {name}!</text>
}
test("Greeting renders name", async () => {
const testSetup = await testRender(
<Greeting name="World" />,
{ width: 80, height: 24 }
)
await testSetup.renderOnce()
const frame = testSetup.captureCharFrame()
expect(frame).toContain("Hello, World!")
})
```
### Snapshot Testing
```tsx
import { test, expect, afterEach } from "bun:test"
import { testRender } from "@opentui/react/test-utils"
let testSetup: Awaited<ReturnType<typeof testRender>>
afterEach(() => {
if (testSetup) {
testSetup.renderer.destroy()
}
})
test("component matches snapshot", async () => {
testSetup = await testRender(
<box style={{ width: 20, height: 5, border: true }}>
<text>Content</text>
</box>,
{ width: 25, height: 8 }
)
await testSetup.renderOnce()
const frame = testSetup.captureCharFrame()
expect(frame).toMatchSnapshot()
})
```
### State Testing
```tsx
import { test, expect, afterEach } from "bun:test"
import { useState } from "react"
import { testRender } from "@opentui/react/test-utils"
let testSetup: Awaited<ReturnType<typeof testRender>>
afterEach(() => {
if (testSetup) {
testSetup.renderer.destroy()
}
})
function Counter() {
const [count, setCount] = useState(0)
return (
<box>
<text>Count: {count}</text>
</box>
)
}
test("Counter shows initial value", async () => {
testSetup = await testRender(
<Counter />,
{ width: 20, height: 5 }
)
await testSetup.renderOnce()
const frame = testSetup.captureCharFrame()
expect(frame).toContain("Count: 0")
})
```
### Test Setup/Teardown Pattern
For multiple tests, use beforeEach/afterEach to manage the renderer lifecycle:
```tsx
import { describe, test, expect, beforeEach, afterEach } from "bun:test"
import { testRender } from "@opentui/react/test-utils"
let testSetup: Awaited<ReturnType<typeof testRender>>
describe("MyComponent", () => {
beforeEach(async () => {
if (testSetup) {
testSetup.renderer.destroy()
}
})
afterEach(() => {
if (testSetup) {
testSetup.renderer.destroy()
}
})
test("renders correctly", async () => {
testSetup = await testRender(<MyComponent />, {
width: 40,
height: 10,
})
await testSetup.renderOnce()
const frame = testSetup.captureCharFrame()
expect(frame).toMatchSnapshot()
})
})
```
### Test Setup Return Object
The `testRender` function returns a test setup object with these properties:
| Property | Type | Description |
|----------|------|-------------|
| `renderer` | `Renderer` | The headless renderer instance |
| `renderOnce` | `() => Promise<void>` | Triggers a single render cycle |
| `captureCharFrame` | `() => string` | Captures current output as text |
| `resize` | `(width, height) => void` | Resize the virtual terminal |
## Solid Testing
### Test Utilities
Solid exports `testRender` directly from the main package:
```tsx
import { testRender } from "@opentui/solid"
```
Note: Unlike React, Solid's `testRender` takes a **function component** (not a JSX element).
### Basic Component Test
```tsx
import { test, expect } from "bun:test"
import { testRender } from "@opentui/solid"
function Greeting(props: { name: string }) {
return <text>Hello, {props.name}!</text>
}
test("Greeting renders name", async () => {
const testSetup = await testRender(
() => <Greeting name="World" />,
{ width: 80, height: 24 }
)
await testSetup.renderOnce()
const frame = testSetup.captureCharFrame()
expect(frame).toContain("Hello, World!")
})
```
### Snapshot Testing
```tsx
import { test, expect, afterEach } from "bun:test"
import { testRender } from "@opentui/solid"
let testSetup: Awaited<ReturnType<typeof testRender>>
afterEach(() => {
if (testSetup) {
testSetup.renderer.destroy()
}
})
test("component matches snapshot", async () => {
testSetup = await testRender(
() => (
<box style={{ width: 20, height: 5, border: true }}>
<text>Content</text>
</box>
),
{ width: 25, height: 8 }
)
await testSetup.renderOnce()
const frame = testSetup.captureCharFrame()
expect(frame).toMatchSnapshot()
})
```
## Snapshot Format
Snapshots capture the rendered terminal output as text:
```
┌──────────────────┐
│ Hello, World! │
│ │
└──────────────────┘
```
### Updating Snapshots
```bash
bun test --update-snapshots
```
## Interaction Testing
### Simulating Key Presses
```typescript
import { test, expect, afterEach } from "bun:test"
import { createTestRenderer } from "@opentui/core/testing"
let testSetup: Awaited<ReturnType<typeof createTestRenderer>>
afterEach(() => {
if (testSetup) {
testSetup.renderer.destroy()
}
})
test("responds to keyboard", async () => {
testSetup = await createTestRenderer({
width: 40,
height: 10,
})
// Create component that responds to keys
// ...
// Simulate keypress
testSetup.renderer.keyInput.emit("keypress", {
name: "enter",
sequence: "\r",
ctrl: false,
shift: false,
meta: false,
option: false,
eventType: "press",
repeated: false,
})
// Render after the keypress
await testSetup.renderOnce()
expect(testSetup.captureCharFrame()).toContain("Selected")
})
```
### Testing Focus
```typescript
import { test, expect, afterEach } from "bun:test"
import { createTestRenderer } from "@opentui/core/testing"
import { InputRenderable } from "@opentui/core"
let testSetup: Awaited<ReturnType<typeof createTestRenderer>>
afterEach(() => {
if (testSetup) {
testSetup.renderer.destroy()
}
})
test("input receives focus", async () => {
testSetup = await createTestRenderer({
width: 40,
height: 10,
})
const input = new InputRenderable(testSetup.renderer, {
id: "test-input",
placeholder: "Type here",
})
testSetup.renderer.root.add(input)
input.focus()
expect(input.isFocused()).toBe(true)
})
```
## Test Organization
### File Structure
```
src/
├── components/
│ ├── Button.tsx
│ └── Button.test.tsx
├── hooks/
│ ├── useCounter.ts
│ └── useCounter.test.ts
└── test-utils.tsx
```
### Running Tests
```bash
# Run all tests
bun test
# Run specific test file
bun test src/components/Button.test.tsx
# Run with filter
bun test --filter "Button"
# Watch mode
bun test --watch
```
## Patterns
### Testing Conditional Rendering (React)
```tsx
import { test, expect, afterEach } from "bun:test"
import { testRender } from "@opentui/react/test-utils"
let testSetup: Awaited<ReturnType<typeof testRender>>
afterEach(() => {
if (testSetup) {
testSetup.renderer.destroy()
}
})
test("shows loading state", async () => {
testSetup = await testRender(
<DataLoader loading={true} />,
{ width: 40, height: 10 }
)
await testSetup.renderOnce()
expect(testSetup.captureCharFrame()).toContain("Loading...")
})
test("shows data when loaded", async () => {
testSetup = await testRender(
<DataLoader loading={false} data={["Item 1", "Item 2"]} />,
{ width: 40, height: 10 }
)
await testSetup.renderOnce()
const frame = testSetup.captureCharFrame()
expect(frame).toContain("Item 1")
expect(frame).toContain("Item 2")
})
```
### Testing Lists
```tsx
test("renders all items", async () => {
const items = ["Apple", "Banana", "Cherry"]
testSetup = await testRender(
<ItemList items={items} />,
{ width: 40, height: 10 }
)
await testSetup.renderOnce()
const frame = testSetup.captureCharFrame()
items.forEach(item => {
expect(frame).toContain(item)
})
})
```
### Testing Layouts
```tsx
test("matches layout snapshot", async () => {
testSetup = await testRender(
<AppLayout />,
{ width: 120, height: 40 } // Larger viewport
)
await testSetup.renderOnce()
expect(testSetup.captureCharFrame()).toMatchSnapshot()
})
```
## Debugging Tests
### Print Frame Output
```tsx
import { testRender } from "@opentui/react/test-utils"
test("debug output", async () => {
const testSetup = await testRender(
<MyComponent />,
{ width: 40, height: 10 }
)
await testSetup.renderOnce()
const frame = testSetup.captureCharFrame()
// Print to see what's rendered
console.log(frame)
expect(frame).toContain("expected")
})
```
### Verbose Mode
```bash
bun test --verbose
```
## Gotchas
### Async Rendering
Always call `renderOnce()` after setting up your component to ensure rendering is complete:
```typescript
const testSetup = await testRender(<MyComponent />, { width: 40, height: 10 })
await testSetup.renderOnce() // Required before capturing frame
const frame = testSetup.captureCharFrame()
```
### Test Isolation and Cleanup
Always destroy the renderer after each test to avoid resource leaks:
```typescript
import { afterEach } from "bun:test"
let testSetup: Awaited<ReturnType<typeof testRender>>
afterEach(() => {
if (testSetup) {
testSetup.renderer.destroy()
}
})
test("test 1", async () => {
testSetup = await testRender(<Component1 />, { width: 40, height: 10 })
// ...
})
test("test 2", async () => {
testSetup = await testRender(<Component2 />, { width: 40, height: 10 })
// ...
})
```
### Snapshot Dimensions
Be consistent with test dimensions for stable snapshots:
```typescript
const testSetup = await createTestRenderer({
width: 80, // Standard width
height: 24, // Standard height
})
```
### Running from Package Directory
Run tests from the package directory:
```bash
cd packages/core
bun test
# Not from repo root for package-specific tests
```
## See Also
- [Core API](../core/api.md) - `createTestRenderer` and renderable classes
- [React Configuration](../react/configuration.md) - React test setup
- [Solid Configuration](../solid/configuration.md) - Solid test setup
- [Keyboard](../keyboard/REFERENCE.md) - Simulating key events in tests
+1
View File
@@ -0,0 +1 @@
../../.cline/skills/publish-cli
+1
View File
@@ -0,0 +1 @@
../../.cline/skills/publish-desktop
+1
View File
@@ -0,0 +1 @@
../../.cline/skills/publish-extension
+1
View File
@@ -0,0 +1 @@
../../.cline/skills/tuistory
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Remove the non-functional "Use compact prompt" toggle from LM Studio provider settings
-5
View File
@@ -1,5 +0,0 @@
---
"cline": minor
---
Adds Messages API support to Oracle Code Assist, adding functionality for Claude models
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Fix auto-approve checkboxes freezing after "New Task": clear the task-scoped settings overlay when the task view is cleared or switched, so stale task settings no longer shadow global settings
-5
View File
@@ -1,5 +0,0 @@
---
"cline": patch
---
Add /q command to quit CLI
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
fix: restore workflow support regressions — expand `/workflow.md` slash commands (the legacy filename spelling the autocomplete inserts) and mid-message commands, honor workflow enable/disable toggles during expansion, refresh the slash menu's workflow list on webview launch, and bring back the Workflows management tab in the rules modal (now last in the tab list, with a deprecation notice pointing to Skills)
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Fix hidden plan/act mode-switch and task-resumption prompts reappearing as user messages when a task is reopened from history
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
fix: strip trailing slashes from the OpenAI Compatible base URL when fetching the model list, so `/models` is queried correctly and the model dropdown populates
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
fix: center-align the sign-in verification code box shown after clicking "Sign in to Cline"
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Fix a streaming crash when a chunk has usage data but no `delta` by guarding reasoning field checks in provider handlers. Add regression tests for OpenRouter, Cline, Vercel AI Gateway, and Fireworks handlers to cover usage-only chunks.
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
fix: use correct base URL for Vertex AI global endpoint with Claude models
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Enable Auto Compact by default so long chats automatically compress conversation history instead of failing at the model context limit. It can be disabled in Settings → Features → "Auto Compact".
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Bring back a copy button on turn-final response rows, under a new subtle "Completed" / "Plan" header
-4
View File
@@ -1,4 +0,0 @@
"claude-dev": patch
---
Add missing smoke evaluation npm scripts so documented commands like `npm run eval:smoke:run` work from the repository root.
-5
View File
@@ -1,5 +0,0 @@
---
"cline": patch
---
Add automatic retries (up to 3 attempts) for smoke test CI jobs to reduce flaky failures
-5
View File
@@ -1,5 +0,0 @@
---
"cline": patch
---
fix acp auth check so acp mode can be used with more providers
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Fix /compact UX: clear the chat input as soon as the command is submitted, wrap the compaction divider row at narrow sidebar widths, and update the context-window header even when compacting a small conversation grows the estimated context
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Disable feature tips by default; they can be enabled in Settings → Features → "Feature Tips"
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Show the edited file in a regular editor tab after the diff preview closes, restoring the legacy post-edit behavior
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Hide the "View Changes" button on completion rows until there are actually changes to show, instead of rendering it faded and disabled. Turns that changed nothing, non-git workspaces, and repos without commits no longer show a dead button with a misleading tooltip.
-5
View File
@@ -1,5 +0,0 @@
---
"claude-dev": patch
---
Update SambaNova Provider models list and add temperature for models
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
Show the user's message in chat immediately when sending to a task opened from history, instead of only a thinking indicator until the session resume finishes
+2 -2
View File
@@ -41,11 +41,11 @@ fi
# Install project dependencies
echo "Installing dependencies..."
npm run install:all
bun run install:all
# Generate gRPC/protobuf types (required for TypeScript)
echo "Generating proto types..."
npm run protos
bun run protos
echo ""
echo "Session setup complete!"
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/cline-sdk
+1
View File
@@ -0,0 +1 @@
../../.agents/skills/opentui
+1
View File
@@ -0,0 +1 @@
../../.cline/skills/publish-cli
+1
View File
@@ -0,0 +1 @@
../../.cline/skills/publish-desktop
+1
View File
@@ -0,0 +1 @@
../../.cline/skills/publish-extension
+1
View File
@@ -0,0 +1 @@
../../.cline/skills/tuistory
+266
View File
@@ -0,0 +1,266 @@
---
name: publish-cli
description: Use when preparing, tagging, and publishing an apps/cli npm release. Guides changelog drafting, apps/cli/package.json version bumps, cli-vX.Y.Z tags, local npm publishing, and the publish-cli GitHub workflow.
---
# CLI Release
Use this skill when the user asks to release the CLI, publish `cline`, bump the CLI version, draft release notes, create a `cli-vX.Y.Z` tag, or trigger the CLI publish workflow.
The CLI is npm-only. Do not add alternate distribution or signing steps.
> Working directory: run every command below from the repository root. Paths and scripts (e.g. `apps/cli/package.json`, `sdk/packages/`, `bun release cli`, `bun run version`) are written relative to the repo root.
The skill should guide the user through one release preparation flow, then offer the publish path options. The two normal publish paths are GitHub Actions and local publishing from an authenticated machine.
## Release contract
- SDK prerequisite: the CLI depends on the SDK via `workspace:*` (`@cline/core`, `@cline/shared`, and friends). If the SDK changed since its last release, release the SDK first and wait for it to finish publishing before releasing the CLI. See "Step 0: Release the SDK first if it changed" below.
- Version source: `apps/cli/package.json`.
- Main release tag: `cli-vX.Y.Z`, where `X.Y.Z` matches `apps/cli/package.json`.
- Nightly release version: `X.Y.Z-nightly.TIMESTAMP`.
- Release prep includes approved release notes, a version bump, and an `apps/cli/CHANGELOG.md` update.
- Publish paths:
- GitHub workflow: `.github/workflows/cli-publish.yml`.
- Local publish helper: `bun release cli`.
- npm dist-tags and git tags are separate. `--tag latest` and `--tag nightly` are npm registry channels. `cli-vX.Y.Z` is a git tag for source history and GitHub releases.
- The GitHub main release workflow runs from `main`, requires an existing `cli-vX.Y.Z` tag, checks out that tag, and publishes from it.
- The GitHub nightly workflow publishes to npm with the `nightly` dist-tag and does not create a tag.
- The local release helper requires a clean checkout and `cli-vX.Y.Z` to point at `HEAD` locally and on `origin` before publishing.
- Local GitHub release creation requires `gh` to be authenticated with release permissions for the repo.
- Always ask before pushing commits or tags.
- Do not amend commits unless explicitly requested.
## Step 0: Release the SDK first if it changed
Do this before anything else in the Workflow below.
The CLI builds and ships against the SDK source in the monorepo (`workspace:*` for `@cline/core`, `@cline/shared`, and the rest), so a CLI release always contains the latest SDK code whether or not the SDK was released. The build and tests use that source too, not anything from npm. Releasing the SDK alongside the CLI is still worth doing for two reasons:
- Hub freshness. The hub daemon lives in `@cline/core` and stamps a `buildId` that defaults to the `@cline/core` package version (`resolveHubBuildId` in `sdk/packages/core/src/hub/discovery/index.ts`). A running hub is only retired and respawned when that `buildId` changes (`isCompatibleHubRecord` / `retireIncompatibleHub` in `sdk/packages/core/src/hub/daemon/index.ts`). So if the SDK code changed but the version did not, a user who upgrades the CLI keeps talking to their already-running hub, which is still executing the old SDK code. Bumping the SDK version makes the new CLI's `buildId` differ, so the stale hub is detected as incompatible and respawned with the fresh code.
- Release hygiene. We want regular SDK releases; cutting one whenever we cut a CLI release keeps the published SDK in step with what the CLI ships.
So when the SDK has changed, release it first (which bumps the `@cline/core` version), then cut the CLI release on top of that bump. Leave the CLI's SDK dependency as `workspace:*` — the fix is to release the SDK, not to pin the CLI.
1. Check for unreleased SDK changes.
```sh
git fetch origin --tags
git tag --list 'sdk/sdk/v*' 'sdk-v*' --sort=-v:refname | head -1
git log <last-sdk-tag>..origin/main --oneline --no-merges -- sdk/packages
```
`sdk/<pkg>/v*` tags are created by the `sdk-publish.yml` workflow; `sdk-v*` tags are created by the local `bun release sdk` helper. Use whichever is newest as the baseline.
If `git log` prints no commits, the SDK is already up to date. Skip the rest of Step 0 and continue with the Workflow below.
If it prints commits, sanity-check the diff (ignore entries that are only the previous version-bump commit's lockfile or generated files), then release the SDK.
2. Decide the SDK version bump.
All SDK packages share one version, read from `sdk/packages/llms/package.json`. Ask whether this is patch, minor, major, or an explicit version. Patch is the default. Do not guess if the user has not made it clear.
3. Draft the SDK release notes and update the changelog.
Draft user-facing notes from the SDK commits found in step 1, translating commit messages into user-facing language (same approach as the CLI release notes below). Prepend a new `## <version>` section with those notes to the top of `sdk/CHANGELOG.md`, using the header format `## <version>` with no date — the same flat, newest-on-top format as `apps/cli/CHANGELOG.md`. This is the SDK changelog (all SDK packages share one version) and it is maintained by hand; the `sdk-publish.yml` workflow does not read it.
4. Bump versions and regenerate.
```sh
bun run version <version>
```
This bumps every SDK `package.json` to the new version, regenerates the lockfile and the generated model catalog, formats, and builds. Review the result.
5. Commit and push the bump to `main`.
The `sdk-publish.yml` workflow publishes the version that is committed on `main` and tags that commit, so the bump must land on `main` before the workflow runs.
```sh
git add -A
git commit -m "chore(sdk): release v<version>"
```
Ask before pushing:
```sh
git push origin HEAD
```
6. Trigger the SDK publish workflow on the `latest` channel.
```sh
gh workflow run sdk-publish.yml -f channel=latest -f confirm_publish=publish
gh run list --workflow=sdk-publish.yml --limit=1 --json databaseId,url,status,createdAt --jq '.[0]'
```
The workflow runs the SDK tests, publishes `@cline/shared`, `@cline/llms`, `@cline/agents`, `@cline/core`, and `@cline/sdk` to npm with the `latest` dist-tag in dependency order, and pushes `sdk/<pkg>/v<version>` git tags.
7. Wait for the SDK workflow to succeed before starting the CLI release.
```sh
gh run watch <run-id> --exit-status
```
Do not start the CLI release until this run has finished successfully. The CLI does not install the SDK from npm, but cutting the CLI release on top of a clean, completed SDK release keeps the two in step: the CLI release commit then sits on top of the `@cline/core` version bump, so the shipped CLI carries the new version that forces a running hub to respawn with the new code, and you are not building a CLI release on top of an SDK release that failed midway.
After the SDK release succeeds, pull `main` so the CLI release is prepared on top of the SDK version bump:
```sh
git checkout main && git pull --ff-only
```
Then continue with the Workflow below.
For a local SDK publish from an authenticated machine instead of the workflow, `bun release sdk <version>` exists, but prefer the `sdk-publish.yml` workflow for normal releases so the CLI release can gate on a single GitHub Actions run.
## Workflow
Complete Step 0 first. Only proceed once the SDK is released (or you confirmed no SDK release was needed).
1. Gather context.
```sh
git status --short --branch
git fetch origin --tags
git tag --list 'cli-v*' --sort=-v:refname | head -10
node -p "require('./apps/cli/package.json').version"
```
Find the latest CLI tag. If there is no `cli-v*` tag, use the first relevant CLI release commit as the baseline and say that the baseline is inferred.
2. Collect release commits.
```sh
git log <last-cli-tag>..HEAD --oneline --no-merges -- apps/cli sdk/packages sdk/scripts .github/workflows/cli-publish.yml
```
The `sdk/packages` commits matter here even though the SDK was released separately in Step 0: the CLI bundles the SDK, so SDK changes ship in this CLI release too. Read those commits and fold anything user-relevant to the CLI into the release notes (provider/model updates, behavior changes, fixes the CLI inherits). Skip SDK changes that are purely internal or have no CLI-visible effect.
3. Draft user-facing release notes.
Include user-facing features, fixes, behavior changes, compatibility changes, and notable install or release changes. Exclude pure refactors, tests, style, chores, and internal file moves unless they matter to users.
Write a flat bullet list. Translate commit messages into user-facing language. If a commit is unclear, read the full commit before summarizing it.
Present the draft and wait for approval before editing files.
4. Decide the version bump.
Ask whether this should be patch, minor, major, or an explicit version. Do not guess if the user has not made it clear.
5. Update release files.
Update `apps/cli/package.json` to the approved version.
Prepend a section to `apps/cli/CHANGELOG.md` for the approved version using the approved release notes. Use the header format `## X.Y.Z` with no date. The publish workflow extracts the top section of the changelog by matching `^## [0-9]` and pastes it verbatim into the GitHub release body and the Slack release announcement, so the section content is the release notes that get shipped.
6. Verify before committing.
Run focused checks first:
```sh
bun -F @cline/cli typecheck
bun -F @cline/cli test:unit
```
For higher confidence, run:
```sh
bun run types
bun --cwd apps/cli run build:platforms:single
```
If the user wants full release confidence before tagging, run:
```sh
bun run test
bun --cwd apps/cli run build:platforms
```
Known local-only test failure: `src/commands/distribution-package.test.ts > rejects direct source package packing by default` will fail on machines that have `ignore-scripts=true` in `~/.npmrc` (set by the npm supply-chain hardening guide). Bun reads npm's `ignore-scripts` from `~/.npmrc`, so `bun pm pack --dry-run` skips the source-publish `prepack` guard and exits 0, which the test reads as a failure. CI does not set `ignore-scripts`, so the test passes there. Confirm by running `bun pm pack --dry-run` directly: with `~/.npmrc` in place it exits 0 with no guard output; with `~/.npmrc` moved aside it exits 1 and prints the guard message. This is not a release blocker by itself, but it does mean the local-publish path (`bun release cli`) will also bypass the source-publish guard on this machine; prefer the GitHub Actions publish path on machines with `ignore-scripts=true` set globally, or temporarily unset it (`npm config delete ignore-scripts` or `mv ~/.npmrc ~/.npmrc.bak`) for the duration of a local publish.
7. Commit release changes.
Only after the user approves the notes and version:
```sh
git add apps/cli/package.json apps/cli/CHANGELOG.md
git commit -m "chore(cli): release vX.Y.Z"
```
Ask before pushing the release commit:
```sh
git push origin HEAD
```
For the GitHub main release path, ask before creating and pushing the release tag:
```sh
git tag -a cli-vX.Y.Z -m "CLI vX.Y.Z"
git push origin refs/tags/cli-vX.Y.Z
```
8. Publish.
Ask the user which path to use:
- GitHub main release. Use this after the release commit is on `main` and the matching `cli-vX.Y.Z` tag has been pushed. The workflow publishes to npm from that tag, creates the GitHub release, and posts to Slack.
- Local release. Use this when the user wants to publish from this machine. The local machine must be authenticated to npm and GitHub.
- GitHub nightly release.
- Stop after the version commit.
For GitHub main release:
```sh
gh workflow run cli-publish.yml -f publish_target=main -f git_tag=cli-vX.Y.Z -f confirm_publish=publish
gh run list --workflow=cli-publish.yml --limit=1 --json url,status,conclusion,createdAt --jq '.[0]'
```
For GitHub nightly release:
```sh
gh workflow run cli-publish.yml -f publish_target=nightly
```
For forced GitHub nightly release:
```sh
gh workflow run cli-publish.yml -f publish_target=nightly -f force_nightly_publish=true
```
For local publish:
```sh
gh auth status
npm whoami
git tag -a cli-vX.Y.Z -m "CLI vX.Y.Z"
git push origin refs/tags/cli-vX.Y.Z
bun release cli
```
After a successful local publish, ask before running:
```sh
gh release create cli-vX.Y.Z --verify-tag --title "CLI vX.Y.Z" --notes "Paste the approved release notes here."
```
If publishing with another npm dist-tag:
```sh
bun release cli --tag next
```
9. Final response.
Report:
- version
- tag
- changelog file updated
- commit hash
- whether anything was pushed
- publish path selected
- workflow URL or local publish result
- tests and builds run
+177
View File
@@ -0,0 +1,177 @@
---
name: publish-desktop
description: Use when preparing, tagging, and publishing a Cline desktop app (apps/examples/desktop-app) release — stable (desktop-vX.Y.Z from main) or beta (desktop-vX.Y.Z-beta.N from desktop-experimental, shipped as the side-by-side "Cline Beta" app). Guides changelog drafting, version bumps in package.json + tauri.conf.json, tagging, and the desktop-publish GitHub workflow that builds, signs, notarizes, and updates the per-channel auto-update feed.
---
# Desktop App Release
Use this skill when the user asks to release the desktop app, publish the Cline desktop app, cut a desktop beta, bump the desktop version, create a `desktop-vX.Y.Z` (or `desktop-vX.Y.Z-beta.N`) tag, or trigger the desktop publish workflow.
> Working directory: run every command below from the repository root.
Desktop releases are macOS-only today (a single signed + notarized universal DMG that runs natively on both Apple Silicon and Intel) and are built entirely in GitHub Actions — there is no local publish path. Installed apps discover new releases automatically through the Tauri updater, so publishing a release is what ships the update to every existing user **on that channel**.
## Release contract
- Two channels, one workflow (`channel` input on `desktop-publish.yml`):
- **stable** — tag `desktop-vX.Y.Z` (no suffix; the workflow rejects prerelease suffixes on this channel), cut from `main`, feeds the rolling `desktop-latest` release, ships as "Cline".
- **beta** — tag `desktop-vX.Y.Z-beta.N`, cut from `desktop-experimental`, feeds the rolling `desktop-beta` release, ships as "Cline Beta" (separate bundle identifier `bot.cline.app.beta`; installs side by side with stable). Built with the extra `src-tauri/tauri.beta.conf.json` overlay. Process background: `apps/examples/desktop-app/EXPERIMENTAL.md`.
- Version sources (must match each other and the tag): `apps/examples/desktop-app/package.json` and `apps/examples/desktop-app/src-tauri/tauri.conf.json`. (`src-tauri/Cargo.toml` has its own version but `tauri.conf.json` overrides it; no need to touch it.)
- Beta versions are prereleases of the **next** stable: stable `0.0.13` → betas `0.0.14-beta.1`, `-beta.2`, … Once a stable ≥ the beta base ships, the next beta bumps its base (`0.0.15-beta.1`).
- Release prep includes approved release notes, the version bumps, and an `apps/examples/desktop-app/CHANGELOG.md` update — committed on `main` for stable, on `desktop-experimental` for beta.
- Publish path: `.github/workflows/desktop-publish.yml` (workflow_dispatch, requires the tag to exist, point at the checked-out commit, and be reachable from the channel's branch — `origin/main` for stable, `origin/desktop-experimental` for beta).
- **Both channels dispatch from `main`.** This is a security invariant, not a convenience: the run executes `main`'s workflow copy and only the checkout points at the tag, so the signing-secret gates (the `github.ref == main` check and the PublishDesktop environment's main-only deployment-branch policy) hold for beta too. Never add `desktop-experimental` to the PublishDesktop deployment-branch policy.
- The workflow creates the tag's GitHub release (universal DMG + updater artifact + `latest.json`; marked prerelease for beta) and refreshes the channel's rolling feed release, which is the static auto-update feed every installed app on that channel polls. Never delete the `desktop-latest` or `desktop-beta` release or tag.
- The changelog's `## <version>` section (exact-match, not "topmost") is extracted verbatim into the GitHub release body, the Slack announcement, and the updater manifest notes.
- Always ask before pushing commits or tags.
## Workflow
0. Ask which channel this release is for — **stable or beta** — if the user has not said. Everything below branches on it; never guess.
1. Gather context.
```sh
git status --short --branch
git fetch origin --tags
git tag --list 'desktop-v*' --sort=-v:refname | head -10
node -p "require('./apps/examples/desktop-app/package.json').version"
node -p "require('./apps/examples/desktop-app/src-tauri/tauri.conf.json').version"
```
If there is no `desktop-v*` tag yet, this is the first release; use the desktop app's first commit as the baseline and say the baseline is inferred.
For a **beta** release, work on `desktop-experimental` (check out `origin/desktop-experimental`; merge `origin/main` into it first if it is behind — see EXPERIMENTAL.md for the conflict policy) and read the version files from that branch. The last-tag baseline is the newest `desktop-v*` tag of either channel that is an ancestor of the branch.
2. Collect release commits.
```sh
# stable (on main):
git log <last-desktop-tag>..HEAD --oneline --no-merges -- apps/examples/desktop-app sdk/packages .github/workflows/desktop-publish.yml
# beta (on desktop-experimental):
git log <last-desktop-tag>..origin/desktop-experimental --oneline --no-merges -- apps/examples/desktop-app sdk/packages .github/workflows/desktop-publish.yml
```
The sidecar bundles `@cline/core` and friends from the monorepo, so SDK changes ship inside the desktop app too. Fold user-visible SDK changes (providers, models, behavior fixes) into the notes; skip purely internal ones.
3. Draft user-facing release notes.
Flat bullet list, user-facing language. Present the draft and wait for approval before editing files.
4. Decide the version bump.
Stable: ask whether this is patch, minor, major, or an explicit version. Do not guess if the user has not made it clear.
Beta: apply the versioning rule — base = next stable version, increment `N` (`0.0.14-beta.1``0.0.14-beta.2`; after stable `0.0.14` ships, next is `0.0.15-beta.1`). Confirm the computed version with the user.
5. Update release files (on `main` for stable, on `desktop-experimental` for beta).
- `apps/examples/desktop-app/package.json` → new version
- `apps/examples/desktop-app/src-tauri/tauri.conf.json` → same version
- Prepend `## X.Y.Z` (no date; `## X.Y.Z-beta.N` for beta) to `apps/examples/desktop-app/CHANGELOG.md` with the approved notes.
6. Verify before committing.
```sh
bun -F @cline/code typecheck
bun test apps/examples/desktop-app/scripts/generate-update-manifest.test.ts
```
The full desktop bundle can only be built on macOS; the workflow's build job is the real verification. For extra local confidence on a Mac checkout, `bun run package:desktop:mac --allow-unsigned-mac` from the app directory.
7. Commit release changes.
```sh
git add apps/examples/desktop-app/package.json apps/examples/desktop-app/src-tauri/tauri.conf.json apps/examples/desktop-app/CHANGELOG.md
git commit -m "chore(desktop): release vX.Y.Z"
```
Ask before pushing the release commit, then before creating and pushing the tag:
```sh
git push origin HEAD
git tag -a desktop-vX.Y.Z -m "Desktop vX.Y.Z" # beta: desktop-vX.Y.Z-beta.N / "Desktop vX.Y.Z-beta.N"
git push origin refs/tags/desktop-vX.Y.Z
```
8. Publish.
The release commit must be on the channel's branch (`main` for stable, `desktop-experimental` for beta) and the tag pushed first. Dispatch from `main` for **both** channels (see the release contract for why).
```sh
# stable:
gh workflow run desktop-publish.yml --ref main -f git_tag=desktop-vX.Y.Z -f channel=stable -f confirm_publish=publish
# beta:
gh workflow run desktop-publish.yml --ref main -f git_tag=desktop-vX.Y.Z-beta.N -f channel=beta -f confirm_publish=publish
gh run list --workflow=desktop-publish.yml --limit=1 --json url,status,conclusion,createdAt --jq '.[0]'
```
**The run pauses for approval.** `validate` runs immediately, then the `build`
job waits on the `PublishDesktop` environment until a required reviewer approves
it — the run sits in `waiting`, which is expected, not a hang. Approve it in the
run's web UI ("Review deployments"), or:
```sh
gh api repos/cline/cline/actions/runs/<run-id>/pending_deployments \
--method POST -f state=approved -f comment="desktop vX.Y.Z" \
-F 'environment_ids[]=19152605990' # PublishDesktop
```
Nothing after `validate` runs — and no signing key is readable — until then.
The workflow builds one universal macOS bundle (`tauri build --target universal-apple-darwin` lipos the aarch64 + x86_64 Rust binaries; the Bun sidecar is lipo'd by `build-sidecar-bin.ts`; beta adds the `tauri.beta.conf.json` overlay), verifies every Mach-O in the bundle carries both slices and that the compiled binary embeds exactly its own channel's feed URL, signs with the Developer ID certificate, notarizes with the App Store Connect API key, signs the updater artifact with the Tauri updater key, creates the GitHub release (prerelease for beta), refreshes the channel's feed (`desktop-latest/latest.json` or `desktop-beta/latest.json`), and posts to Slack. Notarization typically adds 210 minutes.
If the workflow fails on missing credentials, see "Publish secrets (one-time setup)" below.
9. Verify the update feed after the run succeeds.
```sh
curl -sL https://github.com/cline/cline/releases/download/desktop-latest/latest.json | head -30 # stable
curl -sL https://github.com/cline/cline/releases/download/desktop-beta/latest.json | head -30 # beta
```
The `version` field must be the new release and both `darwin-aarch64` and `darwin-x86_64` entries must point at the same new universal `.app.tar.gz` asset under the release tag (each slice of the fat binary requests its own arch key at runtime, so both keys serve the one artifact). Installed apps on that channel — including older per-arch installs — pick the update up on next launch or within 2 hours.
After a **beta** publish, also confirm the stable feed was not touched: `desktop-latest/latest.json` must still serve the previous stable version. (The workflow guards this fail-closed, but it is cheap to verify and catastrophic to miss — the updater comparator is a plain semver "newer than", so a beta manifest on `desktop-latest` would auto-update every stable install onto the beta.)
10. Final response.
Report: channel, version, tag, changelog updated, commit hash, what was pushed, workflow URL, and the feed verification result.
## Publish secrets (one-time setup)
These live on the **`PublishDesktop` environment**, not at repository level, so
only the `build` job can read them and only after an approval. Set them under
Settings → Environments → PublishDesktop → Environment secrets. The environment
also restricts deployments to `main` and requires a reviewer.
Adding one of these as a *repository* secret is the common mistake. The build
would still succeed — an environment-gated job resolves repository secrets too,
with environment values simply taking precedence — so the credential would sit
repo-wide while everything looked fine. `validate` therefore fails the run if any
of them resolves in a job with no environment. If you hit that, delete the
repository-level copy rather than duplicating it.
If a secret is missing everywhere, the preflight in `build` fails the run naming
the missing entries. The Apple values come from the same Apple Developer account
used for manual signing (see the app README's "macOS signing & notarization"
section for how to obtain them):
| Secret | Value |
| --- | --- |
| `APPLE_CERTIFICATE` | Base64 of the **Developer ID Application** identity exported from Keychain Access as `.p12` (must include the private key): `base64 -i certificate.p12 \| pbcopy` |
| `APPLE_CERTIFICATE_PASSWORD` | The password chosen when exporting the `.p12` |
| `APPLE_SIGNING_IDENTITY` | `Developer ID Application: <Team Name> (<TEAMID>)` — from `security find-identity -v -p codesigning` |
| `APPLE_API_KEY` | App Store Connect API **Key ID** (notarization) |
| `APPLE_API_KEY_CONTENT` | Contents of the `AuthKey_<KEYID>.p8` file |
| `APPLE_API_ISSUER` | App Store Connect **Issuer ID** (UUID from Users and Access → Integrations) |
| `TAURI_SIGNING_PRIVATE_KEY` | Contents of the Tauri updater private key (`tauri signer generate`). If this key is ever lost, shipped apps can no longer verify updates — guard it. |
| `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` | Password for that key |
The Slack + telemetry secrets (`SLACK_RELEASE_BOT_TOKEN`, `TELEMETRY_SERVICE_API_KEY`,
`ERROR_SERVICE_API_KEY`, OTEL settings) are shared with the CLI, SDK, and extension
publish workflows and already configured. **Do not move these into
`PublishDesktop`** — scoping them to this environment empties them in every other
publish workflow, silently, with no error beyond missing telemetry and a failed
Slack post.
+186
View File
@@ -0,0 +1,186 @@
---
name: publish-extension
description: Use when releasing the Cline VS Code extension — stable (currently the combined legacy+next A/B VSIX via ext-vscode-ab-package), nightly (ext-vscode-publish-nightly), or a legacy-branch hotfix (ext-vscode-publish-legacy). Guides version selection, changelog, PostHog rollout-flag coordination, workflow dispatch, environment approvals, tagging, and post-publish verification, plus the eventual cutover to publishing the SDK extension standalone.
---
# VS Code Extension Release
Use this skill when the user asks to release, publish, or ship the VS Code extension — stable, nightly, or a legacy hotfix — or to dial the rollout, or to cut over to the SDK extension permanently.
> Working directory: repo root. All workflows are dispatched from `main` (GitHub requires the workflow file on the default branch; each workflow checks out the refs it actually builds).
## The current era: combined A/B rollout
We are mid-migration from the legacy (npm, pre-SDK) extension to the next (SDK-based, bun) extension. Until the cutover is complete, **the stable and nightly listings ship a combined VSIX**: a small loader + two complete extensions (`next/` built from `main`, `legacy/` built from the `legacy-extension` branch). The loader picks one per window based on the PostHog flag `ext-sdk-bundle-rollout`. Deep-dive docs: `apps/vscode-rollout/README.md` (authoritative) and PR #12253 (design + runbook comments).
Endgame (see "Cutover" at the bottom): once the next bundle is trusted at 100%, stable goes back to a plain build of `main` via `ext-vscode-publish-stable.yml` and all the legacy/rollout machinery is retired.
### The listings and the workflows
| Channel | Marketplace ID | Workflow | Trigger | Version |
|---|---|---|---|---|
| Stable (combined) | `saoudrizwan.claude-dev` | `ext-vscode-ab-package.yml` | dispatch only; `publish` input defaults false | manual input (semver, e.g. `4.1.0`) |
| Nightly (combined) | `saoudrizwan.cline-nightly` | `ext-vscode-publish-nightly.yml` | cron 12:00 UTC + dispatch | auto `<major>.<minor>.<unix-ts>` from main's `apps/vscode/package.json` |
| Legacy hotfix (standalone) | `saoudrizwan.claude-dev` | `ext-vscode-publish-legacy.yml` | dispatch | from `apps/vscode/package.json` on `legacy-extension` |
| Stable standalone (post-cutover) | `saoudrizwan.claude-dev` | `ext-vscode-publish-stable.yml` | dispatch | from `apps/vscode/package.json` on `main` |
All three publish paths gate on tests before publishing: nightly and ab-package run the reusable bun suite (`ext-vscode-test.yml`, tests `main`) — ab-package additionally runs the legacy branch's npm suite — and the legacy workflow inlines the npm suite. Environment gates: stable paths use `publish``Publish` environment (required reviewers approve in the Actions UI); nightly uses `PublishNightly` (branch policy only, no reviewers — a reviewer requirement would block the cron).
## Golden rules (read before any release)
1. **One listing, one version line.** `claude-dev` is published from multiple workflows/branches. Every stable publish must use a version **strictly above the highest version ever published to the listing from any branch** — marketplace versions are monotonic and cannot be unpublished (supersede, never delete). Check what's live first:
```bash
curl -s -X POST "https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery" \
-H "Content-Type: application/json" -H "Accept: application/json;api-version=3.0-preview.1" \
-d '{"filters":[{"criteria":[{"filterType":7,"value":"saoudrizwan.claude-dev"}]}],"flags":16}' \
| python3 -c "import json,sys; v=json.load(sys.stdin)['results'][0]['extensions'][0]['versions'][0]; print(v['version'], v['lastUpdated'])"
```
`ext-vscode-ab-package` also enforces this automatically for `publish=true` runs: a preflight job validates the version format (plain `X.Y.Z`) and hard-fails unless it exceeds the live Marketplace version, and the publish job re-checks right before publishing (the approval wait can last days — a legacy hotfix landing in between is caught). Still run the query yourself when *choosing* the version.
2. **Check the flag BEFORE any stable combined publish.** `ext-sdk-bundle-rollout` is **shared between nightly and stable** — the loader sends only a machine id to `/decide`, no channel property, so there is no per-channel targeting. If the flag is high (nightly dogfooding) and you publish stable, stable users get the next bundle at that same percentage. Verify the effective percentage empirically (no PostHog admin needed — sample `/decide` with random ids using the key inlined in any shipped loader):
```bash
node -e '
const KEY = process.argv[1]; // phc_... extracted from a shipped VSIX loader
(async () => {
let t = 0, n = 200;
for (let i = 0; i < n; i += 20) {
const rs = await Promise.all(Array.from({length: 20}, (_, j) =>
fetch("https://data.cline.bot/decide?v=3", { method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({api_key: KEY, distinct_id: `probe-${i+j}-${Math.random()}`})
}).then(r => r.json())));
for (const r of rs) if ((r.featureFlags||{})["ext-sdk-bundle-rollout"] === true) t++;
}
console.log(`~${(100*t/n).toFixed(1)}% (${t}/${n})`);
})()' "$KEY"
```
Flag changes are made in the PostHog UI (Cline project). **0% is the kill switch** — the flag is two-way; there is no separate killswitch flag. Dialing down demotes machines back to legacy on their next window reload.
3. **Ask before pushing** commits or tags. Environment approvals are the maintainer's to give.
4. **Changelog lives at the repo ROOT** (`CHANGELOG.md`), on the branch being released — not `apps/vscode/CHANGELOG.md` (doesn't exist). The legacy and stable workflows hard-fail unless the first heading is exactly `## [<version>]`.
5. **Stuck concurrency groups**: `ext-vscode-ab-package` groups on the version with `cancel-in-progress: false`. Only `publish=true` runs wait on environment approval (build-only rehearsals run ungated to completion), but a publish run left `waiting` still blocks every later dispatch of the same version — cancel it (`gh run cancel <id>`) before re-dispatching.
## Stable release (combined A/B VSIX) — the current stable path
### Pre-flight
```bash
# 1. What's live, and what version comes next (must exceed it — rule 1)
# 2. Flag percentage (rule 2) — decide where it should be for this release
# 3. Legacy tip = what the non-promoted cohort will run; confirm it's the shipped hotfix line
git fetch origin main legacy-extension
git log --oneline -3 origin/legacy-extension
# 4. Cheap local rehearsal of the most likely build failure: the union manifest
# hard-fails if views/viewsContainers/configuration diverged between branches.
git show origin/main:apps/vscode/package.json > /tmp/next.json
git show origin/legacy-extension:apps/vscode/package.json > /tmp/legacy.json
node apps/vscode-rollout/scripts/gen-manifest.mjs --next /tmp/next.json --legacy /tmp/legacy.json --version <VERSION>
# Expected warnings only: engines union (takes newer) + walkthrough copy drift.
```
Release prep on `main` (PR, not direct push):
- Add `## [<VERSION>]` entry at the top of root `CHANGELOG.md`.
- Bump `apps/vscode/package.json` to `<VERSION>` so the repo reflects the published line. Side effect: nightly versions become `<major>.<minor>.<unix-ts>` of the new base — harmless (separate listing, still monotonic).
### Dispatch
```bash
gh workflow run ext-vscode-ab-package.yml --ref main \
-f version=<VERSION> -f next-ref=main -f publish=true
# (the legacy bundle always builds from the protected legacy-extension branch;
# it is deliberately not an input)
# publish=false builds an installable .vsix artifact without publishing and
# needs NO environment approval — the ungated build job uploads the artifact
# and the run completes.
gh run list --workflow=ext-vscode-ab-package.yml --limit 1
```
Preflight (version format + monotonicity) and both test suites run first, then the ungated `build` job packages and uploads the VSIX; for `publish=true` the `publish` job then **waits for `Publish` environment approval** (Actions → run → "Review deployments"). Both bundles build the exact revisions their test gates ran against (branch names are resolved once — commits landing on either branch mid-run or during the approval wait are not picked up); `publish=true` is additionally refused for any `next-ref` other than `main` (the bun gate only tests main — non-main next-refs are for build-only artifact rehearsals). Check what a run is waiting on:
```bash
gh api repos/cline/cline/actions/runs/<run-id>/pending_deployments
```
### Post-publish
1. Verify the marketplace serves the new version (query from rule 1) — expect minutes-to-an-hour of validation lag after "Published" appears in the logs. Also verify Open VSX:
```bash
curl -s "https://open-vsx.org/api/saoudrizwan/claude-dev" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['version'], d['timestamp'])"
```
2. Tag, GitHub Release (with the .vsix attached), and the Slack release-bot post happen **automatically** after a real publish (all `continue-on-error` — the publish itself already succeeded, so bookkeeping failures leave the run green). Verify they landed; the known failure is the tag push when the built commit touches `.github/workflows/**` (default token cannot create such refs — no grantable permission fixes it). Manual fallback:
```bash
git tag v<VERSION> <main-sha-built> # ask before pushing
git push origin v<VERSION>
gh release create v<VERSION> --title "v<VERSION>" --notes "<changelog section>" <path-to.vsix>
```
A real publish also **hard-fails early** if root `CHANGELOG.md` on the built main revision doesn't start with `## [<VERSION>]` — the release prep PR must be merged before dispatching.
3. Thorough artifact check (`gh run download <run-id>`): union `package.json` is `saoudrizwan.claude-dev@<VERSION>`, `next/package.json` and `legacy/package.json` carry the SAME version, `grep -c 'phc_' extension/extension.js` ≥ 1 (loader key inlined), no leftover `process.env.TELEMETRY_SERVICE_API_KEY` / `process.env.CLINE_ROLLOUT_VARIANT` literals in either bundle's dist (leftovers = a build ran without its env and telemetry is silently dead).
4. Monitor: `extension.rollout.bundle_activated` in `otel.otel_logs` filtered to `extension_version = '<VERSION>'` (stable cohort is cleanly separable — nightly versions are timestamps). Watch the next/legacy ratio and the crash-fallback rate; Metabase dashboards 17 (rollout + task error rate) and 19 (error deep dive). `extension.rollout.loader_decision` (incl. `double_failure`) is PostHog-only, not in ClickHouse.
5. Dial the flag per the rollout plan (e.g. 0% at publish → 1% → up), verifying each change with the probe from rule 2. Announce demotions ahead of time — dialing down also demotes nightly dogfooders unless they set `"cline-nightly.rollout.bundleOverride": "next"`.
### Known caveats of this path
- **`engines.vscode` unions upward** (main's floor wins, e.g. `^1.101.0` vs legacy's `^1.84.0`): users on older VS Code are never offered the combined VSIX. Fail-safe during rollout; must be resolved before 100%.
- A red run can still mean a successful publish on paths that tag (see Gotchas).
## Nightly release
Happens automatically (cron 12:00 UTC). Manual cut:
```bash
gh workflow run ext-vscode-publish-nightly.yml --ref main # real publish
gh workflow run ext-vscode-publish-nightly.yml --ref main -f dry-run=true # artifact only
gh run watch <run-id> --exit-status --interval 60
```
No changelog/version prep — the version is computed. Verify with the marketplace query against `saoudrizwan.cline-nightly`.
**Red run ≠ failed publish**: the final tag-push step fails whenever main's HEAD touches `.github/workflows/**` (default token cannot create such refs). If "Published" appears in the logs, the release went out; push the `nightly-main-<UTC ts>-<sha12>` tag manually with user credentials.
## Legacy hotfix release (and emergency full rollback)
For shipping a fix on the `legacy-extension` branch — or as the **structural rollback** from a bad combined stable VSIX: a standalone legacy publish at a higher version supersedes the combined VSIX entirely (loader and all) for every user. (For "next bundle misbehaving" you don't need this — dial the flag to 0% instead.)
```bash
# On legacy-extension: commit the fix, bump apps/vscode/package.json ABOVE the
# highest version ever published to the listing (rule 1 — including combined
# versions, e.g. combined 4.1.0 live -> hotfix is 4.1.1, not 4.0.13),
# add the matching `## [x.y.z]` entry to root CHANGELOG.md, push.
gh workflow run ext-vscode-publish-legacy.yml --ref main \
-f release-type=release
# (the branch is hardcoded to legacy-extension in the workflow; it is
# deliberately not an input)
```
npm test suite runs ungated; the publish job waits on the `Publish` environment. This workflow derives + pushes the `v<version>` tag itself and creates the GitHub release — no manual tagging. Publishes to Marketplace **and** Open VSX. The branch is the npm codebase: use `npm`, never `bun`, and expect the old monolith layout (`apps/vscode/src/core/...`).
## Cutover: retiring the A/B machinery (the endgame)
When the next bundle has held at 100% long enough to trust:
1. **Resolve the engines floor**: decide whether stranding VS Code < main's `engines.vscode` on the last combined version is acceptable, or lower main's floor first.
2. Bump `apps/vscode/package.json` on `main` above everything ever published; root `CHANGELOG.md` entry to match (both are enforced by the workflow).
3. Ship standalone from main: `gh workflow run ext-vscode-publish-stable.yml --ref main` — tests main, tags `v<version>` itself, creates the GitHub release, publishes Marketplace + Open VSX.
4. Watch the same rollout telemetry through the transition — `extension_variant` disappears from events as users leave combined builds, which is itself the adoption signal.
5. Only after the standalone version dominates: retire `legacy-extension` (keep for history), delete `ext-vscode-publish-legacy.yml` and `ext-vscode-ab-package.yml`, convert the nightly workflow back to a plain build of main, remove `apps/vscode-rollout/`, and archive the `ext-sdk-bundle-rollout` flag in PostHog (harmless to machines still on a combined VSIX: absent flag fails safe to... nothing changing until they update, but their loader treats a deleted flag as legacy — leave the flag at 100% until combined-VSIX activations flatline, then archive).
6. Update this skill: delete the combined-era sections and keep the standalone flow.
## Gotchas index
- `inputs.*` are empty strings on `schedule` events — preserve `|| 'default'` fallbacks when editing the nightly workflow.
- `bun run package` in `apps/vscode` does not build `@cline/*` workspace deps — fresh checkouts need `bun run build:sdk` first (workflows handle this).
- Job-level `if:` ref checks in workflow YAML are advisory (a dispatched branch runs its own copy of the file); the enforced boundary is each environment's deployment-branch policy in repo settings.
- Marketplace PATs (`VSCE_PAT`/`OVSX_PAT`) are only mounted into publish steps; neither publish workflow has an untrusted trigger surface.
- Environment-approval runs left waiting don't time out quickly — they sit for days and (for ab-package publish runs) block their version's concurrency group.
- Local forcing for manual testing: `CLINE_BUNDLE_OVERRIDE=next|legacy` env (launch VS Code fresh from a terminal) or the `<prefix>.rollout.bundleOverride` setting + reload; both report as `override` in telemetry so they don't pollute cohort data.
+158
View File
@@ -0,0 +1,158 @@
---
name: publish-ui
description: Prepare, validate, and publish standalone @cline/ui npm releases. Use when bumping the UI package version, publishing latest or next through ui-publish.yml, checking UI release readiness, or completing the one-time npm trusted-publishing bootstrap.
---
# Publish UI
Release `@cline/ui` independently from the Cline SDK runtime packages.
## Release contract
- Version source: `sdk/packages/ui/package.json`.
- Workflow: `.github/workflows/ui-publish.yml`.
- The package keeps `internal: true` only to stay out of the SDK's shared
version/publish scripts. It is still a public npm package because
`private: false` and `publishConfig.access: public` control npm publication.
- `latest` is the production channel. `next` is an opt-in preview channel.
- Use prerelease versions such as `0.2.0-next.0` for `next`; do not publish a
version intended for `latest` under the preview tag because npm versions
cannot be republished.
- There is no UI Git tag, GitHub release, schedule, or Slack announcement.
- The workflow runs only by manual dispatch. Every release attempt runs the UI
quality checks before publishing and requires `confirm_publish=publish` from
`main`.
- The publish job and npm trust relationship use the protected `Publish`
environment.
- Every npm publication needs a new semver version; npm versions are immutable.
- Always ask before pushing commits, triggering the publish workflow, changing
npm trust settings, or running a local publish command.
## Normal release
1. Inspect the branch, current version, npm state, and UI changes.
```sh
git status --short --branch
node -p "require('./sdk/packages/ui/package.json').version"
npm view @cline/ui dist-tags versions --json
git log --oneline --no-merges -- \
sdk/packages/ui apps/examples/desktop-app/webview/components/views/chat \
.github/workflows/ui-publish.yml
```
2. Ask for the npm channel and version together. For `latest`, ask for patch,
minor, major, or an explicit version. For `next`, require an explicit
prerelease version such as `0.2.0-next.0`. Do not guess. Update only
`sdk/packages/ui/package.json` and its workspace version in `bun.lock`. Do
not run the SDK version command.
3. Validate the release candidate.
```sh
bun install --filter @cline/ui --filter @cline/code --frozen-lockfile
bun -F @cline/ui typecheck
bun -F @cline/ui test
bun -F @cline/ui test:package
bun -F @cline/ui build-storybook
bun -F @cline/code test:chat-ui
```
The packed-package test installs the tarball with Bun/React 19 and with
npm/Node/React 18.
Inspect `bun pm pack --dry-run` when the exported file set changed.
4. Commit the version bump separately from feature work. Ask before pushing.
```sh
git add sdk/packages/ui/package.json bun.lock
git commit -m "chore(ui): release vX.Y.Z"
git push origin HEAD
```
5. After the release commit reaches `main`, restate the selected npm tag and ask
for explicit publish approval. Then trigger and watch the standalone
workflow:
```sh
run_url=$(gh workflow run ui-publish.yml --ref main \
-f npm_tag=latest \
-f confirm_publish=publish)
test -n "$run_url"
run_id=${run_url##*/}
gh run watch "$run_id" --exit-status
```
Use `npm_tag=next` only for a deliberate preview. Do not report success until
the workflow succeeds and npm shows the exact version under the selected tag.
```sh
npm view @cline/ui dist-tags versions --json
```
## One-time npm bootstrap
Use this only while `npm view @cline/ui` returns `E404`. npm requires the
package to exist before its GitHub trusted publisher can be configured.
1. Merge the package and `ui-publish.yml` to `main`. Start from a clean,
reviewed `main` checkout. Verify authentication, account 2FA, and write
access to the `@cline` npm organization. The `npm trust` command in step 4
requires npm CLI 11.15 or newer; the automated trusted-publishing workflow
itself enforces npm 11.5.1 or newer.
```sh
npm --version
npm whoami
npm view @cline/ui version
```
If npm is older than 11.15, ask before upgrading with
`npm install -g npm@^11.15.0`.
2. Run the normal release validation in step 3 above. Then build, pack, test,
and inspect the exact initial tarball. Record the absolute archive path
printed by the final command.
```sh
bun -F @cline/ui build
pack_dir=$(mktemp -d)
(cd sdk/packages/ui && bun pm pack --ignore-scripts --destination "$pack_dir" --quiet)
tarball=$(find "$pack_dir" -maxdepth 1 -name '*.tgz' -print -quit)
test -n "$tarball"
bun sdk/packages/ui/scripts/smoke-package.ts "$tarball"
tar -tzf "$tarball"
printf 'Bootstrap archive: %s\n' "$tarball"
```
3. Ask for explicit approval, then publish the initial version publicly under
`latest`:
```sh
npm publish /absolute/path/from-step-2.tgz --access public --tag latest
```
4. Ask separately before configuring the standalone workflow as the trusted
publisher:
```sh
npm trust github @cline/ui \
--repo cline/cline \
--file ui-publish.yml \
--env Publish \
--allow-publish
```
5. Verify both package state and trust. Every later release uses the workflow;
do not add a long-lived npm token.
```sh
npm view @cline/ui dist-tags versions --json
npm trust list @cline/ui
```
## Final report
Report the version and npm tag, release commit, whether anything was pushed,
workflow URL or bootstrap result, npm verification, and tests/builds run. If
the package still returns `E404`, state that bootstrap remains required.
@@ -0,0 +1,4 @@
interface:
display_name: "Publish UI"
short_description: "Prepare and publish the Cline UI package"
default_prompt: "Use $publish-ui to prepare and publish a new @cline/ui npm release."
+107
View File
@@ -0,0 +1,107 @@
---
name: tuistory
description: |
Drive and test terminal apps (especially the Cline CLI TUI in apps/cli) through tuistory — named background PTY sessions that agents can read, wait on, snapshot, screenshot, and type into. Like Playwright/tmux for terminals, with reactive waiting instead of blind `sleep`.
Use this skill when you need to:
- Manually test or reproduce bugs in the interactive Cline TUI (`bun run cli -i`) from a headless environment
- Run a dev server or any long-lived/interactive process in the background without hanging your tool call
- Write or extend Playwright-style e2e tests for the TUI (`bun run test:e2e:tuistory` in apps/cli)
- Capture text snapshots or styled PNG screenshots of a TUI screen as evidence
---
# tuistory
[tuistory](https://github.com/remorses/tuistory) wraps any terminal command in a named background PTY session backed by a Ghostty terminal emulator. Agents interact with the session via short CLI calls that return instantly; humans can `tuistory attach` to the same session to watch or intervene. No real terminal or display (`DISPLAY`) is needed — it works fully headless, which makes it the preferred way for cloud agents to exercise the Cline TUI.
It is installed as a devDependency of `@cline/cli`, so the pinned binary resolves when you run from `apps/cli`:
```bash
cd apps/cli
bunx tuistory --help # source of truth for commands, options, and syntax
```
For full upstream docs: `curl -s https://raw.githubusercontent.com/remorses/tuistory/refs/heads/main/README.md`
## Driving the Cline TUI headlessly
Launch the TUI in an isolated environment so you don't touch real user config (`~/.cline`):
```bash
cd apps/cli
DATA_DIR=$(mktemp -d) && HOME_DIR=$(mktemp -d)
bunx tuistory -s cline --cols 120 --rows 36 \
--env HOME=$HOME_DIR --env CLINE_DATA_DIR=$DATA_DIR \
--env CLINE_DISABLE_CLINE_PASS_NOTICE=1 --env CLINE_TELEMETRY_DISABLED=1 \
-- bun src/index.ts --provider anthropic -m claude-sonnet-4-6 -k test-key
```
The dummy `-k test-key` renders the full chat UI; only an actual agent turn would fail. For recorded LLM turns, use the VCR cassettes described in `apps/cli/src/tests/helpers/env.ts` (`CLINE_VCR=playback` + `CLINE_VCR_CASSETTE`). Real turns need a provider credential (e.g. `ANTHROPIC_API_KEY`, `CLINE_API_KEY`).
Then use an **observe → act → observe** loop:
```bash
# Wait reactively for the chat view — never use sleep
bunx tuistory -s cline wait "What can I do for you?" --timeout 30000
# Act, then always observe the resulting screen state
bunx tuistory -s cline type "/settings"
bunx tuistory -s cline snapshot --trim
bunx tuistory -s cline press enter
bunx tuistory -s cline snapshot --trim
# Styled PNG of the current screen (prints the file path) — good for artifacts
bunx tuistory -s cline screenshot
# Full raw output stream (snapshot shows only the visible screen)
bunx tuistory read -s cline --all
# Tear down a session YOU started (double Ctrl+C exits the TUI cleanly)
bunx tuistory -s cline press ctrl c
bunx tuistory -s cline press ctrl c
bunx tuistory -s cline close
```
## Background processes (instead of tmux)
```bash
bunx tuistory -s my-server -- bun run dev:sidecar # returns immediately
bunx tuistory -s my-server wait "/listening|ready/i" --timeout 30000
bunx tuistory read -s my-server # new output since last read
bunx tuistory -s my-server restart # after code changes
```
## Key rules
- **Options before `--`, command after.** Everything after the first `--` is passed verbatim to the child: `tuistory -s name --cols 150 -- bun src/index.ts` is correct.
- **Snapshot after every action.** TUIs are stateful; dialogs and errors can render over the view you expect. `snapshot` reflects what the user actually sees (occluded text does not count), unlike grepping the raw stream.
- **Wait, never sleep.** `wait "text"` / `wait "/regex/i"` (case-sensitive by default) reacts as fast as the terminal updates; `wait-idle` when you don't know what to expect. Always pass `--timeout`.
- **Keys land instantly.** Unlike sleep-based scripts, a queued second keypress can leak into the next view (e.g. one Enter both accepts a slash completion and submits it).
- **Never close a session you didn't start.** Sessions are shared with humans (`tuistory attach -s name`) and other agents. Default to leaving sessions running; use `read`/`wait`/`snapshot` to inspect without disrupting.
- `--cols`/`--rows` affect TUI layout (assertions are width-sensitive); `--pixel-ratio 2` gives sharper screenshots.
## Writing e2e tests with the library API
`apps/cli/src/cli.tuistory.e2e.test.ts` (run: `bun run test:e2e:tuistory`) is the reference. The programmatic API runs in-process — no daemon:
```ts
import { launchTerminal } from "tuistory";
const session = await launchTerminal({
command: "bun",
args: ["src/index.ts", "--provider", "anthropic", "-k", "test-key"],
cwd: cliRoot,
env: isolatedEnv, // see createCliEnv() in the reference test
cols: 120,
rows: 36,
waitForDataTimeout: 30_000, // CLI cold start compiles a large TS graph
});
await session.waitForText("What can I do for you?", { timeout: 30_000 });
const screen = await session.text({ trimEnd: true }); // emulated screen state
await session.type("/settings");
await session.press("enter");
session.close(); // always close in test teardown
```
Screen-state assertions can check that stale UI is *gone* (`expect(screen).not.toContain(...)`), which stream-grepping harnesses cannot. `session.text({ only: { bold: true } })` filters by style; `session.read()` returns the raw stream since the last read.
+55
View File
@@ -0,0 +1,55 @@
# Bun (tooling) and Node (runtime)
This repo uses **bun** for package management and task running, and **Node** as
the execution runtime. Both are correct at the same time; the distinction is the
source of most confusion, so keep it straight before editing scripts, configs,
docs, or comments.
## Use bun for tooling
- `bun install` (never `npm install` / `npm ci`)
- `bun run <script>` (never `npm run <script>`)
- `bunx <bin>` (never `npx <bin>`)
- `bun <file>.ts` to run a TS entrypoint directly (no `ts-node` / `tsx`)
- `bun esbuild.mjs` to drive the build (esbuild/vite are still the bundlers)
- `bun run --parallel ...` for parallel tasks
The root `bun.lock` is the single lockfile for the whole workspace, including
`apps/vscode`, `webview-ui`, and `testing-platform`. There are no per-package npm
lockfiles.
## Node is the runtime — do NOT rewrite these to bun
The build product runs on Node: the VS Code extension host loads
`dist/extension.js` as CommonJS under Node, and the standalone `cline-core` is a
Node process. The following are Node runtime/ABI references and are correct as-is:
| Reference | Why it is Node |
|-----------|----------------|
| esbuild `platform: "node"` / `target: "node..."` | The bundle targets the Node runtime (extension host, standalone core). |
| `TARGET_NODE_VERSION` (`scripts/package-standalone.mjs`) | Pins the Node ABI of the bundled standalone runtime (matches the JetBrains-packaged Node). |
| `prebuild-install --target=<node version>` | Downloads native `.node` binaries for that Node ABI. |
| `NODE_PATH=... node cline-core.js` | The standalone core is launched by Node, not bun. |
| `node:` import specifiers (e.g. `node:fs`) | Node builtin module scheme; unrelated to tooling. |
| `process.versions.node`, `engines.node`, `@types/node` | Runtime version probe / declared runtime / its types. |
| `ELECTRON_RUN_AS_NODE` | VS Code/Electron runs the extension host as Node. |
When a file legitimately uses both bun and node (e.g. `package-standalone.mjs`
does `bun install` but `prebuild-install --target=<node>`), the `node` token is
the runtime/ABI target, not tooling. If unsure, leave it.
## Tests: bun vs the VS Code host
A test file's runner is decided by its import:
- **`import ... from "bun:test"`** → runs under `bun test` (the node-side unit
suites + the SDK/model-catalog suites). `scripts/run-bun-unit-tests.ts`
discovers these by the `bun:test` import and runs one isolated bun process per
file. `build-tests.js` excludes them from the integration compile so the
`bun:test` builtin never reaches Node.
- **`import ... from "mocha"`** → runs under `@vscode/test-cli` in a real VS Code
extension host (Node). These exercise the live `vscode` API and cannot run
under bun.
So a file imports `bun:test` XOR `mocha`. Don't add `bun:test` to a test that
needs the real extension host.
-33
View File
@@ -1,33 +0,0 @@
# CLI Development
The CLI lives in `cli/` and uses React Ink for terminal UI.
- If needed, look at `cli/src/constants/colors.ts` for re-used terminal colors, e.g. `COLORS.primaryBlue` highlight color (selections, spinners, success states).
- Never use `dimColor` with gray (e.g. `<Text color="gray" dimColor>`) - it's too hard to read. Use `color="gray"` for secondary text and normal foreground (no color) for primary text.
- When thinking about how to handle state or messages from core, look at webview for how it communicates with the vs code extension.
- When updating the webview, consider and suggest to the user to update the CLI TUI since we want to provide a similar experience to our terminal users as we do our vs code extension users.
## Adding New API Providers
When adding a new API provider to the extension, you must also update the CLI:
1. **Update `cli/src/components/ModelPicker.tsx`**: Add the provider to the `providerModels` map so `getDefaultModelId()` returns the correct default model. Import the models and default ID from `@shared/api`:
```typescript
import { newProviderDefaultModelId, newProviderModels } from "@/shared/api"
export const providerModels = {
// ...existing providers
"new-provider": { models: newProviderModels, defaultId: newProviderDefaultModelId },
}
```
2. **Use `applyProviderConfig()` for auth flows**: When implementing OAuth or other auth flows for the provider, use the shared utility at `cli/src/utils/provider-config.ts`:
```typescript
import { applyProviderConfig } from "../utils/provider-config"
// After successful auth:
await applyProviderConfig({ providerId: "new-provider", controller })
```
This handles setting provider, default model, API key mapping, state persistence, and rebuilding the API handler.
3. **Provider-specific auth**: If the provider uses OAuth (like `openai-codex`), add handling in `SettingsPanelContent.tsx`'s `handleProviderSelect` callback. See the existing Codex OAuth flow as a reference.
+129
View File
@@ -0,0 +1,129 @@
# Debug Harness
HTTP-controlled debugger for the VSCode extension at `src/dev/debug-harness/server.ts`.
## Quick start
```bash
# Build extension first if needed (protos + esbuild):
bun run protos && IS_DEV=true bun esbuild.mjs
# Launch (skip-build if already built). Run with node, NOT bun — Playwright's
# Electron launch times out under bun:
node src/dev/debug-harness/server.ts --skip-build --auto-launch
# In another terminal:
curl localhost:19229/api -d '{"method":"status"}'
```
## Data Isolation
The debugee runs with `CLINE_DIR=~/.cline2` by default, separate from your real `~/.cline`.
This prevents the debugee's logout from logging out the debugger, and vice versa.
Override with `--cline-dir /tmp/test-dir`. Check with `status()``clineDir`.
## Browser Capture & OAuth
The debugee runs with `CLINE_CAPTURE_BROWSER=1`, which intercepts `openExternal()` in
`src/utils/env.ts`. URLs are captured instead of opening a real browser:
- Logged to `$CLINE_DIR/data/debug-captured-urls.jsonl`
- POSTed in real-time to `/captured-url` on the harness server
- Queryable via `oauth.captured_urls`
### OAuth API
- **`oauth.captured_urls`** `{clear?}` — URLs the debugee tried to open
- **`oauth.read_stored_token`** — Check auth token presence in secrets.json
- **`oauth.simulate_callback`** `{path, code?, state?, provider?, token?}` — Build vscode:// callback URI
- **`oauth.read_captured_urls_file`** — Read on-disk JSONL of captured URLs
### OAuth testing flow
For **Cline OAuth** (SDK local callback): The SDK starts a local HTTP server, the auth URL
is captured. To complete: open the captured URL in a real browser (it redirects back to the
SDK's callback server), OR extract the callback port and `curl http://127.0.0.1:PORT/callback?code=...`.
For **MCP/Provider OAuth** (vscode:// URI): The redirect goes to a vscode:// URI.
`oauth.simulate_callback` only *builds* the URI — it does not deliver it, and the ESM
extension host can't `require()` the handler. To actually deliver the callback, call the
debug-only hook via `ext.evaluate` (with `awaitPromise: true`):
`globalThis.__clineHandleUri("vscode://saoudrizwan.claude-dev/...?code=...&state=...")`.
It runs the same `SharedUriHandler.handleUri` as VSCode's real URI handler and exists only
when `CLINE_CAPTURE_BROWSER` is set (the harness always sets it; never ships in prod).
For end-to-end MCP OAuth, get a real `code` from the local MCP OAuth test server
(`bun run dev:mcp-oauth-test-server`).
## Navigating Views — Use Commands, Not Clicks
Don't try to find/click small sidebar icons. Use VSCode commands via command palette.
Registered in `src/registry.ts`:
| Command | View |
|---------|------|
| `cline.accountButtonClicked` | Account / sign-in |
| `cline.historyButtonClicked` | Task history |
| `cline.settingsButtonClicked` | Settings |
| `cline.mcpButtonClicked` | MCP servers |
| `cline.plusButtonClicked` | New task (chat) |
| `cline.worktreesButtonClicked` | Worktrees |
```bash
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
```
## Key commands
All via `POST localhost:19229/api` with `{"method":"...", "params":{...}}`:
- **`launch`** / **`shutdown`** — lifecycle
- **`ui.screenshot`** — screenshot to `/tmp/cline-debug/`; returns `{path}`**use `read_file` on the path to examine, do NOT `open` the file** (Preview.app covers the VSCode window)
- **`ui.open_sidebar`** — open the Cline sidebar
- **`ext.set_breakpoint`** `{file, line, condition?}` — breakpoint by source file (sourcemap-resolved)
- **`ext.evaluate`** `{expression, callFrameId?}` — eval in extension host
- **`ext.resume`** / **`ext.step_over`** / **`ext.step_into`** — stepping
- **`ext.call_stack`** — inspect when paused
- **`web.evaluate`** `{expression}` — eval in webview
- **`web.post_message`** `{message}` — send postMessage to extension host via exposed vsCodeApi
- **`wait_for_pause`** `{timeout?}` — block until breakpoint hit
- **`ui.locator`** `{role?, testId?, text?, frame?}` — Playwright locator (auto-retries on stale sidebar frame)
- **`ui.react_input`** `{text, selector?, clear?, submit?}` — set React textarea value via `execCommand('insertText')`; works reliably across multiple tasks
- **`ui.send_message`** `{text, images?, files?, responseType?}` — send chat message bypassing the textarea entirely (via gRPC postMessage)
- **`ui.command_palette`** `{command}` — run VSCode command
## Typical Session
```bash
# 1. Launch
curl localhost:19229/api -d '{"method":"launch","params":{"skipBuild":true}}'
# 2. Open sidebar + dismiss overlays (ALWAYS do this first)
curl localhost:19229/api -d '{"method":"ui.open_sidebar"}'
curl localhost:19229/api -d '{"method":"web.evaluate","params":{"expression":"document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
# 3. Navigate to view
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
# 4. Check captured OAuth URLs if testing auth
curl localhost:19229/api -d '{"method":"oauth.captured_urls"}'
# 5. Verify
curl localhost:19229/api -d '{"method":"ui.screenshot"}'
```
## Caveats
- **⚠️ Dismiss promotional overlays FIRST**: On fresh launches, full-screen promo overlays block the sidebar. **Dismiss immediately after `ui.open_sidebar`**, before any other interaction or screenshot. May need to run twice:
```bash
curl localhost:19229/api -d '{"method": "ui.open_sidebar"}'
curl localhost:19229/api -d '{"method": "web.evaluate", "params": {"expression": "document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
```
- **Screenshots — don't open the file**: `ui.screenshot` and `ui.sidebar_screenshot` save PNGs to `/tmp/cline-debug/` and return the `{path}`. Use `read_file` on that path to examine screenshots. Running `open <path>` launches Preview.app on macOS which covers the VSCode window.
- **Scripts count = 0 after launch**: CDP connects after extension host starts, so scripts parsed during startup aren't tracked. Breakpoints still work via sourcemap resolution.
- **Port 9230**: Extension host inspector. If another VSCode instance uses this port, the harness will fail to connect. Kill other debug instances first.
- **macOS only** for now (Playwright Electron launch behavior).
- **Webview CDP**: `connect_webview` may fail depending on Electron version. `web.evaluate` still works via Playwright's `frame.evaluate()` fallback.
- **Sourcemap paths**: esbuild outputs relative paths like `../src/extension.ts` in the sourcemap. The resolver handles this, but if a file isn't found, use `ext.source_files` to see exact paths.
- **OAuth with fake codes**: Browser capture intercepts the URL but doesn't provide a valid auth code. For real OAuth testing, open the captured URL in a browser. For unit testing, mock the token exchange.
See `src/dev/debug-harness/README.md` for full API reference.
+103 -103
View File
@@ -13,11 +13,57 @@ This file is the secret sauce for working effectively in this codebase. It captu
**What NOT to add:** Stuff you can figure out from reading a few files, obvious patterns, or standard practices. This file should be high-signal, not comprehensive.
## Miscellaneous
- This is a VS Code extension—check `package.json` for available scripts before trying to verify builds (e.g., `npm run compile`, not `npm run build`).
- The whole repo (including `apps/vscode`) uses **bun** for package management and task running. Emit `bun run X` / `bun install` / `bunx <bin>` / `bun file.ts`, never npm/npx. Node remains the *runtime* (VS Code's extension host and the standalone cline-core are Node), so Node-runtime tokens are legitimate and must not be "fixed" to bun — see @.clinerules/bun-and-node.md for the keep-list vs rewrite-list.
- Avoid provider-specific string matching / hardcoded provider branches when fixing provider/config plumbing. Prefer provider metadata, shared catalog/defaults, explicit protocol/client capabilities, or centralized normalization utilities that apply by data shape rather than `providerId === "..."`. If a provider exception seems necessary, stop and explain why instead of adding ad-hoc string matching.
- This is a VS Code extension—check `package.json` for available scripts before trying to verify builds (e.g., `bun run compile`, not `bun run build`).
- When reading a configuration files that users may edit, use `readFileStrippingUtf8Bom`, `readFileSyncStrippingUtf8Bom`, or `stripUtf8Bom` from `@cline/shared/node`. DON'T strip byte order marks of user files handled by tools/passed to models.
- When creating PRs, contributors should not create changelog-entry files. Maintainers handle release versioning and changelog curation during the release process.
- When adding new feature flags, see this PR as a reference https://github.com/cline/cline/pull/7566
- Additional instructions about making requests: @.clinerules/network.md
## Searching the Codebase — Avoiding Build Output
Several directories contain build output or generated code that produces
noisy or unusable results with `search_files` / `grep`:
| Directory | What it is | Why it's a problem |
|-----------|-----------|-------------------|
| `out/` | esbuild bundle output | Mirrors `src/` structure as minified JS — every search gets duplicate hits on single-line files |
| `dist/` | Packaged extension | Entire extension bundled into one minified `extension.js` (~1 long line) |
| `dist-standalone/` | Standalone build output | Same minification issue |
| `src/generated/` | Generated protobuf code | Auto-generated from `proto/`; not the source of truth |
| `src/shared/proto/` | Generated proto type defs | Auto-generated from `proto/`; not the source of truth |
| `node_modules/` | Dependencies | Huge, not project source |
### How to skip build output
**`search_files`** — Point at `src/` (not the project root) and use `file_pattern`:
```
search_files(path="src/core", regex="myFunction", file_pattern="*.ts")
```
The `file_pattern` parameter is the most effective filter — e.g. `"*.ts"`,
`"*.tsx"`, `"*.proto"`.
**`grep` directly** — Exclude build dirs and restrict to source extensions:
```bash
grep -rn "myFunction" src/ --include="*.ts" --exclude-dir={out,dist,node_modules,generated}
```
### When you must search minified files
Sometimes you need to verify what got bundled (e.g., checking if a change
made it into the build). Minified files are typically one long line, so
normal `grep` shows the entire file as context. Use these approaches:
- **`grep -oP`** to extract just the match with limited surrounding context:
```bash
grep -oP '.{0,40}myFunction.{0,40}' dist/extension.js
```
- **`read_file`** on files in `out/src/` — these have source maps and are
more readable than `dist/extension.js` (which is the fully bundled output).
- **Source maps** — `out/src/*.js.map` and `dist/extension.js.map` can be
used to trace minified output back to original source locations.
## gRPC/Protobuf Communication
The extension and webview communicate via gRPC-like protocol over VS Code message passing.
@@ -28,7 +74,7 @@ The extension and webview communicate via gRPC-like protocol over VS Code messag
- Naming: Services `PascalCaseService`, RPCs `camelCase`, Messages `PascalCase`
- For streaming responses, use `stream` keyword (see `subscribeToAuthCallback` in `account.proto`)
**Run `npm run protos`** after any proto changes—generates types in:
**Run `bun run protos`** after any proto changes—generates types in:
- `src/shared/proto/` - Shared type definitions
- `src/generated/grpc-js/` - Service implementations
- `src/generated/nice-grpc/` - Promise-based clients
@@ -48,104 +94,15 @@ The extension and webview communicate via gRPC-like protocol over VS Code messag
- `src/core/controller/task/explainChanges.ts` - Handler implementation
- `webview-ui/src/components/chat/ChatRow.tsx` - UI rendering
## Adding a New API Provider
When adding a new provider (e.g., "openai-codex"), you must update the proto conversion layer in THREE places or the provider will silently reset to Anthropic:
1. `proto/cline/models.proto` - Add to the `ApiProvider` enum (e.g., `OPENAI_CODEX = 40;`)
2. `convertApiProviderToProto()` in `src/shared/proto-conversions/models/api-configuration-conversion.ts` - Add case mapping string to proto enum
3. `convertProtoToApiProvider()` in the same file - Add case mapping proto enum back to string
**Why this matters:** Without these, the provider string hits the `default` case and returns `ANTHROPIC`. The webview, provider list, and handler all work fine, but the state silently resets when it round-trips through proto serialization. No error is thrown.
**Other files to update when adding a provider:**
- `src/shared/api.ts` - Add to `ApiProvider` union type, define models
- `src/shared/providers/providers.json` - Add to provider list for dropdown
- `src/core/api/index.ts` - Register handler in `createHandlerForProvider()`
- `webview-ui/src/components/settings/utils/providerUtils.ts` - Add cases in `getModelsForProvider()` and `normalizeApiConfiguration()`
- `webview-ui/src/utils/validate.ts` - Add validation case
- `webview-ui/src/components/settings/ApiOptions.tsx` - Render provider component
## Responses API Providers (OpenAI Codex, OpenAI Native)
Providers using OpenAI's Responses API require native tool calling. XML tools don't work with the Responses API.
**Symptoms of broken native tool calling:**
- Tools get called multiple times (e.g., `ask_followup_question` asks the same question twice)
- Tool arguments get duplicated or malformed
- The model responds but tools aren't recognized
**Root causes to check:**
1. **Provider missing from `isNextGenModelProvider()`** in `src/utils/model-utils.ts`. The native variant matchers (e.g., `native-gpt-5/config.ts`) call this function. If your provider isn't in the list, the matcher returns false and falls back to XML tools.
2. **Model missing `apiFormat: ApiFormat.OPENAI_RESPONSES`** in its model info (`src/shared/api.ts`). This property signals that the model requires native tool calling. The task runner in `src/core/task/index.ts` checks this and forces `enableNativeToolCalls: true` regardless of user settings.
**When adding a new Responses API provider:**
1. Add provider to `isNextGenModelProvider()` list in `src/utils/model-utils.ts`
2. Set `apiFormat: ApiFormat.OPENAI_RESPONSES` on all models that use the Responses API
3. The variant matcher and task runner will handle the rest automatically
## Adding Tools to System Prompt
This is tricky—multiple prompt variants and configs. **Always search for existing similar tools first and follow their pattern.** Look at the full chain from prompt definition → variant configs → handler → UI before implementing.
1. **Add to `ClineDefaultTool` enum** in `src/shared/tools.ts`
2. **Tool definition** in `src/core/prompts/system-prompt/tools/` (create file like `generate_explanation.ts`)
- Define variants for each `ModelFamily` (generic, next-gen, xs, etc.)
- Export variants array (e.g., `export const my_tool_variants = [GENERIC, NATIVE_NEXT_GEN, XS]`)
- **Fallback behavior**: If a variant isn't defined for a model family, `ClineToolSet.getToolByNameWithFallback()` automatically falls back to GENERIC. So you only need to export `[GENERIC]` unless the tool needs model-specific behavior.
3. **Register in `src/core/prompts/system-prompt/tools/init.ts`** - Import and spread into `allToolVariants`
4. **Add to variant configs** - Each model family has its own config in `src/core/prompts/system-prompt/variants/*/config.ts`. Add your tool's enum to the `.tools()` list:
- `generic/config.ts`, `next-gen/config.ts`, `gpt-5/config.ts`, `native-gpt-5/config.ts`, `native-gpt-5-1/config.ts`, `native-next-gen/config.ts`, `gemini-3/config.ts`, `glm/config.ts`, `hermes/config.ts`, `xs/config.ts`
- **Important**: If you add to a variant's config, make sure the tool spec exports a variant for that ModelFamily (or relies on GENERIC fallback)
5. **Create handler** in `src/core/task/tools/handlers/`
6. **Wire up in `ToolExecutor.ts`** if needed for execution flow
7. **Add to tool parsing** in `src/core/assistant-message/index.ts` if needed
8. **If tool has UI feedback**: add `ClineSay` enum in proto, update `src/shared/ExtensionMessage.ts`, update `src/shared/proto-conversions/cline-message.ts`, update `webview-ui/src/components/chat/ChatRow.tsx`
## Modifying System Prompt
**Read these first:** `src/core/prompts/system-prompt/README.md`, `tools/README.md`, `__tests__/README.md`
System prompt is modular: **components** (reusable sections) + **variants** (model-specific configs) + **templates** (with `{{PLACEHOLDER}}` resolution).
**Key directories:**
- `components/` - Shared sections: `rules.ts`, `capabilities.ts`, `editing_files.ts`, etc.
- `variants/` - Model-specific: `generic/`, `next-gen/`, `xs/`, `gpt-5/`, `gemini-3/`, `hermes/`, `glm/`, etc.
- `templates/` - Template engine and placeholder definitions
**Variant tiers (ask user which to modify):**
- **Next-gen** (Claude 4, GPT-5, Gemini 2.5): `next-gen/`, `native-next-gen/`, `native-gpt-5/`, `native-gpt-5-1/`, `gemini-3/`, `gpt-5/`
- **Standard** (default fallback): `generic/`
- **Local/small models**: `xs/`, `hermes/`, `glm/`
**How overrides work:** Variants can override components via `componentOverrides` in their `config.ts`, or provide a custom template in `template.ts` (e.g., `next-gen/template.ts` exports `rules_template`). If no override, the shared component from `components/` is used.
**Example: Adding a rule to RULES section**
1. Check if variant overrides rules: look for `rules_template` in `variants/*/template.ts` or `componentOverrides.RULES` in `config.ts`
2. If shared: modify `components/rules.ts`
3. If overridden: modify that variant's template
4. XS variant is special—has heavily condensed inline content in `template.ts`
**After any changes, regenerate snapshots:**
```bash
UPDATE_SNAPSHOTS=true npm run test:unit
```
Snapshots live in `__tests__/__snapshots__/`. Tests validate across model families and context variations (browser, MCP, focus chain).
## Modifying Default Slash Commands
Three places need updates:
- `src/core/slash-commands/index.ts` - Command definitions
- `src/core/prompts/commands.ts` - System prompt integration
- `webview-ui/src/utils/slash-commands.ts` - Webview autocomplete
## Adding New Global State Keys
Adding a new key to global state requires updates in multiple places. Missing any step causes silent failures.
Required steps:
1. Type definition in `src/shared/storage/state-keys.ts` - Add to `GlobalState` or `Settings` interface
2. Read from globalState in `src/core/storage/utils/state-helpers.ts`:
- Add `const myKey = context.globalState.get<GlobalStateAndSettings["myKey"]>("myKey")` in `readGlobalStateFromDisk()`
- Add to the return object: `myKey: myKey ?? defaultValue,`
3. StateManager handles read/write via `setGlobalState()`/`getGlobalStateKey()` after initialization
2. Add any default value or transform in `src/shared/storage/state-keys.ts` if the key needs one
3. Read and write the value through `StateManager` (`setGlobalState()` / `getGlobalStateKey()`) after initialization
Common mistake: Adding only the return value without the `context.globalState.get()` call. This compiles but the value is always `undefined` on load.
Persistent state is file-backed through `StateManager`; do not add new runtime reads or writes against VS Code `ExtensionContext` storage. That storage is only a legacy migration source.
Settings plumbing gotcha: if a key is user-toggleable from settings, wire both controller update paths:
- `src/core/controller/state/updateSettings.ts` for webview `updateSetting(...)`
@@ -153,28 +110,26 @@ Settings plumbing gotcha: if a key is user-toggleable from settings, wire both c
Missing one path causes a toggle to appear to change in one surface while the backend state stays unchanged.
Webview toggle gotcha: settings changes must also round-trip back in state payloads.
- Add the field to `UpdateSettingsRequest` in `proto/cline/state.proto` (for webview update requests), then run `npm run protos`
- Add the field to `UpdateSettingsRequest` in `proto/cline/state.proto` (for webview update requests), then run `bun run protos`
- Include the key in `Controller.getStateToPostToWebview()` (`src/core/controller/index.ts`)
- Ensure `ExtensionState` and webview defaults include the key (`src/shared/ExtensionMessage.ts`, `webview-ui/src/context/ExtensionStateContext.tsx`)
If this round-trip wiring is missing, the backend value can update but the toggle in webview appears stuck or reverts.
## StateManager Cache vs Direct globalState Access
StateManager uses an in-memory cache populated during `StateManager.initialize(context)` in `common.ts`. For most state, use `controller.stateManager.setGlobalState()`/`getGlobalStateKey()`.
StateManager uses an in-memory cache populated during `StateManager.initialize()` from file-backed storage. For most state, use `controller.stateManager.setGlobalState()`/`getGlobalStateKey()`.
Exception: State needed immediately at extension startup (before cache is ready)
Exception: host migration code may read legacy VS Code storage before file-backed storage is initialized.
When Window A sets state and immediately opens Window B, the new window's StateManager cache is populated from `context.globalState` during initialization. If you need to read state in Window B right at startup (e.g., in `common.ts` during `initialize()`), read directly from `context.globalState.get()` instead of StateManager's cache.
Example pattern (see `lastShownAnnouncementId` and `worktreeAutoOpenPath`):
Example pattern:
```typescript
// Writing (normal pattern)
controller.stateManager.setGlobalState("myKey", value)
// Reading at startup in common.ts (bypass cache)
const value = context.globalState.get<string>("myKey")
// Reading after initialization
const value = controller.stateManager.getGlobalStateKey("myKey")
```
This is only needed for cross-window state read during the brief startup window before StateManager cache is fully usable. Normal state access after initialization should use StateManager.
Use `context.globalState` only in VS Code migration code that copies legacy ExtensionContext values into the shared file-backed stores.
## ChatRow Cancelled/Interrupted States
When a ChatRow displays a loading/in-progress state (spinner), you must handle what happens when the task is cancelled. This is non-obvious because cancellation doesn't update the message content—you have to infer it from context.
@@ -203,3 +158,48 @@ const isGenerating = explanationInfo.status === "generating" && !wasCancelled
**See also:** `BrowserSessionRow.tsx` uses similar pattern with `isLastApiReqInterrupted` and `isLastMessageResume`.
**Backend side:** When streaming is cancelled, clean up properly (close tabs, clear comments, etc.) by checking `taskState.abort` after the streaming function returns.
## Debug Harness: clear inherited VSCode/Electron env vars before launching
The debug harness (`apps/vscode/src/dev/debug-harness/server.ts`) launches a child
VSCode via Playwright's `_electron.launch({ env: { ...process.env, ... } })`. If you
run the harness from a process that was itself spawned by VSCode (e.g. the Cline
extension host, an integrated terminal, or an agent running inside VSCode), the
parent's VSCode/Electron env vars leak into the child and break the launch.
The fatal one is **`ELECTRON_RUN_AS_NODE=1`**: it makes the child VSCode binary run
as plain Node, so it rejects every VSCode CLI flag. Symptom:
```
.../Visual Studio Code.app/Contents/MacOS/Code: bad option: --extensionDevelopmentPath=...
Error: Process failed to launch! (Playwright _electron.launch)
```
This is NOT the macOS Playwright flakiness mentioned in the harness README — it's
env inheritance. Fix: strip the inherited vars before starting the harness:
```bash
env -u ELECTRON_RUN_AS_NODE -u ELECTRON_NO_ATTACH_CONSOLE \
-u VSCODE_CLI -u VSCODE_CODE_CACHE_PATH -u VSCODE_CRASH_REPORTER_PROCESS_TYPE \
-u VSCODE_CWD -u VSCODE_ESM_ENTRYPOINT -u VSCODE_HANDLES_UNCAUGHT_ERRORS \
-u VSCODE_IPC_HOOK -u VSCODE_NLS_CONFIG -u VSCODE_PID -u VSCODE_L10N_BUNDLE_LOCATION \
bun src/dev/debug-harness/server.ts --auto-launch --skip-build
```
Check your own env with `env | grep -iE 'electron|vscode_'` first; `ELECTRON_RUN_AS_NODE=1`
present means you must scrub before launching.
Other harness notes confirmed in practice:
- The extension host is **ESM** (`VSCODE_ESM_ENTRYPOINT`), so `ext.evaluate` has no
`require` and module-internal functions aren't reachable as globals. To inspect
internal builders (e.g. `buildBedrockProviderConfig`), set a breakpoint with
`ext.set_breakpoint` and read locals via `ext.evaluate` with the paused `callFrameId`
— don't try to `require()` the bundle.
- `web.evaluate` wraps the expression as a single returned expression; multi-statement
snippets must be an IIFE `(() => { ...; return x; })()`, otherwise you get
`SyntaxError: Unexpected token ';'`.
- Webview settings inputs are `vscode-text-field` web components with debounced React
onChange. Setting `.value` + dispatching events via `web.evaluate` is unreliable for
some fields; focus the inner shadow `input` then use real keystrokes (`ui.type` +
`ui.press Tab`, or click the dropdown option) to make the value persist.
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "PostToolUse running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "PostToolUse response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "PostToolUse hook custom errorMessage"
}
EOF
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "PreToolUse running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "PreToolUse response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "PreToolUse hook custom errorMessage"
}
EOF
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "TaskCancel running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "TaskCancel response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "TaskCancel hook custom errorMessage"
}
EOF
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "TaskResume running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "TaskResume response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "TaskResume hook custom errorMessage"
}
EOF
-19
View File
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "TaskStart running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "TaskStart response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "TaskStart hook custom errorMessage"
}
EOF
@@ -1,19 +0,0 @@
#!/usr/bin/env bash
echo "UserPromptSubmit running inside local cline/.clinerules/hooks/ directory"
input=$(cat)
echo $input | jq .
for i in {1..5}; do
sleep 1
echo "$i"
done
cat <<EOF
{
"cancel": false,
"contextModification": "UserPromptSubmit response from the local cline/.clinerules/hooks/ directory.",
"errorMessage": "UserPromptSubmit hook custom errorMessage"
}
EOF
+1 -1
View File
@@ -42,7 +42,7 @@ Here, we use the common `StringRequest` and `KeyValuePair` types.
After editing a `.proto` file, regenerate the TypeScript code. From the project root, run:
```bash
npm run protos
bun run protos
```
This command compiles all `.proto` files and outputs the generated code to `src/generated/` and `src/shared/`. Do not edit these generated files manually.
+26
View File
@@ -0,0 +1,26 @@
# SDK Adapter
The VSCode extension runs on the Cline SDK (`@cline/core`, `@cline/llms`,
`@cline/shared`) through an adapter layer in `apps/vscode/src/sdk/`. The
webview still talks gRPC; the adapter translates between gRPC handlers and SDK
calls. See `apps/vscode/src/dev/debug-harness/README.md` for the debug harness.
## Conventions
1. **Look up SDK APIs, don't guess.** Use `kb_search(name="sdk", query="...")`
before implementing against an SDK surface.
2. **Reference the pre-SDK implementation when replacing a module.** Add a
`// Replaces classic src/core/... (see origin/main)` header and use
`kb_search(name="cline", commit="origin/main")` or
`git show origin/main:path` to consult the prior implementation.
3. **Single entry point.** There is one codepath — the SDK adapter. No
`CLINE_SDK` env flag.
4. **Use `{appBaseUrl}`**, never hardcode `app.cline.bot`.
5. **Avoid `as` casts.** Use explicit conversion functions with tests. The
branded types in `apps/vscode/src/sdk/model-catalog/contracts.ts` exist so
casts are unnecessary outside parse/compute boundaries.
## Debug harness
- **Dismiss the Kanban/promo overlay** before any debug harness interaction.
- **Use the command palette** to navigate tabs in the debug harness.
+2 -2
View File
@@ -91,7 +91,7 @@ On the main branch, create a commit that updates:
3. No changelog-entry file cleanup is needed. Contributors do not create changelog-entry files in this repo.
**Skip running `npm run install:all`** - release automation handles lockfile consistency as needed.
**No dependency install is needed.** A CHANGELOG + `version` bump does not change any dependency, and `bun.lock` does not pin workspace-package versions, so the lockfile stays consistent. The publish workflow runs `bun install --frozen-lockfile`, which would *fail* on an out-of-sync lock — so only run `bun install` here if you actually change dependencies (then commit the updated `bun.lock`).
Commit with message format: `v{VERSION} Release Notes (hotfix)`
@@ -176,7 +176,7 @@ Present a final summary:
- Slack message copied to clipboard: yes
Remind the user to:
1. Manually trigger the publish release GitHub Action at: https://github.com/cline/cline/actions/workflows/publish.yml (paste `v{VERSION}` as the tag)
1. Manually trigger the publish release GitHub Action at: https://github.com/cline/cline/actions/workflows/ext-vscode-publish-stable.yml (paste `v{VERSION}` as the tag)
2. Post the Slack message to announce the hotfix
## Important Notes
+1 -1
View File
@@ -43,7 +43,7 @@ git push origin v<version>
### 4) Trigger publish workflow
Tell the maintainer to run:
https://github.com/cline/cline/actions/workflows/publish.yml
https://github.com/cline/cline/actions/workflows/ext-vscode-publish-stable.yml
Use `v<version>` as the release tag.
+3 -2
View File
@@ -20,8 +20,9 @@ command = "chmod +x ./scripts/run-extension-host.sh && ./scripts/run-extension-h
name = "CLI"
icon = "run"
command = '''
npm run cli:build
npm run cli:run
cd sdk
bun install
bun run cli
'''
[[actions]]
+1 -1
View File
@@ -1,2 +1,2 @@
/.github/ @saoudrizwan @arafatkatze @maxpaulus43 @candieduniverse
/.github/ @saoudrizwan @arafatkatze @maxpaulus43 @dominiccooney
/README.md @saoudrizwan @juanpflores
+25 -3
View File
@@ -7,14 +7,16 @@ body:
value: |
**Important:** All bug reports must be reproducible using Claude Sonnet 4.5. Cline uses complex prompts so less capable models may not work as expected.
- type: dropdown
id: plugin-type
id: cline-surface
attributes:
label: Plugin Type
description: Which plugin are you reporting a bug for?
label: Cline Surface
description: Which Cline surface are you reporting a bug for?
options:
- VSCode Extension
- JetBrains Plugin
- CLI
- Desktop App
- Cloud Platform
default: 0
validations:
required: true
@@ -26,6 +28,12 @@ body:
placeholder: 'e.g., 1.2.3'
validations:
required: true
- type: checkboxes
id: beta
attributes:
label: Beta version
options:
- label: I am using a beta version of Cline
- type: textarea
id: what-happened
attributes:
@@ -53,6 +61,20 @@ body:
placeholder: 'e.g., cline:anthropic/claude-sonnet-4.5, gemini:gemini-2.5-pro-exp-03-25'
validations:
required: false
- type: textarea
id: ide-diagnostics
attributes:
label: Diagnostics
description: |
Paste the diagnostics for your Cline surface. This captures the build, runtime, and host details we need.
- VSCode Extension: open `Help → About` (Windows/Linux) or `Code → About Visual Studio Code` (macOS), then copy the info.
- JetBrains Plugin: open `Help → About` (Windows/Linux) or `<IDE name> → About` (macOS), then click `Copy` to grab build, runtime, OS, memory, and cores.
- CLI: there is no About dialog. Run `cline --version` and paste the output.
- Desktop App: paste the app version from the Settings view.
- Cloud Platform: paste your browser name and version, plus the page URL where the issue occurred.
placeholder: Paste the copied About info, `cline --version` output, or browser/app details here.
validations:
required: false
- type: textarea
id: system-info
attributes:
+57
View File
@@ -0,0 +1,57 @@
# Copilot Instructions for Cline
This is a VS Code extension. Read `.clinerules/general.md` for tribal knowledge and nuanced patterns.
## Architecture
- **Core** (`src/`): `extension.ts``WebviewProvider``Controller` (single source of truth) → `Task` (agent loop).
- **Webview** (`webview-ui/`): React/Vite app. State via `ExtensionStateContext.tsx`, synced through message passing.
- **Communication**: Protobuf-defined gRPC-like protocol over VS Code message passing. Schemas in `proto/`.
- **MCP**: `src/services/mcp/McpHub.ts`.
## Build & Test (Critical — non-obvious commands)
- **Build**: `bun run compile` — NOT `bun run build`.
- **Watch**: `bun run watch` (extension + webview).
- **Protos**: `bun run protos` — run **immediately** after any `.proto` change. Generates into `src/shared/proto/`, `src/generated/`.
- **Tests**: `bun run test:unit`. After prompt/tool changes: `UPDATE_SNAPSHOTS=true bun run test:unit`.
## Protobuf RPC Workflow (4 steps)
1. **Define** in `proto/cline/*.proto`. Naming: `PascalCaseService`, `camelCase` RPCs, `PascalCase` Messages. Use `common.proto` shared types for simple data.
2. **Generate**: `bun run protos`.
3. **Backend handler**: `src/core/controller/<domain>/`.
4. **Frontend call**: `UiServiceClient.myMethod(Request.create({...}))`.
- Adding enums (e.g. `ClineSay`) → also update `src/shared/proto-conversions/cline-message.ts`.
## Adding API Providers (silent failure risk)
Three proto conversion updates are **required** or the provider silently resets to Anthropic:
1. `proto/cline/models.proto` — add to `ApiProvider` enum.
2. `convertApiProviderToProto()` in `src/shared/proto-conversions/models/api-configuration-conversion.ts`.
3. `convertProtoToApiProvider()` in the same file.
Also update: `src/shared/api.ts`, `src/shared/providers/providers.json`, `src/core/api/index.ts`, `webview-ui/.../providerUtils.ts`, `webview-ui/.../validate.ts`, `webview-ui/.../ApiOptions.tsx`.
For Responses API providers: add to `isNextGenModelProvider()` in `src/utils/model-utils.ts` and set `apiFormat: ApiFormat.OPENAI_RESPONSES` on models.
## Adding Tools to System Prompt (5+ file chain)
1. Add enum to `ClineDefaultTool` in `src/shared/tools.ts`.
2. Create definition in `src/core/prompts/system-prompt/tools/` (export `[GENERIC]` minimum).
3. Register in `src/core/prompts/system-prompt/tools/init.ts`.
4. Whitelist in `src/core/prompts/system-prompt/variants/*/config.ts` for each model family.
5. Handler in `src/core/task/tools/handlers/`, wire in `ToolExecutor.ts`.
6. If tool has UI: add `ClineSay` enum in proto → `ExtensionMessage.ts``cline-message.ts``ChatRow.tsx`.
7. Regenerate snapshots: `UPDATE_SNAPSHOTS=true bun run test:unit`.
## Modifying System Prompt
Modular: `components/` (shared) + `variants/` (model-specific) + `templates/` (`{{PLACEHOLDER}}`). Variants override components via `componentOverrides` in `config.ts` or custom `template.ts`. XS variant is heavily condensed inline. Always regenerate snapshots after changes.
## Global State Keys (silent failure risk)
Adding a key requires updating the typed storage definitions in `src/shared/storage/state-keys.ts`; runtime reads and writes should go through `StateManager`, not VS Code `ExtensionContext` storage. Persistent state is file-backed so it works across VS Code, CLI, and JetBrains hosts.
## Slash Commands (3 places)
- `src/core/slash-commands/index.ts` — definitions.
- `src/core/prompts/commands.ts` — system prompt integration.
- `webview-ui/src/utils/slash-commands.ts` — webview autocomplete.
## Conventions
- **Paths**: Always use `src/utils/path` helpers (`toPosixString`) for cross-platform compatibility.
- **Logging**: `src/shared/services/Logger.ts`.
- **Feature flags**: See PR #7566 as reference pattern.
+2 -2
View File
@@ -2,7 +2,7 @@ version: 2
updates:
# Main extension dependencies
- package-ecosystem: "npm"
directory: "/"
directory: "/apps/vscode"
schedule:
interval: "weekly"
# Group all updates into a single PR
@@ -20,7 +20,7 @@ updates:
# Webview UI dependencies
- package-ecosystem: "npm"
directory: "/webview-ui"
directory: "/apps/vscode/webview-ui"
schedule:
interval: "weekly"
groups:
+1 -1
View File
@@ -59,7 +59,7 @@ We're not looking for exhaustive documentation - just evidence that you've thoug
<!-- Put an 'x' in all boxes that apply -->
- [ ] Changes are limited to a single feature, bugfix or chore (split larger changes into separate PRs)
- [ ] Tests are passing (`npm test`) and code is formatted and linted (`npm run format && npm run lint`)
- [ ] Tests are passing (`bun test`) and code is formatted and linted (`bun run format && bun run lint`)
- [ ] I have reviewed [contributor guidelines](https://github.com/cline/cline/blob/main/CONTRIBUTING.md)
### Screenshots

Some files were not shown because too many files have changed in this diff Show More