mirror of
https://github.com/cline/cline.git
synced 2026-09-13 18:10:14 +08:00
db9d3a3436734efa07fa254d34409cbda596de8e
264
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
+14 |
dd4d80b7b3 |
chore(desktop): sync latest main into desktop experimental (#13648)
* 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> * 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 * 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. * 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. * 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. * fix(vscode): SDK remote-config parity — refresh coordination, session gating, and fail-closed opt-out (#13226) * 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> * 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> * 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> * 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.) * 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. * 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. * 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> * fix: normalize Gemini custom base URLs for legacy host-root values (#13329) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * docs: add GLM-5.3 to ClinePass models and reference pricing (#13357) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * 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> * 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> * 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> * Use fixed selection chevron in account dialog to match other dialogs (#13364) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): align system prompt with session mode (#13361) Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> * 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> * chore(desktop): release v0.0.14 * 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 * 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> * 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 * fix(llms): display billed gateway cost (#13385) * 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> * 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. * 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. * 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> * 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. * 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> * 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> * 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> * 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> * fix(desktop): remove settings gear hover state while Account screen is open (#13408) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * 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> * 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> * 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> * 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> * 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> * 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> * 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> * 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. * feat(desktop): refresh app icons and branding (#13400) * 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. * 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. * 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. * 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. * 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> * 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> * 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> * 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 |
||
|
+8 |
e04e23c52d |
chore(desktop): sync latest main into desktop experimental (#13523)
* 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> * 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 * 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. * 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. * 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. * fix(vscode): SDK remote-config parity — refresh coordination, session gating, and fail-closed opt-out (#13226) * 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> * 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> * 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> * 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.) * 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. * 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. * 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> * fix: normalize Gemini custom base URLs for legacy host-root values (#13329) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * docs: add GLM-5.3 to ClinePass models and reference pricing (#13357) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * 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> * 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> * 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> * Use fixed selection chevron in account dialog to match other dialogs (#13364) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): align system prompt with session mode (#13361) Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> * 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> * chore(desktop): release v0.0.14 * 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 * 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> * 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 * fix(llms): display billed gateway cost (#13385) * 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> * 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. * 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. * 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> * 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. * 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> * 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> * 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> * 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> * fix(desktop): remove settings gear hover state while Account screen is open (#13408) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * 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> * 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> * 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> * 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> * 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> * 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> * 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> * 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. * feat(desktop): refresh app icons and branding (#13400) * 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. * 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. * 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. * 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. * 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> * 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> * 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> * 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 |
||
|
+3 |
b905c78640 |
chore(desktop): sync latest main into desktop experimental (#13461)
* 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> * 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 * 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. * 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. * 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. * fix(vscode): SDK remote-config parity — refresh coordination, session gating, and fail-closed opt-out (#13226) * 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> * 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> * 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> * 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.) * 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. * 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. * 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> * fix: normalize Gemini custom base URLs for legacy host-root values (#13329) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * docs: add GLM-5.3 to ClinePass models and reference pricing (#13357) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * 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> * 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> * 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> * Use fixed selection chevron in account dialog to match other dialogs (#13364) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): align system prompt with session mode (#13361) Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> * 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> * chore(desktop): release v0.0.14 * 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 * 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> * 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 * fix(llms): display billed gateway cost (#13385) * 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> * 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. * 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. * 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> * 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. * 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> * 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> * 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> * 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> * fix(desktop): remove settings gear hover state while Account screen is open (#13408) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * 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> * 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> * 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> * 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> * 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> * 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> * 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> * 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. * feat(desktop): refresh app icons and branding (#13400) * 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. * 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. * 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. * 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. * 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> * 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> * 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> * 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 |
||
|
+2 |
2e2400ad1f |
chore(desktop): sync main and cut 0.0.15-beta.1 (#13425)
* 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> * 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 * 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. * 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. * 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. * fix(vscode): SDK remote-config parity — refresh coordination, session gating, and fail-closed opt-out (#13226) * 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> * 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> * 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> * 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.) * 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. * 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. * 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> * fix: normalize Gemini custom base URLs for legacy host-root values (#13329) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * docs: add GLM-5.3 to ClinePass models and reference pricing (#13357) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * 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> * 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> * 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> * Use fixed selection chevron in account dialog to match other dialogs (#13364) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): align system prompt with session mode (#13361) Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> * 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> * chore(desktop): release v0.0.14 * 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 * 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> * 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 * fix(llms): display billed gateway cost (#13385) * 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> * 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. * 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. * 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> * 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. * 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> * 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> * 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> * 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> * fix(desktop): remove settings gear hover state while Account screen is open (#13408) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * 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> * 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> * 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> * 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> * 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> * 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> * 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> * 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. * chore(desktop): cut 0.0.15-beta.1 * chore(desktop): reconcile Cargo.lock with merged dependencies * feat(desktop): refresh app icons and branding (#13400) * chore(desktop): restore ai dependency and refresh lockfile * chore(desktop): align beta cloud copy with the Cline rename * chore(desktop): merge refreshed app icons (#13400) and dedupe changelog --------- Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> Co-authored-by: JasmineLCY <38378321+JasmineLCY@users.noreply.github.com> Co-authored-by: Mikołaj Kondratek <19799111+mkondratek@users.noreply.github.com> Co-authored-by: Max <maxpaulus43@gmail.com> Co-authored-by: Bee <68532117+abeatrix@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Bee <abeatrix@users.noreply.github.com> Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com> Co-authored-by: Ara <arafat.da.khan@gmail.com> Co-authored-by: Haley Park <haleypark.design@gmail.com> |
||
|
|
10c747663d |
chore(desktop): sync latest main into desktop experimental (#13379)
* 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> * 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 * 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. * 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. * 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. * fix(vscode): SDK remote-config parity — refresh coordination, session gating, and fail-closed opt-out (#13226) * 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> * 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> * 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> * 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.) * 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. * 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. * 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> * fix: normalize Gemini custom base URLs for legacy host-root values (#13329) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * docs: add GLM-5.3 to ClinePass models and reference pricing (#13357) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * 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> * 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> * 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> * Use fixed selection chevron in account dialog to match other dialogs (#13364) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): align system prompt with session mode (#13361) Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> * 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> * chore(desktop): release v0.0.14 * 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 * 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> * test(vscode): stabilize code action activation * test(vscode): activate code action by keyboard * test(vscode): decouple action discovery from invocation --------- Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> Co-authored-by: JasmineLCY <38378321+JasmineLCY@users.noreply.github.com> Co-authored-by: Mikołaj Kondratek <19799111+mkondratek@users.noreply.github.com> Co-authored-by: Max <maxpaulus43@gmail.com> Co-authored-by: Bee <68532117+abeatrix@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Bee <abeatrix@users.noreply.github.com> |
||
|
|
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> |
||
|
|
51784a3bf1 |
docs: add Qwen3.8 Max to ClinePass model list and reference pricing (#13144)
Co-authored-by: Cline <cline@users.noreply.github.com> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
72561771a7 | docs: move ACP editor integration to a dedicated Usage page (#12821) | ||
|
|
2ac20c647c |
docs: add Editor Integration (ACP) section to CLI overview (#12808)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
1cf19304f4 | docs: restore 'open Cline in right sidebar' guide as section of IDE usage page (#12771) | ||
|
|
95b841a2dd | docs: add screenshots for finding free models (#12690) | ||
|
|
7d63376d98 |
Revert "Revert "docs: add Cline free models page (#12183)" (#12185)" (#12186)
This reverts commit
|
||
|
|
372f029343 | docs: add Poolside provider setup guide (#12586) | ||
|
|
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. |
||
|
|
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 |
||
|
|
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 |
||
|
|
bdb216c110 |
Revert "docs: add Kimi K3 to ClinePass documentation (#12380)" (#12407)
This reverts commit
|
||
|
|
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> |
||
|
|
cc29955c2d | docs: add Kimi K3 to ClinePass documentation (#12380) | ||
|
|
2ca8364ffc | docs: add Kimi K3 to ClinePass model list and reference pricing (#12393) | ||
|
|
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
|
||
|
|
ed3107f9ec |
Revert "docs: add Cline free models page (#12183)" (#12185)
This reverts commit
|
||
|
|
6bce48aad4 |
docs: add Cline free models page (#12183)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> |
||
|
|
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> |
||
|
|
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. |
||
|
|
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 |
||
|
|
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 |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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 |
||
|
|
9c030a93b6 |
Remove Explain Changes feature (#11278)
* chore(vscode): remove explain changes entry points * chore(vscode): remove explain changes feature |
||
|
|
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) |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |
||
|
|
42e4ea60db |
Fix marketplace getting started link (#11170)
Co-authored-by: morning-verlu <258725120+morning-verlu@users.noreply.github.com> |
||
|
|
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 |
||
|
|
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 |
||
|
|
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). |
||
|
|
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 |
||
|
|
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
|
||
|
|
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> |
||
|
|
acca9186f1 | docs: clarify plugin SDK peer dependency (#10823) |