* 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>
* 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>
* chore(vscode): remove per-tool MCP auto-approve checkboxes from webview
MCP auto-approval is now governed solely by the global 'Use MCP servers'
toggle; the SDK approval path (shared with the CLI and desktop app) has no
per-tool granularity, so the per-tool and 'Auto-approve all tools'
checkboxes were no-ops that implied control that no longer exists. Remove
them from the MCP settings view and chat tool rows. The autoApprove arrays
in cline_mcp_settings.json and the toggleToolAutoApprove RPC are left
intact for the legacy extension.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* chore(vscode): hide per-tool MCP auto-approve checkboxes behind a flag
Keep the checkbox components, handlers, and RPC plumbing intact but gate
rendering behind SHOW_MCP_PER_TOOL_AUTO_APPROVE=false: the SDK approval
path (shared with the CLI and desktop app) is all-or-nothing via the
global 'Use MCP servers' toggle, so the per-tool checkboxes were no-ops.
Flip the flag back on if the SDK gains per-tool approval granularity.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(core): prevent search_codebase from crashing the process on giant single-line files
searchWithRipgrep buffered all of rg's --json stdout into one string. Each
JSON event embeds the full text of the matched line (--max-columns is
ignored in JSON mode), so searching a directory of serialized trace dumps
(single-line multi-hundred-MB JSON files) accumulated gigabytes of stdout
until string concatenation threw RangeError: Out of memory inside the
stream data handler. That throw is outside the tool's try/catch, so it
escalated to an uncaughtException and killed the CLI/hub daemon.
Parse rg's JSON events incrementally line by line, drop events larger
than 256KB, truncate matched/context lines to MAX_LINE_CHARS, and stop
reading once maxResults is reached. The fallback regex scan now skips
files larger than 10MB (reporting the skip count) and truncates its
context lines the same way.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* simplify search_codebase crash fix to a minimal diff
Replace the incremental JSON-event parser with three small guards: stop
buffering rg stdout past 10MB, drop the trailing partial event before
parsing, and slice fallback context lines to MAX_LINE_CHARS. Drops the
fallback file-size skip and skip-count reporting.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
The /shutdown handler queued teardown on a microtask, which runs before
the event loop's write phase, so the daemon could process.exit() before
the accepted 202 was handed to the socket. Unix masked it (uv_try_write
lands small loopback writes synchronously); Windows has no such fast
path and lost the race regularly — the recurring shutdown.e2e.test.ts
'socket hang up' failures on windows-latest. Start teardown from the
response's write callback instead, with an idempotent 1s fallback so a
client that vanishes mid-write cannot strand the daemon, and send
Connection: close so the client gets a FIN rather than an abort.
Since the flakiness this compensated for is fixed at the source, restore
maxWorkers: 2 for the Windows core suite (serializing it cost ~3 min of
CI per run), and raise the e2e daemon discovery hang guard 10s→30s —
it guards against hangs, not runner speed.
ci-node-smoke.ts installs the packed SDK tarballs with a plain npm
install in a fresh temp dir, where the repo root package.json overrides
do not apply. When @sap-cloud-sdk 4.9.0 shipped (2026-08-24) it broke
@sap-ai-sdk/ai-api 2.14.0 (via @jerome-benoit/sap-ai-provider in
@cline/llms) with ERR_PACKAGE_PATH_NOT_EXPORTED, failing the smoke step
on every PR even though the root already pins @sap-cloud-sdk/* to 4.6.0.
Copy the root overrides block into the generated sandbox package.json
so the smoke install resolves the same pinned versions as the repo and
future third-party releases cannot break it independently.
* fix(hub): cap hub-events db size so it can't fill the disk
Row/time retention alone didn't bound disk usage: envelopes carrying
full session snapshots reach hundreds of KB each, so retained rows
could total tens of GB, sweeps only ran hourly, and DELETE never
shrinks a SQLite file. Enforce a 64 MiB size budget in prune() (oldest
rows first, VACUUM to return the space), and also prune after every
16 MiB appended so bursts can't outrun the hourly timer.
Fixes#13505
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(hub): tolerate VACUUM failure on a full disk
VACUUM needs scratch space and can fail in exactly the state a
ballooned event log causes. The byte-budget deletes already bound live
data, so swallow the error and let the next sweep retry the reclaim
instead of aborting startup pruning and disabling the durable log.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(hub): count the size budget in UTF-8 bytes, not characters
envelopeJson.length (UTF-16 units) and SQLite LENGTH() (characters)
undercount multibyte text by up to 3x, which could leave a CJK-heavy
log settled above budget and re-running VACUUM every sweep. Use
Buffer.byteLength and LENGTH(CAST(... AS BLOB)) instead.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(vscode): resolve hook workspace identity from the window, not shared global state
Hook discovery, hook cwd selection, and the workspaceRoots metadata passed
to hook scripts all read the workspaceRoots/primaryRootIndex global state
keys. Global state lives in ~/.cline and is shared by every Cline instance
(all VS Code windows, the CLI, the JetBrains plugin), and nothing writes
these keys anymore, so hooks resolved against whatever project some other
or older instance last recorded. With a second window open on another
project, a workspace's .clinerules/hooks scripts were never discovered.
Resolve workspace roots via a single guarded helper backed by
HostProvider.workspace.getWorkspacePaths() (in-process, window-scoped,
same as refreshHooks): blank paths are filtered, a host-bridge failure
degrades to no workspace roots instead of silently disabling global hooks
or skipping blocking PreToolUse guards, and one resolution is threaded
through hooks-dir discovery, cache misses, cwd selection, and hook input
metadata so they can't disagree (previously up to four host lookups per
hook execution — real gRPC round trips in the standalone host). Roots and
hooks dirs are matched on whole path segments with the longest root
winning, so prefix-sharing or nested workspace roots resolve to the right
project. The adapter creates the runner once per event and skips no-op
runners, making creation the single resolution point; the separate
hasHook/getHookInfo checks are removed. The dead workspaceRoots and
primaryRootIndex state keys are dropped, and the four hand-rolled
HostProvider.workspace test stubs are consolidated into one shared
helper.
* test(vscode): add e2e coverage for workspace-scoped hook discovery
Boots real VS Code with the packaged extension against the workspace
fixture, sends a prompt, and asserts the fixture's UserPromptSubmit hook
was discovered from the open window's workspace, executed with that
workspace root as its cwd, and received the same root in its
workspaceRoots input — the end-to-end contract the hook workspace
identity fix establishes.
* test(vscode): isolate the e2e hook fixture from the shared workspace
The UserPromptSubmit fixture hook lived in the shared e2e workspace, so
every prompt-sending spec executed it (hooksEnabled defaults to true) —
and its cold PowerShell spawn on Windows pushed chat.test.ts past the
5s expect timeout. hooks.test.ts now overrides workspaceDir to a
dedicated workspace-hooks fixture, so only the hooks spec pays the hook
spawn.
The $4.99 first-month promo is ending, so the CLI's first-launch "Try ClinePass" dialog should no longer advertise it. Also drops the leftover CLI_PROMO_CODE plumbing, which has been an empty string since the promo-code flow was removed.
* fix(vscode): honor MCP auto-approve settings for SDK tool calls
The SDK extension required both the global 'Use MCP servers' auto-approve
toggle AND each tool's per-tool autoApprove flag before silently approving
an MCP call, while the legacy extension treated them as either/or. Restore
the legacy OR semantics so toggling MCP auto-approve works again.
Also key toolPolicies by the registered SDK tool name (via
defaultMcpToolNameTransform, now exported from @cline/core) instead of raw
server__tool. Servers whose names contain sanitized characters (e.g.
marketplace names like github.com/user/repo) or exceed 64 chars produced
policy keys that never matched the registered tool, so those MCP tools ran
without any approval gate; the live auto-approve lookup now re-applies the
transform instead of string-splitting the name.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* Revert "fix(vscode): honor MCP auto-approve settings for SDK tool calls"
This reverts commit 86c568fbba.
* fix(vscode): auto-approve all MCP tool calls when the MCP toggle is on
The SDK extension only auto-approved an MCP call when the global 'Use MCP
servers' auto-approve toggle AND that tool's per-tool autoApprove flag were
both set, so toggling MCP auto-approve appeared to do nothing and users had
to opt in each tool individually. The toggle alone now governs all MCP
tools; the per-tool flag is no longer consulted.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
The task.completed fallback lived only inside shutdownSession, but
stopSession/dispose route interactive sessions with a terminal reported
status through releaseSessionRuntime, which never emitted. Truthful
session-status reporting (shipped in 4.1.11) re-routed a large share of
interactive stops onto that branch and silently dropped the event.
Route the emission through a single choke point,
emitTaskCompletedOnTeardown, called from both shutdownSession and
releaseSessionRuntime. The completion criterion no longer reads
session.status: interactive sessions use the recorded final-turn
outcome (lastInteractiveTurnFinishReason), non-interactive sessions
keep the existing input.status === "completed" logic. A new
taskCompletedEmitted flag (also set by the submit_and_exit observer)
enforces exactly one task.completed per session. failSession now
records the errored final turn so a stale "completed" from an earlier
turn can never leak into the teardown emission. Telemetry only; no
user-facing behavior changes.
Four consecutive SDK publish runs failed on windows-latest, each on a
different test, all of them plain timeouts: two @cline/shared SQLite
tests at the 5s vitest default, core's bash executor at 10s, and the hub
singleton endpoint test at 10s. The 2-core Windows runner spawns forks
and takes SQLite locks slowly enough to blow those budgets under load.
These timeouts guard against hangs; they are not timing assertions (the
one suite that does assert elapsed time, shutdown.e2e, was fixed by
removing file-level parallelism instead). Raise core to 20s and give
@cline/shared an explicit 15s in place of the inherited 5s default.
singleton.e2e.test.ts (added in #13468) spawns real daemons and runs for
~15s. Vitest's default file parallelism let it run alongside
shutdown.e2e.test.ts, whose assertions are wall-clock bound: discovery
within 10s, exit within 5s, and a 2s shutdown watchdog. On the 2-core
windows-latest runner that contention alone broke those budgets, failing
the shutdown test two different ways across runs — once never observing
discovery, once with the daemon forced to exit before its HTTP 202
flushed (socket hang up). The test passed on Windows before #13468 and
has failed every SDK publish run since.
* fix(core): seed tools capability when custom model capabilities are synthesized from boolean flags
For a models.json entry with no explicit capabilities list, toStoredModelInfo
synthesized a capability array purely from boolean convenience flags (e.g.
supportsReasoning: true -> ["reasoning"]). modelSupportsToolCalling fails open
only for a missing or empty list, so the synthesized non-empty list read as an
authoritative denial and silently stripped every tool definition from requests
to custom OpenAI-compatible models (#13463).
Seed "tools" whenever the list was not explicitly authored and the boolean
projections made it non-empty, preserving the fail-open contract. Explicitly
authored capability lists remain authoritative and can still disable tools.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* test(core): cover stale catalog capability overrides
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix: treat stored capability lists as non-authoritative for tool calling
The hasExplicitCapabilities guard still let two producers of tool-less
lists through:
- The VS Code legacy-override migration (legacyModelInfoToOverrides)
persists explicit partial lists like ["prompt-cache"] into models.json
for custom OpenAI-compatible models, which then read as an authoritative
"cannot call tools" and drop every tool - same symptom as #13463.
- Any hand- or UI-authored partial list on a non-catalog model.
Stored entries and user-authored provider metadata have no way to declare
"cannot call tools" (there is no supportsTools field, and every writer
that authors a full list includes "tools"), so seed "tools" into any
non-empty list for a language model. Only generated catalog capabilities
remain authoritative - a genuine no-tools catalog model stays that way -
and non-language models (e.g. image generation) never gain a tools claim.
Also make legacyModelInfoToOverrides write "tools" into the arrays it
fabricates, matching the providers.json migration, so models.json stops
being poisoned for older readers.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* feat(hub): add drain and upgrade commands with replay support
* handles disconnection
* feat(hub): wire bot profiles, drain, and durable event/run-queue into the live transport
Completes the wiring the previous commits' primitives needed:
HubServerTransport gains isDraining(), hub.drain/hub.status/profile.get
command handling, and replayEventsAfter() (backed by the durable event
log), plus the sequence/sinceSequence wire types they depend on in
shared/hub.ts. run-queue-handlers.ts reads the active bot profile's
plugin roots when executing durable runs.
Also adds hub/profiles/: profile.json (identity/rules/plugins) ->
system prompt composition, --profile / CLINE_HUB_BOT_PROFILE
resolution, and the bundled cline-dad profile with its
cline_hub_support read-only diagnostics tool.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Revert "feat(hub): wire bot profiles, drain, and durable event/run-queue into the live transport"
This reverts commit 6696d5d202.
* fix(hub): dedupe replayed events by eventId, not just sequence
HubEventLogStore.append() returns a new envelope stamped with a
sequence rather than mutating the input, so a pending approval
re-issued sequence-less by subscribe() (it predates any durable-log
append) and its later sequence-stamped copy from the durable log are
two different objects carrying the same eventId. The replay-then-live
buffer in browser-websocket.ts only deduped by sequence, so the
sequence-less copy's guard never tripped and it was delivered a second
time when the buffer flushed after replay.
Track delivered eventIds alongside the sequence cursor; eventId
survives the append/stamp round-trip unchanged, so this dedupes the
exact-same logical event regardless of which copy arrives first.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(hub): wire drain, durable event log, and run queue into the live transport
CI on this branch failed bun run build:sdk: browser-websocket.ts,
client/index.ts, and hub-websocket-server.ts (already on this branch)
reference sequence/sinceSequence, HubServerTransport.isDraining(), and
the "hub.drain" command — but the commit that reverted bot profiles
out of this branch also reverted this wiring, since it shared a commit
with the profiles work. That wiring is a hub concern, not a
bot-profiles one; split it back out.
- shared/hub.ts: sequence/sinceSequence types, run.enqueue/run.list/
hub.drain/hub.status/stream.replay capability, command, and event
names. profile.get intentionally excluded — stays bot-profiles-only.
- context.ts: isDraining() on HubTransportContext. botProfile field
intentionally excluded.
- hub-server-transport.ts: eventLog/runQueue fields and start/stop
lifecycle, publish() appends to the durable log, handleCommand cases
for run.enqueue/run.list/hub.drain/hub.status, drain-refusal check,
replayEventsAfter()/lastEventSequence(). startBotProfile()/
startHubSupportTool() and the profile.get case intentionally
excluded.
- run-queue-handlers.ts: added without handleProfileGet (needs
ctx.botProfile, which doesn't exist here).
- hub-upgrades.test.ts: added without its two bot-profile-injection
tests (they need a resolved bot profile to assert against).
Verified bun run build:sdk exits 0 (the exact CI command) and
bunx vitest run src/hub passes (311/312; the one failure is the
same pre-existing environment-timing flake already present before
this change).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(hub): export instance-lock, event-log, and run-queue from the hub barrel
These landed as internal modules only; hub-server-transport.ts and
hub-websocket-server.ts import them by direct path, but nothing
re-exported them from the public @cline/core/hub surface the way
sibling discovery/server modules already are.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(hub): wire the instance lock into the daemon entry point
The singleton lock (discovery/instance-lock.ts) and its consumption in
startHubWebSocketServer/ensureHubWebSocketServer were already on this
branch, but the daemon entry point's own half was not: retrying a bind
when a retiring predecessor still holds the lock, and exiting with a
distinct code (3) instead of the generic fatal path when a live Hub
already owns the data directory. Without this, a daemon racing a
retiring predecessor could fail outright instead of waiting the lock
out, and losing the singleton race looked identical to a crash.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(hub): address drain/upgrade review findings (#13478)
- cline hub upgrade: check idleness at least once (--wait 0 works), reject
non-numeric --wait, and un-drain on every abort path so an aborted
upgrade can never leave the hub refusing new work
- add cline hub drain --off and the off query param to requestHubDrain so
POST /drain?off is reachable from shipped code
- HubEventLogStore/HubRunQueue: WAL journal mode + busy_timeout, and stamp
sequences from lastInsertRowid instead of SELECT MAX(sequence)
- HubInstanceLock.acquire: degrade to an unheld lock when SQLite is
unavailable instead of refusing hub startup; only BUSY/LOCKED still
raises HubLockHeldError
- ensureHubWebSocketServer: retire an unusable discovered hub through the
shared retireDiscoveredHub (busy hubs are attached to, drain precedes
shutdown, discovery cleared only when the hub actually retired)
- replay adapter: advance the cursor past eventId-deduped events, cap
replay pages, stop when the cursor stalls, and drop the dedupe set after
the buffered flush so it cannot grow for the socket lifetime
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* fix(hub): derive the singleton e2e challenger cwd portably
The challenger's working directory was derived by round-tripping the
discovery path through a file: URL and stripping the last pathname
segment. On Windows that yields a POSIX-style '/C:/...' path, which is
not a valid spawn cwd, so the spawn fails ENOENT before the singleton
lock is ever contested and the Windows SDK test job goes red.
The data dir is simply the discovery file's parent: use dirname().
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
The PublishNightly environment gained required reviewers, so each cron
run parked on approval, held the workflow's concurrency group, and
silently cancelled every scheduled run queued behind it. 20 consecutive
scheduled nightlies died this way between 2026-07-31 and 2026-08-21;
the only nightlies that shipped in that window were manual dispatches.
Drop the cron rather than leave a trigger that cannot succeed unattended.
* fix telemetry session propagation
* feat telemetry client version metadata
* fix(core): address Langfuse review feedback — hub client identity + delegated agent session grouping (#13475)
* fix(core): rebuild hub session client identity from request headers
Hub-backed sessions do not transport extensionContext (it is local-only),
so the daemon's runtime built traces without the clientName/clientVersion
metadata even though the hub client bakes X-CLIENT-TYPE / X-CLIENT-VERSION
into the session's provider headers. Reconstruct extensionContext.client
from those headers during local runtime bootstrap so hub-backed Langfuse
traces carry the same client identity as local runtimes, and the daemon's
header re-resolution stops clobbering the original X-CLIENT-TYPE.
* fix(core): propagate parent distinctId/sessionId to delegated agents
Delegated agents (spawned sub-agents, configured agents, teammates) were
built without distinctId and sessionId, so their Langfuse traces had no
userId or sessionId and did not group with the parent user or session.
Thread the host-resolved distinctId through RuntimeBuilderInput and the
root sessionId through the delegated-agent config provider, and copy both
onto the delegated AgentConfig.
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* Treat an empty preserved capability list as unspecified when seeding tools
toSdkModelInfo guarded the tools seeding with a strict
preservedCapabilities === undefined check, but modelHasCapability —
the runtime's own reader — treats undefined AND length === 0 as
"unspecified". A custom OpenAI-Compatible model whose stored
capabilities field is a defined-but-empty array (a config carried over
from before the field existed, or one round-tripped through a boundary
that defaults it to []) skipped the seeding; the first boolean
projection to run afterwards (e.g. supportsReasoning) then populated
the array, the runtime gate read the non-empty, tool-less list as
authoritative, and every tool definition was silently dropped from the
session (#13463).
The guard now covers the empty array too, matching the reader's
unspecified semantics.
* test: satisfy the store's isModelInfo gate so the empty-capabilities case actually reaches knownModels
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: yzxcj797 <yzxcj797@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(vscode): enforce enterprise MCP controls on the Customize marketplace
The unified Customize marketplace replaced the old MCP marketplace
without carrying over enterprise remote-config enforcement: the catalog
RPC returned every MCP entry and installs were never policy-checked,
so orgs with mcpMarketplaceEnabled=false or an allowedMCPServers
allowlist saw (and could install) all marketplace MCP servers.
- Filter MCP entries out of getMarketplaceCatalog when the marketplace
is disabled, and restrict entries to the allowlist when configured
(matching entry id, display name, installed server name, or source
repo URL, mirroring legacy GitHub-URL allowlist ids)
- Reject installMarketplaceEntry requests that violate the policy
- Map the published catalog's repo/homepage fields onto
sourceUrl/homepageUrl so URL-based allowlists can match
- Update the enterprise MCP server controls docs
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* refactor: simplify MCP marketplace policy enforcement
Fold the policy check into marketplace-helpers, drop the dedicated
test suite, and trim the docs edit to the strictly necessary line.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* chore(deps): update Langfuse packages and bump app versions
Update @langfuse/otel to v5.10.1 and add @langfuse/vercel-ai-sdk v5.9.1 for improved observability with Vercel AI SDK.
Bump versions for @cline/code to 0.0.14 and @cline/ui to 0.2.0-next.6, updated via bun.lock.
Other Changes:
Added optional userId to AgentRuntimeConfig.
Propagated userId, sessionId, conversationId, runId, iteration, provider, and model context into AI SDK telemetry.
Added AI SDK 7 runtimeContext with explicit includeRuntimeContext.
Added stable OTEL_SERVICE_NAME=cline-sdk.
Added runtime metadata assertions in agent tests.
* add taskId
* Revert "add taskId"
This reverts commit f20d31d96d.
* 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>
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>