mirror of
https://github.com/cline/cline.git
synced 2026-09-21 13:21:23 +08:00
595f1dbf2ea819e987afeadb4ed4dd9a0ae9a55e
215
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4d28d82efa |
feat(cli): handle outdated hub sessions with drain and replace flow (#13727)
* feat(cli): handle outdated hub sessions with drain and replace flow Add logic to detect when the CLI is newer than the running Hub and provide users with options to either keep the older Hub running (to avoid interrupting active sessions from other clients) or force-replace it. Implement `describeOutdatedHubSessions` helper to show quantified session activity in the dialog, and add `HubOutdatedContent` UI component with detailed messaging for the `build_mismatch` case. The `unsupported_protocol` case remains a modal requiring update, while the softer mismatch now uses a toast with enter-to-replace or escape-to-keep choices. Includes tests for draining and replacing an older busy hub when forced. * fix(hub): gate desktop hub_upgrade behind trusted connection and make drain-first a hard guarantee Address review: an originless local WebSocket client could invoke the forceful hub_upgrade command, and a failed drain request still allowed a forced retirement, so work started during the wait window could be killed. - hub_upgrade now requires the same canApproveTools per-connection gate as the tool-approval commands. - upgradeManagedHub skips the idle-wait window when the drain was not established (an undrained hub keeps admitting work, so waiting only widens the blast radius) and refuses to replace a busy hub that did not accept the drain, force or not. An idle hub is still replaced so pre-drain-endpoint hubs (404) remain upgradable. * fix(hub): treat failed activity readings as unknown, not idle, during hub upgrade A transient session.list failure inside the drain wait window previously read as an idle hub, which could end the grace window early and authorize retirement while turns were still finishing. - Failed readings never end the wait window early, never overwrite the last real observation, and never authorize a non-forced retirement. - Without force, a hub whose activity was never confirmed is handed back un-drained (still_busy) instead of retired; an undrained hub is now replaced only when positively observed idle. - With force and an accepted drain, an unanswerable hub is still replaced: the user already consented to interrupting its sessions. * fix(hub): never retire an undrained hub on an idle snapshot An older hub that rejects the drain has no admission barrier, so a single idle reading cannot authorize retirement: a session admitted right after the snapshot would die in a retire the consent prompt never covered. upgradeManagedHub now retires a hub only under an accepted drain. The undrained-idle case is delegated to the locked ensure path, which re-checks activity immediately before its own retire ladder and attaches (deferring the swap) when new work arrived in the meantime; the upgrade then reports still_busy instead of replaced, and the desktop/TUI surfaces tell the user to retry. * fix(hub): require an accepted drain unconditionally before any upgrade retirement Review follow-up: the undrained-idle delegation still reached retireDiscoveredHub, whose own drain attempt is best-effort, so a session admitted after the idle re-check could die in the shutdown. upgradeManagedHub now fails fast when the hub does not accept the drain - no wait window, no idle exception, no delegation. The drain is the admission barrier that keeps every subsequent reading true through the retire; a hub too old or wedged to accept it is left to the automatic ensure path, which replaces it once idle at the next client startup, and the error says so. * fix(hub): establish the drain barrier before the automatic idle check Review follow-up: the automatic incompatible-hub path read session activity first and drained only inside the retire ladder, so a session admitted between the idle snapshot and the shutdown could be terminated. retireIncompatibleHub now requests the drain before the busy check: with the drain accepted, the idle reading stays true through the retire. A deferred (busy) hub, and one whose retirement fails or is skipped by the circuit breaker, gets the drain lifted so it never sits alive-but-refusing work. Hubs that do not accept the drain (pre-/drain builds answer 404) keep the historical best-effort snapshot rather than being stranded forever. * polish(hub): tighten the outdated-hub dialog copy Two short sentences instead of four long ones, spell out what Quit Cline does (closes the app, leaves the Hub running), and rename the action to Update Now in both the desktop dialog and the TUI variant. * fix(cli): show the keep-Hub reminder toast when the outdated-hub dialog is dismissed (#13754) dialog.choice() resolves undefined on Esc rather than rejecting, so the reminder toast in .catch() never ran. Move it to the falsy branch of .then(), matching the unsupported_protocol handler. --------- Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> |
||
|
|
b427fae9f9 |
feat(desktop): group scheduled runs under their schedule in the sidebar (#13752)
* feat(core): stamp schedule id, name, and run number onto scheduled sessions Sessions started by the cron runner only carried a generic sessionHistoryOrigin.trigger = "hub-schedule", so clients could tell a session was scheduled but not which schedule it belonged to or which run it was. The runner now passes schedule provenance to the runtime handlers, which merge it into the session metadata alongside the origin trigger: scheduleId the hub schedule's external id scheduleName the schedule title scheduleExecutionId the cron run id scheduleRunNumber 1-based position among every run created for the spec The run number comes from a new SqliteCronStore.getRunOrdinal, which counts runs of every status in creation order so a later cancellation never shifts numbers already stamped onto earlier sessions. A reclaimed run keeps its number, so two sessions with the same number make a duplicate visible. HubScheduleRuntimeHandlers.startSession gains an optional second argument carrying the metadata; existing implementations that ignore it keep working. * feat(desktop): group scheduled runs under their schedule in the sidebar A schedule that fires daily filled the sidebar's Scheduled section with a row per run, each titled with the same prompt text, which read as if the task had been duplicated. Runs of one schedule now fold into a single collapsible row named after the schedule, with the run count on the right; expanding it lists the runs as "Run N" sub-items (newest first) with their usual status dot, time, hover card, context menu, and delete button. The group holding the active session expands on its own so a run opened from the Schedules page is visible. Grouping also applies inside project groups when sorting by project. The Scheduled header now counts schedules rather than runs. Threads learn the schedule identity from the metadata the runner now stamps (scheduleId, scheduleName, scheduleRunNumber). Runs recorded before that fall back to the schedule executions list the hook already polls, which now yields the schedule id and name instead of a bare session id set, and finally to grouping by shared title. Runs without a number are labelled with their start time instead of "Run N". * fix(desktop): reopen a collapsed schedule group when one of its runs is opened A stored collapse used to win over the active-session default for the sidebar's lifetime, so a run opened from the Schedules settings page could stay hidden inside its collapsed group. Opening a session now clears the stored choice for the group that holds it; the group can still be collapsed afterwards. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
b9977a139f |
feat(desktop): import sessions from Claude Code, Codex, and opencode (#13744)
* feat(core): session import service for Claude Code, Codex, and opencode history
Adds a SessionImportService to @cline/core that discovers sessions in the
on-disk stores of Claude Code (~/.claude/projects JSONL), Codex
(~/.codex/sessions rollouts + session_index titles), and opencode
(opencode.db sqlite), translates each conversation into Cline's native
MessageWithMetadata format, and persists it through CoreSessionService as
a completed, listable, resumable session.
Key mechanics:
- Claude Code: parentUuid tree walk from the newest leaf picks the active
branch (edits/retries branch the log); same-message.id assistant lines
merge back into one turn; sidechains, meta lines, and slash-command
wrappers are excluded; ai-title/summary lines provide titles.
- Codex: real prompts come from user_message event_msg lines (user-role
response_items are injected AGENTS/environment context, with a fallback
for old rollouts); function_call/output pairs map to tool_use/tool_result;
resumed rollouts that re-embed the original session id dedupe to the
richest file; token_count events stamp per-turn metrics.
- opencode: reads a temp snapshot of the WAL-mode db; inline tool parts
split into tool_use + tool_result to preserve provider-valid structure;
child (subagent) sessions and synthetic parts are skipped.
- Shared sanitizer guarantees replayability: orphaned tool_use gets a
placeholder result, orphaned tool_results and empty text blocks drop,
provider-session-scoped signatures/encrypted reasoning strip.
- Imported sessions pass every history-visibility gate (terminal status,
non-empty provider/model, chat-workspace fallback cwd, no fabricated
checkpoint metadata) and carry metadata.importedFrom for idempotent
re-discovery (alreadyImportedSessionId).
* feat(desktop): sidecar commands for importing sessions from other tools
Adds two sidecar WebSocket commands backed by @cline/core's
SessionImportService:
- list_importable_sessions: returns { installedTools, sessions } where
sessions are ImportableSessionSummary rows (tool, sourceId, title, cwd,
timestamps, messageCount, preview, alreadyImportedSessionId) discovered
in the local Claude Code / Codex / opencode stores.
- import_sessions: takes { selections: [{ tool, sourceId }] }, validates
each selection against the known tool list, imports sequentially
(per-session transactional), and broadcasts session_import_progress
events ({ index, total, result }) so the UI can render live progress.
Returns { results } with per-item ok/sessionId/title/error.
* feat(desktop): import sessions UI for Claude Code, Codex, and opencode
Adds an Import Sessions dialog to the desktop app driven by the sidecar's
list_importable_sessions / import_sessions commands:
- Scan phase discovers local history from all three tools and groups it
per tool with select-all checkboxes, per-row title, relative time,
message count, and workspace folder; rows already imported are disabled
and badged (idempotent re-open).
- Text filter across title, folder, and first-prompt preview.
- Import phase streams session_import_progress events into a progress bar
and per-item result list; the dialog cannot be dismissed mid-import via
overlay click. Done phase summarizes successes and lists failures with
their error messages.
- Entry points: an Import button in the Sessions view header and an
"Import sessions" row in Settings → General.
- use-session-history subscribes to session_import_progress so history
refreshes no matter which surface started the import.
- Wire types live in webview/lib/session-import.ts (mirrors the core
module's types so the client bundle never imports node-only code).
* fix(desktop): import dialog crash rendering session timestamps
formatRelativeTime takes a string (parseTimestamp calls .trim() on any
truthy value), but the import dialog passed the numeric updatedAtMs,
crashing the page with 'e.trim is not a function' as soon as scanned rows
rendered. Convert to an ISO string at the call site.
Slipped through because the webview has no typechecking anywhere:
tsconfig.dev.json excludes webview/ and next.config sets
typescript.ignoreBuildErrors, and the webview's own tsconfig currently
carries 64 pre-existing errors.
* feat(desktop): offer session import during onboarding
Adds an 'import' onboarding step between connect/github and done. The
step scans for importable Claude Code / Codex / opencode history on
entry and silently advances when nothing (new) is found or the scan
fails, so only people with actual history from other tools ever see it.
When sessions are found it summarizes the count and source tools, opens
the same ImportSessionsDialog used by the Sessions page for picking, and
flips to a confirmation state once at least one session imports. Skip is
always available, including while the scan is still running.
* fix(desktop): import dialog text overflow, collapsible sections, select all
- Titles no longer clip or push the row wide: they word-wrap up to two
lines (line-clamp-2 + break-words, with min-w-0 down the flex chain so
long unbroken Codex prompt titles can actually shrink); the meta line
keeps time/count fixed and truncates only the workspace name; progress
rows get the same min-w-0 treatment.
- Each tool section header is now a collapse toggle (chevron +
aria-expanded) so one tool with hundreds of sessions doesn't force
scrolling past it; collapsed headers still show count and selected
count, and filtering forces sections open so search matches can't hide
in a collapsed group. Collapse state resets per dialog open.
- New global Select all row above the list with indeterminate state and
an x-of-y selected counter; it operates on the currently visible
(filtered) selectable sessions, matching the per-section checkboxes.
* fix(desktop): import dialog header and search clipped by intrinsic column width
The dialog grid used the default auto column track, so a single
unbreakable string in a session title (Codex titles often contain URLs)
set the column's min-content width wider than the fixed 620px dialog --
break-words affects layout but not intrinsic sizing -- and
overflow-hidden then clipped everything in the column, including the
description and the search field. Pin the column to minmax(0,1fr) so the
container width always wins and long words wrap at the box edge instead.
Also add sm:max-w-none (the primitive's sm:max-w-lg survives
tailwind-merge across variants and was silently capping the dialog at
512px) and shrink-0 on the search and select-all rows so a tall list can
never compress them vertically.
* fix(desktop): onboarding import step rescanned after import and looped to done screen
The import step's scan effect depended on onContinue, an inline arrow the
parent recreates every render — and importing itself re-renders the app
shell via the history refresh. Each re-render re-ran the scan, and when
the user had imported everything (select all), the re-scan found zero
remaining sessions and hit the nothing-to-import auto-advance, yanking
them past their own import confirmation onto the done screen. The scan
now runs exactly once per step entry (onContinue held in a ref for the
async auto-skip paths).
Also, after a successful import the button is now 'Start building' and
completes onboarding directly instead of routing through the separate
done screen — two consecutive confirmation screens read as a loop. The
skip and nothing-found paths still go through the done screen so those
users get the 'You're all set' confirmation.
* fix(core): consolidate imported tool_results into the message after their tool_use
The import sanitizer answered missing tool_use ids with a separate
placeholder user message while leaving real results for the same turn in
later user messages. Anthropic requires every tool_result for a turn in
the user message immediately following it, so a partially-answered turn
would still 400 on resume. Rebuild any incomplete or split span as one
consolidated results message in tool_use order (placeholders for missing
ids, duplicates dropped) followed by a message carrying whatever else the
span held, mirroring the legacy migration sanitizer.
* fix(desktop): imported sessions resume on the user's configured provider; batch adapter caches
Opening a history session adopts the row's provider/model
(use-chat-session: session.provider || prev.provider), so imported rows
stamped with the source tool's provider — openai-native for Codex,
whatever opencode reported — resumed on providers the user may never have
configured and failed on first send. The dialog now passes the app's
current model selection (lastProvider/lastModelByProvider, i.e. what a
new chat would run on) and the service stamps it on the row; both halves
must be present so a Cline provider is never paired with a foreign model
id. The source provider/model are preserved in metadata.importedFrom and
per-message modelInfo stays accurate. Codex's provider id is corrected to
Cline's openai-native, and opencode's openai/google map to
openai-native/gemini.
Adapters also gain per-batch caches released via dispose(): Codex's
convert() re-walked the sessions tree and re-read every rollout head per
imported session (O(sessions x files)); it now builds the session-id ->
richest-file index once per batch. opencode copied the whole WAL db per
imported session; it now snapshots once per batch.
* fix(core): roll back failed imports and dedupe at import time
Addresses both Greptile P1s on #13744:
- A write failing after createRootSessionWithArtifacts (messages, status,
manifest, title) left a half-written pid-0 session in history whose
importedFrom marker also blocked retrying the source. persistConverted
now deletes the session on any later failure and rethrows.
- Dedup markers were read through listSessions, which caps its scan at
2000 rows, so a prior import older than the newest 2000 sessions was
invisible and the source could be imported again. Add
listSessionMetadata (ids + metadata for every row, no manifest reads or
reconciliation) and use it for markers. Also check idempotency at
import time, not only at discovery: a request for an already-imported
source resolves to the existing session (alreadyImported: true) instead
of writing a copy, covering stale pickers and repeated requests.
* fix(core): create imported sessions terminal and mark them imported last
Two failure modes shared one root cause -- the import wrote its session
in stages and claimed success too early:
- The row was created running/pid-0 and flipped to completed afterwards.
The stale-session reconciler runs in the hub daemon against the same
SQLite DB and, in that window, marks such rows failed and stamps
terminal_marker metadata. createRootSessionWithArtifacts now accepts
status/endedAt/exitCode so imports are created completed with the
source session's end time; the separate status flip and manifest
rewrite are gone.
- The importedFrom marker was written at creation, so a session whose
later writes failed (and whose rollback delete also failed) still
blocked retrying its source. The marker is now the final write, so it
means 'this import finished' and a half-written session can never
claim the source.
listSessionMetadata is unbounded by default so dedup sees every row.
* fix(core): resolve TS2352 casts in session-import tests (#13746)
tsc rejects casting ContentBlock[] straight to Record<string, unknown>[]
(RedactedThinkingContent is not comparable), which failed the Quality
Checks typecheck. Route the five assertion-site casts through a small
blocks() helper that widens via unknown.
* fix(core): flatten Codex content-block tool outputs during import
Newer Codex rollouts write custom_tool_call_output.output as an array of
Responses-API content blocks ({type:"input_text", text}) instead of a
plain string. The importer JSON.stringified that array into the
tool_result content, and the chat UI's tool-summary parser then rendered
each non-text block as its type label, so imported exec calls showed up
as "[input_text][input_text]" with no output.
Concatenate the text of string/text-bearing blocks (they are stream
chunks, so no separator) and keep the JSON fallback for anything else.
* fix(desktop): edit-and-resend on runs without a checkpoint
Editing a message forks the session before that run, and the sidecar
always routed that through manager.restore with workspace: true. Imported
sessions carry no checkpoint history, so editing any of their prompts
failed with "No checkpoint found at or before run N" — even after the
user had continued the session in Cline, since only the new runs get
checkpoints.
When no checkpoint exists at or before the edited run there is no
workspace state to roll back, so fork the trimmed transcript onto the
current workspace (the same path a full-history fork takes) instead of
erroring. Runs that do have a checkpoint still restore the workspace.
* fix(core): roll back failed session creation and coalesce overlapping imports
Two gaps Greptile flagged on the import path:
createRootSessionWithArtifacts upserts the row before writing the messages
file and manifest, and the call sat above persistConverted's rollback try.
A file write failing there left a completed row with no transcript in
history. Creation now runs inside the rollback, and deleteSession already
tolerates a missing row or missing files.
Each import_sessions request builds its own service and snapshots the
existing-import markers once, so two overlapping requests for one source
(a second window, a double-fired command) both passed the dedupe check and
persisted two sessions. A module-level in-flight map keyed by tool:sourceId
makes the later caller wait on the first write and report its session as
already imported.
* fix(desktop): resolve the import resume target like a new chat does
An imported Claude Code session resumed on the Anthropic provider instead
of the user's Cline selection. The dialog read model-selection storage
directly and required both a remembered provider and a remembered model;
the composer only records a model from the explicit picker handlers, so
anyone running on the default model has no entry, the lookup came back
empty, and the service fell back to the source tool's provider.
Resolve the target with getInitialChatConfig() -- the same chain a new
chat uses (remembered selection, then the built-in default), which is
never empty -- and have the import_sessions handler default to the cline
provider and CLINE_DEFAULT_MODEL_ID when a caller sends nothing, matching
other server-started sessions. The source provider can no longer become
the resume target.
|
||
|
|
6d5a9793fc |
Desktop marketplace: show detail panel only on item click, left-align detail content (#13747)
* Desktop marketplace: show detail panel only on click, left-align detail content Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Desktop marketplace: drop license cell, single Learn more link (homepage, else repo) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Desktop marketplace: keep selected entry open while list is filtered Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
9b1374059e | fix(desktop): enable macOS voice input (#13741) | ||
|
|
8eb5f3d57f |
Default web search on for the desktop app (#13725)
* Default web search on for the desktop app Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Make desktop web search default seed best-effort Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> |
||
|
|
a7a57509e2 | chore(desktop): release v0.0.21 | ||
|
|
bcfa7c7e4d |
fix(desktop): keep Stop available for running child agents (#13678)
* fix(desktop): keep Stop available for running child agents * fix(desktop): reconcile aborted tool activity * fix(desktop): guard abort and agent polling races * fix(desktop): preserve authoritative abort status * fix(desktop): track queue-verified completion * test(desktop): trim duplicate abort coverage * fix(desktop): settle delayed queue verification |
||
|
|
c096030ace |
Desktop marketplace redesign: two-pane explorer with full catalog metadata (#13653)
* feat(desktop): add marketplace design exploration prototypes (storefront, explorer, registry) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): render catalog icon tiles without percentage padding Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * feat(desktop): drop placeholder icon tiles from explorer marketplace direction Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * feat(desktop): make explorer the marketplace view, drop design exploration harness Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * feat(desktop): add category tag filters to marketplace explorer Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * feat(desktop): collapse marketplace category pills behind a more toggle * feat(desktop): remove maturity badges and CLI install section from marketplace --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> |
||
|
|
60c74bc727 |
feat(ui): share attachment drop zone (#13672)
* feat(ui): share attachment drop zone * fix(ui): cancel disabled attachment drops * chore(ui): simplify drop zone surface * chore(ui): release v0.2.0-next.8 |
||
|
|
1fbcfab05d |
test: cover session search fallback on hub timeout and rejection (#13642)
* feat: add searchable session history Rebased onto main and updated to supersede the sidebar search dialog from #13533: the sidebar search icon now opens the indexed command bar (Cmd/Ctrl+P) instead of a sidebar-local cmdk dialog that eagerly loaded the entire session history via loadAllSessions(). CommandDialog gains a shouldFilter passthrough so server-ranked FTS hits are displayed as-is. * fix: harden session history search * fix: evict failed restoration sessions from search * fix: preserve deletion when search eviction fails * fix: address session search review feedback * fix: preserve search suppression during reconciliation * test: cover sidecar search fallback on hub timeout and rejection The existing search_sessions tests only exercised the index-hit and empty-index-fallback paths with an immediately-resolved hub reply. Add coverage for the two other realistic Hub-connection failure modes the fallback is meant to tolerate: the hub call rejecting, and the hub call hanging past the 750ms withSearchDeadline race. --------- Co-authored-by: abeatrix <beatrix@cline.bot> Co-authored-by: Bee <68532117+abeatrix@users.noreply.github.com> |
||
|
|
ce71fe5eb9 |
chore(llms): built-in model list update 1787907289186 (#13663)
* chore(llms): built-in model list update 1787907289186 Result of `bun run build:models`. Includes updated model list and fixed formatting issues across codebase. * test(llms): update GLM reasoning toggle expectation |
||
|
|
936c018689 | chore(desktop): release v0.0.20 | ||
|
|
957a4bf5d9 |
feat(desktop): render tool output images as attachments (#13643)
* fix(desktop): render tool output images as attachments Add support for displaying media returned by tool calls (e.g. screenshots) as rendered images with expand-to-fullscreen capability instead of raw base64 text. Introduces an `ImageCarousel` component for navigating multiple images, propagates the expand handler to tool message blocks, and extracts/validates output media in tool summaries. * test: cover multi-image and canonical media extraction in tool output (#13645) extractOutputMedia and the desktop tool-message rendering path were only ever exercised with exactly one distinct valid image, and canonicalInlineMedia (MCP-style type: "media" blocks for audio/video/file) had zero coverage. Add tests for: multiple distinct images in one tool result (parser + desktop carousel navigation), inline audio via the mime_type key spelling, canonical video/file media blocks, and rejection of an invalid canonical image block. --------- Co-authored-by: Harrison <harrison@cline.bot> |
||
|
|
b78f6d16d0 |
Add a GitHub integration step to the onboarding (#13225)
* Add feature flags to the app * React to account updates * Address comments * Add a GitHub integration step to the onboarding * validate domain and fix errors on auth * Hide the step behind a feature flag * update version --------- Co-authored-by: John Choi <john.choi@cline.bot> |
||
|
|
29530caa58 |
feat: add searchable session history (#13420)
* feat: add searchable session history Rebased onto main and updated to supersede the sidebar search dialog from #13533: the sidebar search icon now opens the indexed command bar (Cmd/Ctrl+P) instead of a sidebar-local cmdk dialog that eagerly loaded the entire session history via loadAllSessions(). CommandDialog gains a shouldFilter passthrough so server-ranked FTS hits are displayed as-is. * fix: harden session history search * fix: evict failed restoration sessions from search * fix: preserve deletion when search eviction fails * fix: address session search review feedback * fix: preserve search suppression during reconciliation --------- Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> |
||
|
|
f753a01d85 |
fix(desktop): don't show providers as configured without real credentials (#13608)
* fix(desktop): don't show providers as configured without real credentials The desktop settings marked any provider with a persisted settings entry as Configured, but legacy VS Code migration and empty saves can seed entries (e.g. qwen-code, sapaicore) holding only a default model and no credentials. Move the CLI's isProviderSettingsUsable readiness check into @cline/core, expose it as a computed 'configured' flag on the provider catalog, and use it in the desktop's isProviderConnected. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): resync catalog after saves so Configured badge updates live Optimistic provider mutations can't know the sidecar-computed 'configured' flag, so after connecting a keyless provider or saving cloud credentials (e.g. a Vertex project id) the row stayed 'Not configured' until remount. Silently refetch the catalog after each successful save, guarded by the existing generation counter so newer edits discard stale responses. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): claim a generation in post-save resync so overlapping refreshes can't apply stale snapshots Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): bump catalog generation on OAuth login success Every other optimistic provider mutation claims a new generation; the OAuth success path didn't, so a catalog load or resync still in flight could arrive late and overwrite the just-connected state. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): resync catalog after OAuth login instead of bare generation bump The resync claims a new generation (discarding any stale in-flight response) and its own fetch covers both the new OAuth connection and any provider saved moments earlier, matching the post-save path. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
c017c7016e |
fix(desktop): make the Tauri shell work on Windows (#13632)
- Defer updater installation to the user-initiated restart on Windows: install() launches the NSIS installer and exits the process immediately, so the background cycle now downloads only and stages the bytes, and restart_to_apply_update installs them after stopping the sidecar. - Spawn child processes (sidecar, git, cmd /C start) with CREATE_NO_WINDOW so the GUI-subsystem app doesn't pop visible console windows. - Fall back to USERPROFILE when HOME is unset resolving the MCP settings path, matching the sidecar's homedir(). - Reap the sidecar after the Windows hard-kill so its exe file lock is released before the NSIS installer replaces it. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
1d5d3b0055 |
Add tooltips explaining Live and After recording badges on voice input models (#13610)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
80dd573156 |
Desktop: surface scheduled-task final output — auto-expand submit_and_exit and render its summary as markdown (#13612)
* desktop: auto-expand submit_and_exit and render its summary as markdown Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * desktop: render submit summary in full foreground color Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * desktop: label the submit row 'Scheduled task completed' Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * desktop: label errored submit_and_exit rows as failed Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
ce2f7a00bb |
Make suggested routine template prompts prescriptive about their final output (#13611)
* Make bug hunter routine template prescriptive about its final report Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Make remaining routine templates prescriptive about their final output Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
ad1408636e |
fix(desktop): show agent-created schedules on the Schedules page (#13613)
* fix(desktop): show agent-created schedules on the Schedules page Schedule hub commands are scoped to the workspace registered by the connection, but the desktop app's hub client registers the app launch directory while agent-created schedules live under each chat's own workspace folder - so they never appeared on the Schedules page. Grant token-authenticated hub connections (which can already bind any workspace at registration) explicit cross-workspace schedule access via an allWorkspaces payload flag, and have the desktop sidecar request it for routine schedule commands. Workspace-bound clients (local browser origins) and default CLI behavior stay scoped. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * refactor(core): strip allWorkspaces flag from schedule inputs and pin it in the sidecar payload Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
8981079a43 |
Build and Authenticode-sign a Windows x64 desktop installer in desktop releases (#13607)
* feat(desktop): build and Authenticode-sign a Windows x64 NSIS installer in desktop releases Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): pin OIDC-adjacent actions to commit SHAs in the Windows signing job Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(desktop): pin checkout and upload-artifact to commit SHAs in the Windows signing job Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
ee0982cb98 |
fix(desktop): keep the window title bar draggable across views (#13572)
* fix(desktop): keep window title bar persistent * fix(desktop): reserve persistent title bar space * fix(desktop): polish persistent title bar layout |
||
|
|
70654acc3e |
feat(ui): share agent welcome hero (#13567)
* feat(ui): share agent welcome hero * test(ui): cover welcome hero pointer states * refactor(ui): keep welcome hero API minimal * test(ui): verify welcome hero package assets * fix(ui): inline welcome hero masks |
||
|
|
40c3a4dbd8 | chore(desktop): release v0.0.19 | ||
|
|
c0d6301884 | chore(desktop): release v0.0.18 | ||
|
|
d71f097656 |
fix(desktop): install marketplace plugins and MCP servers in-process instead of spawning a cline binary (#13585)
* fix: install marketplace plugins and MCP servers in-process instead of spawning a cline binary The desktop app sidecar and cline-hub shelled out to 'cline plugin install' and 'cline mcp install' for marketplace installs. Packaged GUI apps inherit launchd's minimal PATH on macOS and most desktop users have no cline CLI installed at all, so installs failed with a red 'Executable not found in $PATH: "cline"' error. Install via @cline/core's installPlugin/installMcpServer in-process instead, matching what the VS Code extension already does. Also fix parseMcpInstallArgs in @cline/core to treat the marketplace catalog's '--' separator as end-of-options; previously the separator itself became the stdio command. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * refactor: drop test-injection plumbing from marketplace installers Call @cline/core's installPlugin directly instead of threading an installer option through the marketplace entry points; tests stub the core module instead. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * revert: keep cline-hub marketplace installs CLI-backed The hub dashboard is launched via 'cline dashboard', so a CLI is always present and CLINE_WRAPPER_PATH resolves it; the PATH bug only affects the desktop app, which does not ship a CLI. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
6539f4deea |
feat(desktop): hover trash button on sidebar session rows (#13582)
Each session row shows a trash icon on the right while hovered (or when the button itself is focused), opening the same delete confirmation dialog the row's context menu uses. The row is a button and buttons cannot nest, so the trash is an absolutely positioned sibling inside a group/row wrapper, overlaid where the timestamp sits: row hover hides the timestamp, shows the trash, and moves the row's hover background to the wrapper group so it holds while the pointer is on the trash itself. |
||
|
|
036fc75b1f |
fix(desktop): don't block the main thread on quit while stopping the sidecar (#13566)
Quitting the mac app beach-balled for ~5-7s. The shutdown POST was built from the ws transport URL (appending /shutdown lands inside the query string), so the sidecar was never told to exit, and stop() then polled the child for up to 7s on the main thread - on macOS inside applicationWillTerminate - before SIGKILLing it. stop() now sends SIGTERM and returns immediately. The sidecar handles SIGTERM with the same bounded (5s) graceful shutdown as the /shutdown endpoint and exits itself, finishing session persistence as an orphan. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
6fc40127a6 |
feat(desktop): scheduled sessions UX — unified details dialog, run-now handoff, hidden steering, stuck-thinking fix (#13573)
* feat(desktop): merge schedule details into one view and open run-now sessions
The schedule details dialog drops its Overview/Runs tabs: one scrollable
column with the meta grid, the configuration JSON (capped at max-h-64
with internal scroll so it cannot crowd out what follows), and a Runs
section beneath it showing the three most recent runs with a ghost
"Show all N runs" expander (collapsed again whenever a different
schedule's details open). The "Full configuration for this schedule"
subtext is gone; the dialog passes aria-describedby={undefined} so
Radix does not warn about the missing description.
Run now hands you into the session it starts. The trigger command
queues the run and returns before the runner attaches a session id, so
after the toast the handler polls the schedule overview once a second
for up to 15 seconds — which doubles as keeping the page's run status
fresh (refreshSchedules now returns the fetched overview to make that
single-stream) — until the triggered execution reports its session id,
then calls onOpenSession. Guarded so it never auto-navigates after the
user left the page.
* feat(desktop): hide runtime steering messages from transcripts
Scheduled/automation runs inject user-role steering messages each
iteration ("[SYSTEM] This run is not complete until you call
submit_and_exit...", plus a team-obligations variant). The chat view
rendered them as user bubbles, as if the person had typed them — in a
scheduled session the transcript was mostly [SYSTEM] noise.
They are machinery talking to the model, not something the person said
or needs to read, so the transcript now hides them entirely:
MessageBubble renders null for any [SYSTEM]-prefixed user message.
Grouping still treats them as working-row machinery via a single
isSystemSteeringMessage predicate — they collapse into the run's work
span, are never a turn boundary, can never be mistaken for a run's
answer, and never advance the run count even when metadata is missing —
so work-block folding and checkpoint/edit run numbering stay correct.
A finished scheduled session now reads as prompt, work summary, answer.
* fix(desktop): poll history while an attached session's event stream is dead
Opening a scheduled session while (or right after) it runs left the
view stuck on the thinking shimmer until the user switched away and
back. Root cause is in core: the hub daemon executes scheduled runs on
a private LocalRuntimeHost inside createLocalHubScheduleRuntimeHandlers,
while the hub server only projects live events from its own session
host — so session.attach succeeds but no assistant/tool/status events
ever flow. And since multiple hub daemons share cron.db, a run claimed
by a different daemon is invisible to this hub regardless. The proper
core rewiring is tracked as ENG-2474.
Client-side heal that covers every case: while an attached history
session reports a busy status and no chat_event chunk has arrived for
five seconds (and no assistant bubble is mid-stream), poll every three
seconds — re-read canonical history, merged through the same dedupe
path hydration uses, and the session record's status — so the
transcript and the thinking indicator settle in place. Locally driven
turns keep chunks flowing, so the quiet-window guard keeps the fallback
inert there.
* chore(desktop): format workspace selector components
Biome formatting drift that landed on main; picked up by a formatter
pass over components/views/chat.
* fix(desktop): keep stale-stream poll inert during locally driven turns
The fallback poll could fire between a local submit and the model's
first chunk (optimistic user bubble added, stream quiet past the
window, no assistant bubble yet). It then replaced the optimistic
bubble — raw prompt text — with its canonical history twin, which is
stored wrapped in a user_input envelope. The rekey handler that runs
when the stream starts looks for a trailing user bubble matching the
raw prompt, finds only the wrapped copy, and appends a second bubble:
duplicated messages in normal interactive chat.
The poll now stays inert while a local turn is in flight
(turnEpoch !== turnSettledEpoch, or outstanding optimistic user
messages), checked both before polling and again after the snapshot
returns. Hydration marks the turn settled — the mount defaults
(epoch 0, settled -1) otherwise read as an open turn and would keep
the fallback inert forever for the scheduled-session case it exists
for. Applying a polled snapshot also rebuilds the live tool routing
keys, same as hydration, so later tool events update canonical rows
in place instead of appending.
* fix(desktop): keep the working indicator alive for narrating scheduled runs
Watching a scheduled run live: the first tool row appeared, then the
thinking indicator vanished with nothing streaming, and the rest of
the run (final answer, submit_and_exit) only showed up seconds later
in one lump.
inferHydratedChatStatus treats a "running" session record whose
transcript ends on an assistant message as a session that died without
a status flip and reports "completed". That heuristic is right for
stale records, but scheduled/automation models narrate between tool
calls, so a polled snapshot can genuinely end on assistant text
mid-run — the completed flip hid the working indicator, folded the
run early, and disarmed the stale-stream poll (status left the busy
set), dead-ending live updates until an in-flight poll happened to
deliver the finished run.
The heuristic now only applies once the transcript has actually gone
quiet (newest message older than two minutes — comfortably past model
latency plus tool runs). A recently active transcript keeps the
record's "running" verdict, so the indicator stays up and polling
stays armed until the record itself settles.
* fix(desktop): stale-stream poll mirrors the session record instead of inferring
Replaces the previous fix for the vanishing working indicator (the
time-window guard added to inferHydratedChatStatus) with a version
that adds no inference at all: the heuristic is restored to exactly
its long-standing form, and the poll now maps the session record's
status verbatim (mapSessionRecordStatus).
The record is the right authority in the poll's context: the sessions
this fallback serves have a live host maintaining their record, and it
flips to a terminal status when the run ends. Transcript-shape
inference belongs only where it has always lived — hydrating sessions
whose records may be orphaned — and would misread a mid-run snapshot
ending on assistant narration as a finished session, hiding the
working indicator and disarming the poll.
* fix(desktop): address review findings on steering detection and run-now matching
Steering detection additionally requires the injected-message marker
(meta.userRunSpan === 0) beside the [SYSTEM] prefix, so a person's
genuine prompt that happens to start with "[SYSTEM]" stays visible
and turn-counted. The failure direction is deliberate: an unstamped
injected reminder would merely show as a user bubble, while the
content-only check could hide a real prompt.
Run-now only follows the execution id the trigger reply itself named;
the newest-execution-for-this-schedule fallback could open a previous
run's session when the trigger failed to enqueue one.
* fix(desktop): report a failed run-now instead of confirming a start
A trigger reply without an execution means no run was enqueued (the
schedule may have been disabled or deleted since the page loaded). The
handler previously toasted "Run started" regardless and then silently
skipped the session-open polling. It now shows a destructive
"Run not started" toast, refreshes the schedule list so the row
reflects reality, and skips the polling entirely.
|
||
|
|
110138b540 |
feat(desktop): sidebar time view, Customize/Marketplace split, and schedule page UX (#13570)
* feat(desktop): split Customize into Installed and Marketplace pages
The Customize hub previously embedded a Browse section inside every tab
that had a catalog. That inlining made each tab long and buried the
catalog. Customize is now the installed inventory only (skills, MCP,
plugins, rules, hooks, tools tabs pass marketplaceVariant="installed"
to the embedded MarketplaceView; McpServersContent grew the same prop),
with an outline Marketplace button in the header.
Browsing moved to a dedicated Marketplace settings section that renders
the previously dead "directory" variant of MarketplaceView: one list
across all catalog types with type-filter chips, wrapping tag chips,
and light rules separating the filter tiers from each other and from
the results. The Clear control now renders inline at the end of the tag
row only while a tag is active, the Updated date is gone, and the
header hosts an Installed button mirroring the one on the Customize
page. Directory subheader copy: "A curated set of plugins, MCP
servers, and skills from the Cline community."
Tag and type chips wrap to new lines instead of scrolling
horizontally.
* feat(desktop): sidebar time view with sections, sort toggle, and scheduled detection
Restores the time-sorted session list as the default sidebar view, with
collapsible Pinned / Scheduled / Tasks sections (headers appear only
once something is pinned or scheduled) and the page-fill effect that
grows the fetched history window until a Show-more click makes visible
progress. Project grouping stays as the alternate mode behind a
one-click sort toggle whose icon reflects the active mode — the old
dropdown cost an extra click for a two-option choice.
Scheduled sessions are detected two ways: the hub-schedule origin
trigger in session metadata, plus a fallback that asks the hub which
session ids belong to schedule executions (list_routine_schedules,
fetched on mount and every two minutes, merged into a rolling set).
The fallback matters because locally executed scheduled runs do not
reliably stamp the trigger into session metadata — a real scheduled
session created today carried only {mode:"user"} provenance. The
scheduled clock icon now leads the row, left of the title; pin and
timestamp stay on the right.
The initial visible page grows from 10 to 30 rows so a tall sidebar
fills instead of stranding a stub of rows over empty space (history
fetches already start at 50).
The expanded sidebar's Customize row now hosts indented Installed and
Marketplace sub-tabs while a customize section is open; the active
sub-tab carries the full selected background while the parent keeps a
subtler one so the two simultaneous highlights read differently.
Also fixes the hover-card flash on click (logo card and session-row
cards): Radix HoverCardContent sits on a DismissableLayer, so a click
on the trigger registers as a pointer-down outside the card and
dismisses it, and the trigger's focus event immediately reopens it.
onPointerDownOutside preventDefault suppresses the dismissal; cards
still close on pointer leave.
* feat(desktop): schedule page row, dialog, and details UX polish
Schedule cards are now click targets: clicking anywhere on a card
outside its controls opens the details dialog (guarded via
closest("button,...") since every inline control, including the Radix
switch, renders a button element), with Enter/Space keyboard support.
The redundant eye button is gone. The remaining edit / run / pause /
delete buttons grow from the 12px icon-sm size to 28px targets with
16px icons, sized consistently with the adjacent enable toggle — the
icons use explicit size-4 classes so the Button base svg rule cannot
shrink them back.
The new/edit dialog gains breathing room between field labels and their
inputs (space-y-2 per field wrapper).
The details dialog no longer scrolls as a whole when the schedule JSON
is long: the dialog is a flex column capped at 85vh, the JSON pre
shrinks to the remaining space (min-h-0) and scrolls internally, and
the Runs tab list scrolls inside the tab the same way.
|
||
|
|
3497391c5a |
feat(desktop): customize macOS DMG install window (#13563)
* feat(desktop): add Retina DMG background tooling * feat(desktop): customize the macOS DMG layout * ci(desktop): validate DMG background assets * fix(desktop): adjust DMG Applications icon position * ci(desktop): drop redundant DMG artwork validation from publish workflow Tauri's beforeBuildCommand already runs dmg:background (with its own validation) at the start of the build/sign/notarize step, and the release/beta config overlays do not override the build section, so this step duplicated work the publish job performs anyway. PR-time coverage lives in desktop-test.yml. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
095385b985 |
fix(desktop): unblock sdk-test lint on the voice-input model picker (#13553)
The model picker renders a radiogroup of styled buttons with role=radio and aria-checked; biome's useSemanticElements flags the role as an error, which fails the sdk-test Quality Checks lint for every PR touching sdk/ or apps/ paths. Suppress with a justification — switching to input type=radio needs a restyle and belongs to the desktop settings work. |
||
|
|
491b30b806 | chore(desktop): release v0.0.17 | ||
|
|
8b046d04f9 |
feat(desktop): Customize hub, sidebar overhaul, and settings polish (#13538)
* feat(desktop): merge customization pages into a Customize hub with inline marketplace Replaces the Plugins page and the dedicated Marketplace page with a single Customize hub. Tabs: Skills, MCP, Plugins, Rules, Hooks, Tools, each with live counts. Tabs backed by a marketplace catalog render the installed items followed by an inline browsable Browse section (CLI-hub style), so installing from the catalog immediately reflects in Installed above. - Installed cards restyled to mirror the browse-card anatomy: bg-card p-4 containers, absolute top-right xs Uninstall matching Install, truncating semibold titles, primary-tinted icons, real Badge components instead of ad-hoc bordered spans, un-indented line-clamped descriptions - Rules/Hooks/Tools rows brought into the same card language; redundant intro paragraphs (duplicating the page description) removed; Tools group headers match the Installed header style with counts - Marketplace section header renamed to Browse; duplicate 'N results' row removed (the header count is the single source) - MCP embedded view now shows the full marketplace instead of installed-only * feat(desktop): overhaul sidebar sessions and navigation Sessions list: - Sort toggle removed; sessions are always grouped by project, with pinned sessions leading each group (both subsets ordered by recency). The Pinned/Scheduled/Tasks category sections and their time-mode paging machinery (page-fill effect included) are deleted - Scheduled sessions get an inline clock icon next to the pin position; pin + clock render together when both apply, and the running/unread status dot now coexists with them - One font size (text-sm) across the list: titles, timestamps, project headers, show-more buttons, empty states. sidebarText needed !text-sm because the default button size's text-base wins the twMerge conflict - Gradient fade under the Sessions header once the list scrolls, so rows fade out instead of hard-clipping - The session-detail hover card is controlled from the sidebar and closes on scroll (Radix receives no pointer events while scrolling, so it used to float over moving content) - Sidebar min resize width raised 224->260px; the per-project show-more label truncates so its nowrap text can't force rows to overflow and clip timestamps at narrow widths Navigation: - Customize replaces the Plugins/Marketplace/Hooks/Rules/Tools sidebar entries; Schedules and Customize are hidden from the expanded settings nav (their top rows cover them) but stay reachable when collapsed - The settings gear always opens General instead of resuming the last section; the Account no-op hover special case is gone - The New row highlights (aria-current) while the fresh not-yet-started task page is showing and hands off to the session row once the task starts; hitting New also focuses the prompt input via a window-event signal (lib/prompt-input-focus.ts) since the sidebar and composer sit in distant subtrees - Fixed the xs button size collapsing any icon-bearing button to 12x12 (leftover has-[>svg]:size-3 from when xs was a micro button) — this was why Uninstall buttons rendered broken next to Install * feat(desktop): polish settings pages and chat composer Models page: - The provider detail panel is always open: no X button, no empty no-selection state. It defaults to the first connected provider (falling back to the first in the catalog), which also removes the layout shift that happened when the page swapped between full-width and panel variants on selection - Fixed the list pane becoming unscrollable while the panel was open: grid items default to min-size auto, so the pane grew past its track inside the overflow-hidden grid and its ScrollArea had nothing to scroll; wrapped it in a min-h-0 min-w-0 cell - Add Provider opens a Dialog instead of swapping the page (AddProviderContent gained a dialog variant that renders only the form) - Embedded inputs (provider search, model search, detail fields) share one EMBEDDED_INPUT_CLASS stripping the Input component's own border/dark bg tint/shadow/ring, which rendered as a mismatched inner box; the model search box uses the same h-9/px-3 frame as the provider search - Model list flows with the page instead of a max-h capped inner scroller Other pages: - Account uses the shared PageFrame/PageHeader: left-aligned, text-3xl title, Sign Out in the header actions slot - Desktop notifications is one General section: header row plus the Event/Notify/Sound matrix nested in a card, so its rows no longer read as top-level peers of Dark mode; 'Available in the desktop app' label removed - Schedule page retitled from Schedules with a real description; Customize description rewritten Chat composer: - The voice dictation button only renders once a voice model is configured (Settings -> Voice); the unconfigured deep-link state is gone (prop type kept for an easy restore) |
||
|
|
8a6c6f8afe |
Redesign desktop Model Providers page and split voice input into its own settings page (#13531)
* Redesign desktop Model Providers page and split voice input into its own settings page - Group providers into Connected / Popular / All with auth-kind hints and connection status instead of per-row enable toggles - Show browser sign-in (not an API key field) for OAuth providers, with a collapsed manual-key escape hatch where supported, plus explicit Connect / Disconnect / Sign out actions - Move voice input to a dedicated Settings > Voice page that only offers connected transcription-capable providers, preselects a default model (streaming preferred), and stays disabled in the sidebar until a provider is connected Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Show native tooltip on the disabled Voice settings nav item Disabled buttons drop pointer events, so the 'connect a model provider' hint moves to a wrapping span for the browser tooltip to render. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Drop letter avatars and gray provider ids from provider rows and voice chips Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Drop model counts from provider list rows Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Rename provider Connected status to Configured and drop the green styling A settings entry is configuration, not a live connection; neutral gray text avoids implying an active link, since the user still picks which configured provider to use per chat. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Resync provider catalog from disk when a settings save fails Connect/disconnect/credential edits update the list optimistically; a failed save now reloads the catalog instead of leaving the optimistic state (and the view's module cache) claiming a configuration that was never persisted. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Rename oauthProvider test fixture to dodge CodeQL name heuristic CodeQL's clear-text-storage query flags any identifier matching 'oauth' as a credential source and traced the fixture's provider id into the favorite-models localStorage write, which stores only provider/model id strings. Renaming the fixture removes the false-positive source. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Guard catalog reloads against races and resync detail drafts on failed saves Optimistic provider mutations now bump a generation that discards any in-flight catalog response, so a failed-save recovery reload can't overwrite a newer edit with an older disk snapshot. The recovery also remounts the provider detail panel via a reset token so its local field drafts reflect the reloaded on-disk state instead of unpersisted edits or an optimistically cleared disconnect. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Fix failed-save recovery ordering and retry superseded reloads Remount the provider detail only after the authoritative catalog reload lands, so its drafts re-seed from disk state rather than the optimistic values that failed to persist. When a concurrent edit supersedes the recovery's in-flight response, retry the reload (bounded) instead of dropping it, since that edit performs no reload of its own. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
4f5f238407 |
Desktop app: organize sidebar sessions into Pinned, Scheduled, and Tasks sections (#13528)
* Add Pinned/Scheduled/Tasks categories to desktop app sidebar Replace the Schedules and Favorites filter-menu options with visible collapsible category sections in the session sidebar, and rename the Favorite action to Pin across the sidebar and sessions view. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Grow full history window when Tasks show-more outpaces loaded tasks loadMoreSessions treats its argument as a limit on all sessions, but the Tasks show-more count only tracks Task rows, so once pinned/scheduled rows pushed the loaded total past the requested count the call no-oped and clicks went dead. Grow the whole history window via loadOlderSessions instead, and only when the loaded tasks cannot fill the next page. Addresses Greptile review on #13528. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Auto-fill the Tasks page instead of fetching once per show-more click A single 50-session window growth can consist entirely of pinned or scheduled sessions, leaving a show-more click with no visible Tasks progress. Replace the one-shot fetch with a page-fill effect that keeps growing the history window until the requested Tasks page fills or history runs out. Addresses the follow-up Greptile review on #13528. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Halt page-fill retries after a failed history fetch A failed fetch leaves the task count and has-more state unchanged, which are exactly the conditions the page-fill effect fires on, so one failing request would retry and re-toast forever. Halt the effect after a failure and let the next explicit show-more click retry. Addresses the third Greptile review on #13528. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
8f69880ac4 |
Hide Channels and Agents sections from desktop app sidebar (#13527)
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
a0c341e93c |
desktop: sidebar navigation cleanup with New/Schedule/Customize rows and dialog-based search (#13533)
* desktop: clean up sidebar navigation chrome - Give New Task its own full-width labeled row below the logo row instead of an ambiguous icon next to the agenda toggle - Wire the New Task row to the home action so starting a new task clearly takes you home (the logo still works as a fallback) - Swap back/forward chevrons for browser-style arrow icons and bump their size Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * desktop: sidebar New/Schedule/Customize rows and always-visible search - Stack New (plus icon), Schedule, and Customize as full-width labeled rows below the logo; whole row highlights on hover via sidebarItem - New starts a fresh task (home), Schedule opens Settings > Schedules, Customize opens the Customizations sections (Plugins first) - Show the session search bar permanently above the sessions list instead of hiding it behind a search icon toggle Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * desktop: move session search into a dialog behind a logo-row icon - Replace the inline sidebar search bar with a search icon in the logo row that opens a cmdk command dialog listing sessions - Selecting a result opens that session and closes the dialog - Remove the agenda/tasks toggle the icon replaces, along with the now-unreachable sidebar Agenda panel (the welcome screen still surfaces agenda tasks) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * desktop: load full session history when the search dialog opens Addresses Greptile review on #13533: the dialog only searched the currently loaded history batch, so older unloaded sessions could not be found. Opening search now kicks off loadAllSessions() (the hook's purpose-built global-search loader), and the empty state reads 'Searching older sessions...' while more history is streaming in. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
83b2588c9c |
Disable the agent todo tool and hide the Agenda UI in the desktop app (#13530)
* remove todo tool and Agenda UI, keep schedule-only tasks tool Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * chore: biome formatting fixes Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * restore agenda backend; disable todo kind behind a flag instead of deleting Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * keep agenda automation pump idle while the todo tool is disabled Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * remove todo tool and Agenda UI altogether (revert the disable-flag hybrid) Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * restore all agenda code to main state Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * disable agent todo tool and hide Agenda UI behind flags Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
6e09e81a79 |
Add suggested schedule templates to the desktop Schedules page (#13529)
* Add suggested schedule templates to desktop Schedules page Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Fix unreadable selected text in inputs caused by selection utility conflict Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Restyle Suggested section label as small gray uppercase Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * Hide suggested schedule cards that match an existing schedule name Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
6cb653a362 | chore(desktop): release v0.0.16 | ||
|
|
fb58e340a2 |
Add feature flags to the desktop app (#13289)
* Add feature flags to the app * React to account updates * Address comments * use a per-app file |
||
|
|
fb60f9e5fd | chore(desktop): release v0.0.15 | ||
|
|
05da55857a | feat(desktop): reskin first-run onboarding (#13441) | ||
|
|
35583ee8bb |
feat(desktop): interactive welcome hero graphic (#13399)
* feat(desktop): add interactive welcome hero * feat(desktop): support composable welcome hero variants |
||
|
|
eef7958cad |
fix(core): report truthful session status so desktop checkpoint restore stops wedging (#13418)
* fix(core): keep hub session status truthful across queue-drained turns Queue-drained turns settle only through the event stream, but the hub runtime host mistranslated their lifecycle in two ways: - session.updated events carrying only a snapshot (persistence updates) defaulted the projected status to "running". When one trailed the final idle update after a turn, clients that track busy state from status events (the desktop sidecar's workspace restore gate) stayed busy forever. Use the snapshot's real status and emit nothing when neither source reports one. - the per-run agent.done dedup was only reset by run.started, which the daemon-side queue drain never publishes, so a drained turn's done was swallowed as a duplicate of the previous turn's. Reset the dedup on session.pending_prompt_submitted, and suppress stale run.completed events that land inside a drained turn's window so they can neither emit a phantom done nor consume the drained turn's dedup slot. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * test(desktop): cover restore unlock after an event-settled queued turn Exports the sidecar's core-session event handler so the queued-turn lifecycle (busy via status events, cleared by the done agent event, restore allowed afterwards) is testable end-to-end. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * fix(core): start interactive sessions without a prompt as idle The runtime host reported every new session as "running" until its first turn ended. Interactive hosts (the desktop app) start sessions with no prompt and dispatch turns through separate send calls, so a created-but-never-prompted session stayed "running" forever — wedging clients that gate workspace operations (checkpoint restore, message edit) on active turns. Interactive no-prompt starts now begin idle, start emits the session's actual status (resumed sessions no longer masquerade as running), and markTurn* transitions keep tracking in-memory status for lazily persisted sessions so the first turn still reports running -> idle. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * style: format hub-runtime-host test filter Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * refactor: drop the drained-turn done bookkeeping, keep the minimal fix The stuck restore is fully explained by the two status defects (fabricated "running" from snapshot-only session.updated events, and never-prompted interactive sessions reporting "running"). The done-dedup machinery for queue-drained turns addressed a separate cosmetic gap (queued turns emit no chat_done, pre-existing) and required fragile run-window heuristics, so it is removed to keep this change reviewable. Sidecar test now settles the queued turn through the status event, matching the shipped mechanism. Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> * docs(sdk): document the truthful session-status contract --------- Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com> |
||
|
|
e6f4d5fef7 | feat(desktop): refresh app icons and branding (#13400) | ||
|
|
7903c76812 |
feat(desktop): recommended-feed badges and descriptions in provider settings (#13416)
* feat(ui): sectioned model picker support in SearchCombobox
Adds option sections with headers, badges (NEW/Free pills), keyboard
navigation (arrows/Home/End/Enter with active-row tracking and
aria-activedescendant), substring match highlighting, a configurable
panel width, a trigger chevron, and a cleaner borderless search row.
All additions are backwards compatible; bumps @cline/ui to
0.2.0-next.6.
* feat(desktop): recommended and free model tiers in the composer picker
The composer's model selector showed raw provider/model ids and listed
the entire catalog alphabetized by id. It now labels providers and
models by display name and, for the cline provider, leads with the
Recommended and Free tiers from the recommended-models feed (NEW/Free
badges, descriptions) ahead of an All models section — matching the
CLI's featured picker and the kanban selector. cline-pass gets
Subscribed/Free tiers. A new list_cline_recommended_models sidecar
command exposes @cline/core's fetchClineRecommendedModels (display-ready
names, bundled offline fallback); feed ids resolve against the catalog
with a unique-slug fallback for Vercel/OpenRouter alias spellings, and
unresolvable entries are dropped rather than rendered unselectable.
* fix(desktop): widen the provider trigger for display names
Provider labels are now display names (e.g. "Cline Usage-Billing"),
which truncated badly at max-w-28.
* chore(desktop): drop unused featured-models test helper
* style(desktop): align workspace/branch picker search rows with the model picker
The composer's workspace/branch popover and the welcome screen's
workspace and branch pickers used a boxed inner search shell that now
clashed with the model picker's borderless search row sitting next to
them. Behavior unchanged.
* feat(ui): center the selected option when SearchCombobox opens
Opening a long list previously scrolled the selection just into view at
the panel edge; it now lands centered, and keyboard/hover navigation
falls back to minimal nearest-edge scrolling.
* style(desktop): picker row contrast, transparent search fields, centered open
The workspace/branch pickers' rows had a nearly invisible
surface-hover-lighter hover; rows now hover with surface-hover and mark
the current entry with the accent background plus check, matching the
model picker. The search inputs drop the Input base class's
dark:bg-input/30 tint that rendered a gray box inside the panel in dark
mode. Opening a picker now centers the current workspace/branch via a
shared scroll helper instead of starting at the top of the list.
* fix(ui): visible option hover/selected states and no scroll-jump on hover
The option row stacked bg-transparent with the conditional state
backgrounds; at equal specificity the later-sorted bg-transparent
utility won, so hover/selected rows rendered with no background at all.
The background classes are now mutually exclusive.
Mouse-driven active-row changes also reused the keyboard scroll-into-
view effect: hovering a row at the panel edge scrolled it into view,
which moved the list under the cursor and re-triggered hover — an
endless jump. Scroll mode is now per-source: center on open, nearest
for keyboard/typing, none for hover.
* fix(desktop): show only subscribed and free tiers in the cline-pass picker
The ClinePass offer is exactly the feed's subscribed + free tiers, but
stale bundled/cached catalog entries (e.g. a nemotron model) leaked
into an "All models" tier. Match the CLI's featured picker: hide
catalog leftovers, and only fall back to the full catalog when the
subscribed bucket is empty so a subscriber is never limited to free
models offline.
* fix(ui/desktop): strengthen the selected-row highlight in light mode
The selected row used the semantic accent surface (violet step 3),
which is nearly white in light mode. SearchCombobox and the desktop
workspace/branch pickers now highlight the selected/current row with
accent step 4 (with a fallback to --accent), which reads clearly in
both themes without touching the shared --accent token that shadcn
hover states depend on.
* fix(desktop): fit full provider display names in the composer trigger
"Cline Usage-Billing" — the default provider — truncated to
"Cline Usage-Bi…" at max-w-36; the trigger now allows up to max-w-56,
which fits the longest built-in provider names.
* style(ui/desktop): animate picker panels open like the shadcn dropdowns
The thinking-effort Select (shadcn/Radix) animates open while the
model/provider/workspace/branch pickers popped in instantly. All picker
panels now share the same open treatment — 150ms fade + slight zoom,
sliding from the trigger side. SearchCombobox uses a self-contained CSS
keyframe (consumers may not ship tw-animate-css); the desktop's custom
panels use the app's tw-animate utilities. Both respect
prefers-reduced-motion.
* chore(desktop): drop stale eslint-disable comments in picker search rows
This repo lints with biome; the jsx-a11y/no-autofocus disables were
inert leftovers. Flagged in review.
* refactor(core/desktop): stamp recommended-feed tiers onto ProviderModel in the SDK
Review feedback on the composer picker: tier joining should live where
the SDK serves model lists so each client doesn't fetch and join the
recommended-models feed itself (the CLI and now the desktop each did).
ProviderModel gains description and featured ({tier, rank, tags});
getLocalProviderModels overlays the feed's recommended/free tiers onto
cline models and subscribed/free onto cline-pass via
applyClineFeaturedModels, matching feed ids through the
Vercel/OpenRouter alias rules. The feed access is a new cached wrapper
(getCachedClineRecommendedModels, 5-minute TTL, in-flight dedupe) —
this path runs on every picker open, and the bundled offline fallback
is cached too so offline users don't re-pay the 5s timeout per list.
The desktop webview now reads tiers straight off the models: the
list_cline_recommended_models sidecar command, the webview feed fetch,
and its unique-slug alias matching are all deleted. toProviderModel
also carries ModelInfo.description generally.
* feat(desktop): recommended-feed badges and descriptions in provider settings
Review suggestion on #13410: the provider settings page has room for
more model detail than the composer's picker. The cline/cline-pass
provider cards now refresh their model list through
list_provider_models (the catalog snapshot deliberately skips the
recommended-feed overlay so the startup catalog fetch never blocks on
the feed) and render Recommended/Free tier badges plus feed tags (NEW)
next to the model name, with the model description underneath. The
refreshed list also surfaces the live entries instead of the bundled
snapshot.
* fix(ui): hand focus back to the combobox trigger on selection, close on Tab
Selecting an option (Enter or click) unmounted the focused search input
without a new focus target, dropping keyboard users' focus to <body> —
only Escape restored it. And since the search input is the panel's only
tabbable element, Tab always moved focus outside the component while
leaving the popup open behind the new focus target.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(desktop): keep the composer model selection inside the picker's visible offer
The active/remembered model was validated against the provider's full
catalog while the picker can intentionally hide models (the ClinePass
offer is exactly its subscribed/free tiers), so a stale remembered model
could become the selection while being absent from the dropdown.
Remembered and default selections (including on provider switch) now
resolve against the picker's visible options, and an explicitly
configured model that falls outside the offer stays active but is
surfaced under a 'Current model' section so the selection is always
visible and re-selectable.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(desktop): scope the settings featured model list to its provider and revision
The fetched featured list was unscoped component state: switching
between cline and cline-pass reused the component instance, so the
previous provider's models stayed visible while the new request was
pending (or forever, when it failed), and the retained copy shadowed
later provider.modelList updates — adding a second custom model
submitted the stale list as the complete configuration and dropped the
first addition.
The fetched list now only applies to the provider and modelList
revision it was fetched for (falling back to the catalog snapshot
otherwise and refetching on membership changes), and add-model submits
the union of the displayed and configured ids so an update can never
silently unconfigure existing entries.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* feat(core): stamp featured tiers onto the provider catalog synchronously
listLocalProviders deliberately skipped the feed overlay so the catalog
never blocks on the network — but that left the composer's very first
picker open after a cold boot rendering an untiered flat list until the
per-provider fetch landed. Blocking was never required: stamp tiers from
a synchronous peek at data already in memory (the cached live feed when
fresh, else the bundled fallback, whose recommended ids resolve against
the bundled cline catalog). The per-provider model-list path still
refreshes with live feed data moments later.
* fix(core): harden featured-tier matching and the feed cache reset
Review findings on the tier overlay:
Vendor-prefix mismatches now match by unambiguous id slug (two-pass, so
a catalog carrying both spellings of a model stamps one row, and a slug
shared by two feed entries stamps nothing) — the bundled fallback feed's
vendor-prefixed ids can otherwise miss cline-free/-prefixed catalog
entries, leaving them untiered in degraded mode.
resetClineRecommendedModelsCacheForTests now bumps a generation so an
in-flight feed request resolving after a reset cannot repopulate the
cache it just cleared.
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
|