Commit Graph
519 Commits
Author SHA1 Message Date
Mikołaj Kondratek 3453880297 fix(vscode): keep error details in production logs (#14693)
* fix(vscode): keep error details in production logs

Logger dropped every extra argument unless IS_DEV was true, and production
bundles build IS_DEV as false, so lines like 'Cline OAuth login failed:' lost
the error. Production now appends error messages (with cause chain), numbers
and booleans; strings and objects stay dropped because call sites pass OAuth
URLs, terminal and file contents and settings. Verbose mode now serializes
Errors as stacks instead of {} and survives circular arguments. The
'Opening browser' line logs only origin and path.

* fix(vscode): reduce URLs in production log output to their origin

Error messages printed in production can embed URLs with credentials in
the query, path or userinfo, such as the expanded MCP server URL in the
invalid-URL error or Bun's URL parse error echoing its input. Reduce
every URL in a production error message to its origin, log only the
origin when opening a browser, and echo the MCP server URL as entered
rather than with environment variables expanded.

* fix(vscode): redact whole URLs in log text and keep the matcher linear

Parentheses and brackets are valid URL characters, so ending a match at
them left the rest of a credential-bearing URL in the log. Match up to
whitespace, quotes or angle brackets instead, trim only trailing
sentence punctuation and unbalanced closing brackets, and bound the
scheme length so scanning stays linear on adversarial input.

* fix(vscode): move the URL redaction helper into shared

Logger lives in src/shared, which the webview build compiles too, and
there `@/` resolves to webview-ui/src, so importing `@/utils/redact-url`
broke the webview typecheck.

* refactor(vscode): drop free-text URL scrubbing from the Logger

Scrubbing URLs out of arbitrary error messages is a blocklist that can
never be complete. Give error messages the same trust as the log message
itself instead, and keep the deterministic fixes: the MCP invalid-URL
error no longer embeds expanded environment variables, and the browser
URL is logged as its origin at the one call site that logs it.

* fix(vscode): omit JSON parse error messages from production logs

V8 and Bun quote the parsed input in JSON.parse errors, and the parsed
input is often a settings file or an API response carrying credentials,
such as the remote-config provider API keys. Log only the error name.

* fix(vscode): keep the entered MCP URL out of the invalid-URL error

A URL typed with userinfo or query credentials failed validation and
was echoed in the error, which is logged in production. Omit the URL
and keep the guidance; the user just typed it and can see it in the
form. Also drop the changeset: the repo no longer uses them.

* feat(vscode): fingerprint redacted URLs in the browser-open log line

Append three hex digits of a SHA-256 over the full URL to the redacted
origin, so a log shows whether two lines with the same origin were the
same URL without revealing it.
2026-10-08 17:35:27 +09:00
Mikołaj Kondratek ab4c1136a8 fix(vscode): stop labelling every HTTP 401-428 as an auth failure (#14919)
* fix(vscode): stop labelling every HTTP 401-428 as an auth failure

ClineError.getErrorType treated any status in 401-428 as
ClineErrorType.Auth, so a 404 from an OpenAI-compatible server with a
stale model id, or from Gemini for a retired model, was shown as an
authentication problem: for the cline provider the webview replaced the
real message with the logged-out card or "(Click Retry below)".

Only 401/403/407 are Auth now. 402 maps to Balance, 404/405/410 to a new
NotFound class, and other 4xx fall through to the generic row, which
shows the real status and message. The axios ERR_BAD_REQUEST code (set
on every 4xx) only stands in for an auth failure when no status is
available. ErrorRow adds model-id/base-URL guidance to a status-based
NotFound, and the host's text-matched model-not-found rewrite shares the
same guidance text so a message is never told twice.

* fix(vscode): keep 407 off the auth row and pin message-based detection

A 407 is the proxy rejecting credentials, not the provider, so the
sign-in card would point at the wrong account; it now stays on the
generic row, matching the SDK classifier's 401/403. Also pin that a
status outside the dedicated sets still reaches the wording checks:
Gemini answers an invalid key with a 400 and only the message says so.
2026-10-07 23:34:40 +02:00
Mikołaj Kondratek 5b67631981 fix: child-stdin EPIPE and the SAP SDK's winston handler no longer kill the host process (#13762)
* fix: stop child-stdin EPIPE and the SAP SDK's winston handler from killing the host process

Two stacked bugs made cline-core exit with code 1 about 3s after a hook
(or MCP server) exited before draining its stdin:

- HookProcess and the SDK stdio MCP client wrote to a child's stdin with
  no "error" listener. When the child dies while part of the write is
  still queued (input larger than the pipe buffer), Node fails it
  asynchronously with EPIPE and, unhandled, raises it as an uncaught
  exception on the host process. Both writers now absorb the error and
  let the existing exit handling report how the child ended.

- @sap-cloud-sdk/util (a dependency of the SAP AI Core provider) builds a
  winston logger with exceptionHandlers at module load, installing a
  process-wide uncaughtException listener that calls process.exit(1)
  three seconds later -- overriding cline-core's own "log but survive"
  handler. @jerome-benoit/sap-ai-provider bundles a second private copy
  of that module, so disableExceptionLogger() cannot reach it; the
  community vendor module now strips winston's process catchers by name
  after the import and again after building the provider.

Regression tests cover both halves: a hook and an MCP server that exit
mid-write must fail the single operation without an uncaught exception,
and importing the community module must leave no winston catcher on the
process.

* fix(hooks): explain an undelivered input in hook failure messages

Review follow-up: the stdin "error" listener only logged. It still must
not settle the run -- a hook may legitimately ignore its input and exit
0, and the shell wrapper keeps the pipe open while the script lives, so
"close" stays the single source of truth -- but the failure is now
recorded and appended to the timeout / non-zero-exit errors so a hook
that never read its input is diagnosable from the error alone. Adds a
test that a hook ignoring a large input and exiting 0 still succeeds.

* test: type the uncaughtException probes so apps/vscode check-types passes

bun-types merges extra process listener overloads into `process`, which
made `process.off("uncaughtException", ...)` fail overload resolution
under the apps/vscode tsconfig (and with it the e2e package step). Type
the probe listeners and remove them through the plain EventEmitter type.

* fix(mcp): report the server's exit, not the broken pipe, when it dies mid-write

Review follow-up: the stdin "error" listener failed every pending request
with the EPIPE message, and it fires before "exit", so the exit handler
(which carries the exit code and stderr) never got to report. It now only
records the failure; the exit and request-timeout errors append a note
that the server stopped reading its input. The test server crashes with
exit code 3 and the test asserts the exit is what gets reported.

Also drops the changeset: changesets are no longer used in this repo.

* fix(mcp): kill a server whose stdin write failed instead of waiting out the timeout

A stdin write only fails once nothing holds the pipe's read end, so the
connection is dead either way. Killing the child lets the "exit" handler
fail pending requests immediately. A server that already exited keeps
its real exit status (the kill is a no-op); one that closed stdin but
stayed alive no longer stalls the request for the full timeout.

* fix: strip only the SAP SDK's own process catchers; give a dying MCP server its graceful window

Review follow-ups:

- community.ts removed every winston `uncaughtException`/`unhandledRejection`
  listener by name, which would also take out an embedding host's own
  winston exception handlers. The SAP provider is now imported lazily
  (literal specifier, so bundlers still include it) inside the same
  snapshot-and-diff wrapper the opencode provider already uses, so only
  the listeners that import and construction added are removed. The
  hygiene test installs a host listener with winston's name and asserts
  it survives.

- The MCP client killed the server as soon as a stdin write failed, so a
  server that closed stdin and was about to exit with its own code was
  reported as SIGTERM instead. The kill now waits the existing graceful
  shutdown window (250ms) and is skipped if the server exits first; a
  test covers a server that exits 7 shortly after closing stdin.

* test(mcp): keep the stays-alive fixture alive and assert the client killed it

Number("") is 0, so with FAKE_MCP_EXIT_AFTER_CLOSE_MS unset the fixture
exited with code 7 at once and the stays-alive test passed without
exercising the kill. Use Number(undefined) (NaN) for the unset case and
require signal=SIGTERM so a fixture that exits on its own fails the test.

* fix(mcp): bound request failure and shutdown when a server outlives a failed stdin write

Sending a single SIGTERM and waiting for "exit" left a server that handles
SIGTERM alive, with the pending request stalled until its normal timeout.
If the server is still the current child when the graceful window ends,
the pending requests now fail with the stdin-failure message and the child
goes through disconnect()'s SIGTERM -> SIGKILL escalation. A server that
exits within the window is still reported by its own exit code.

Tests cover a stdin-closing server that honors and one that ignores
SIGTERM: the request fails within seconds and the process is gone.

* fix(llms): remove only the exit handlers the SAP import registers

The previous wrapper diffed process listeners across the async import and
construction, so it also removed host listeners registered in that
window (e.g. the CLI's abort-time unhandledRejection swap), and it had no
finally, so a rejected first construction left winston's handlers armed.

The SAP provider now loads through a small helper that attributes
"uncaughtException" registrations to the import by async context
(AsyncLocalStorage + a temporary "newListener" listener) and removes
exactly those in a finally. Construction runs after cleanup.
unhandledRejection is no longer touched: SAP never registers one.
On Bun, async context does not reach module evaluation, so nothing is
attributed; the Bun-run CLI and hub already treat uncaught exceptions as
fatal. The opencode signal-handler helper is back to exactly main's.

Tests run in fresh processes (one per file) and prove the SAP import
armed a handler: host listeners registered after loading starts survive
and SAP's are removed, on both a successful and a rejected first
construction.
2026-10-07 14:54:34 +02:00
Saoud Rizwan afaf4f2457 chore(vscode): release v4.1.23 2026-10-07 00:05:49 -07:00
5dea21ab89 Remove deprecated Focus Chain plumbing from the VS Code extension (#14861)
* Hide retired Focus Chain messages when loading legacy tasks

* Remove deprecated Focus Chain plumbing from the extension

* docs: remove retired Focus Chain telemetry events

* Remove unused AgentConfigLoader from the VS Code extension (#14881)

* refactor(vscode): remove unused legacy agent config loader

* test(vscode): verify initialized state persists to disk

* test(vscode): avoid contextual inference in storage assertion

---------

Co-authored-by: Cline <noreply@cline.bot>

---------

Co-authored-by: Cline <noreply@cline.bot>
Co-authored-by: cline-cloud[bot] <276134852+cline-cloud[bot]@users.noreply.github.com>
2026-10-07 13:40:20 +09:00
6354d209c8 docs: correct shared paths, log files, commands and tool names (#14884)
* docs: correct shared paths, log files, commands and tool names

Several pages repeat the same facts about where Cline keeps its files,
which commands exist and what the built-in tools are called, and many of
them no longer match the shipped CLI and SDK:

- Workflows live in ~/.cline/workflows/, not ~/.cline/data/workflows/.
  The session index is ~/.cline/data/db/sessions.db, each session has a
  folder of JSON files, and hub locks and logs are under ~/.cline/data/.
- cline start and cline dev log do not exist; the log command is
  cline doctor log, and the CLI log is ~/.cline/data/logs/cline.log.
- The built-in tools are run_commands, search_codebase,
  fetch_web_content and skills, not bash, search, fetch_web and
  use_skill.
- CLINE_HUB_ADDRESS is read only by cline schedule; the hub port is
  CLINE_HUB_PORT.
- No CLI 3 release reads CLINE_COMMAND_PERMISSIONS. The pages that
  offered it, including the PR review sample, now say the CLI has no
  command allow list and point to a PreToolUse hook. The unused
  permissionDeniedError string in the VS Code prompts goes with it.

Part of #14853.

Co-authored-by: Mikołaj Kondratek <19799111+mkondratek@users.noreply.github.com>
Co-authored-by: Manpreet Kaur <26463231+Manpreet2298@users.noreply.github.com>
Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>

* docs: qualify log, tool routing and command-approval advice

- cline doctor log always opens the default log file, so say so where
  CLINE_LOG_PATH is described.
- The editor and apply_patch choice is a default that SDK callers can
  replace with toolRoutingRules.
- The PR review sample now warns that hooks and plugins in the checked-out
  pull request run with the job's environment, and says how to keep them
  from running before relying on a global PreToolUse hook.
- The conditional-approval example no longer auto-approves git diff,
  which runs a configured external diff program, and notes that Git can
  run programs named in a repository's config.

* docs: run the PR review sample with a read-only token

The sample gave an auto-approved Cline run a token that can write pull
requests. Split it into two jobs: `review` runs Cline with read-only
permissions on a checkout of the base branch and writes review.md;
`post` runs no repository or model code and posts the file with
`gh pr review --comment`.

Skip pull requests from forks and Dependabot, which get no secrets,
and warn against `pull_request_target`. Fence the workflow with four
backticks so the code block inside the prompt no longer ends it early.

---------

Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
Co-authored-by: Mikołaj Kondratek <19799111+mkondratek@users.noreply.github.com>
Co-authored-by: Manpreet Kaur <26463231+Manpreet2298@users.noreply.github.com>
Co-authored-by: Renee Huang <100229782+reneehuang1@users.noreply.github.com>
2026-10-07 12:10:37 +09:00
Dominic CooneyandDominic Cooney e6a3b1cf0a docs: state telemetry defaults and scope accurately (#14860)
* docs: state telemetry defaults and scope accurately

The Enterprise and monitoring pages called Cline telemetry opt-in and
anonymous, and said code never leaves the machine. Telemetry is on by
default in the VS Code and JetBrains extensions, Cline Desktop and the
CLI. It identifies signed-in accounts and organizations, and includes
prompts for requests to the Cline and ClinePass providers.

Rewrite the telemetry page around what is collected and how to turn it
off in each product. Scope the remote-configuration restrictions to the
products that enforce them, add a per-product support table, and tell
admins to ignore the folders Cline writes into workspaces. Align the VS
Code setting description and host-telemetry warning with the docs.

Part of #14853

* docs: drop bold from remote-config support table rows

The bold marked dashboard switch names, but nothing on the page said so,
so the rows looked inconsistent.

* docs: address review of the telemetry and remote-config pages

State only what the telemetry setting enforces in the extensions: the
lock, and that members can't turn telemetry on when the organization
turns it off. Scope the support-table introduction to the controls
marked Yes, since no product enforces YOLO or Kanban. Use recursive
.gitignore patterns and say why: the cached settings can include prompt
storage access keys, and a member may open a subfolder of a repository.
Narrow the indexing card to content: Cline lists file paths to search
them but doesn't index or embed file contents.

* docs: say products support remote-config controls, not enforce them

Organization rules and workflows reach sessions as instructions that a
member can edit, so "enforces" overstated what the CLI does with them.
The section heading already says "support".

---------

Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
2026-10-07 09:40:45 +09:00
Mikołaj Kondratek 6017c502c3 fix(vscode): apply the user's rules when generating commit messages (#14103)
* refactor(core): select and order session rules in one place

The rules section of a session's system prompt was selected in several
places: `listEnabledRulesFromWatcher` (enabled only, sorted by name) for the
single-workspace plugin, and an inline filter without the sort in the
combined user-instruction service. Callers outside a session that need the
same section had nowhere to reach it and would have grown a third copy.

Add `listEnabledRulesFromRecords` / `loadRulesForSystemPromptFromRecords`
next to the watcher variants, make the watcher variants delegate to them,
and use the records variant in the combined service. Export the loader from
`@cline/core`.

Behavior change: the combined service now orders rules by name like the
single-workspace path does, instead of in listing order.

* fix(vscode): apply the user's rules when generating commit messages

"Generate Commit Message with Cline" built its prompt from a hardcoded
system prompt and never loaded `.clinerules`, so commit conventions users
had written down (language, format, ticket references) were honored in
chat and silently dropped by the button.

`Controller.getRulesForSystemPrompt` takes the enabled rules from the
user-instruction watcher the controller already runs for the workspace,
through core's shared `loadRulesForSystemPromptFromRecords`, and the
commit-message system prompt appends them behind a short preamble telling
the model which rules are relevant to a commit message. The button sees the
rule set a chat session in the workspace sees, including a rule that has
`disabled: true` frontmatter being left out of both. If the rules can't be
read, the base prompt is used.

Known limitation: the Rules panel's on/off toggles live in extension state
the SDK doesn't read, so a rule switched off there is still sent to chat
and to the button alike (CLINE-3120).

The new rules load is one more await before the request; the existing
cancel check before `createMessage` covers it, and a test pins that.

Fixes #9410

* fix(vscode): let Stop end a commit generation still waiting for rules

The first generation in a workspace waits for the user-instruction watcher's
initial scan of rules, skills and workflows. Stop was honored after that
await (no request went out), but the generation and its SCM progress
indicator stayed pending until the scan finished, which can take a while on
a slow filesystem.

Race the rules load against the generation's abort signal so Stop ends the
generation at once. The scan carries on in the background and warms the
watcher chat uses; nothing waits on it any more.

* fix(core): render hub client rules through the shared selection

The hub's client user-instruction proxy filtered and formatted rules inline,
in listing order, while the combined service now goes through the shared
`loadRulesForSystemPromptFromRecords`, which sorts by name. runtime-builder
wraps the client proxy in the combined service when host plugin skills are
present, even though the plugin service contributes no rules, so adding a
host plugin reordered the same client rules in the session prompt.

Render the hub snapshot's rules through the shared helper too. Every path
that builds a session's rules section now selects and orders rules the same
way; hub client sessions now get their rules sorted by name. A boundary test
renders reversed names plus a disabled rule through the proxy alone and
through the combined service, and expects the same output from both.
2026-10-06 10:58:43 +02:00
Xx 2f93c1d6f7 docs: fix two broken README links (vscode contributing, sdk telegram) (#14826)
* docs: fix two broken README links (vscode contributing, sdk telegram)

* docs: fix two broken README links (vscode contributing, sdk telegram)
2026-10-05 09:08:08 +02:00
Bee 39ff2359f7 feat(llms): opt-in Langfuse tracing for BYOK providers plus env tags, metadata and environment (#14787)
* feat(llms): opt-in Langfuse tracing for BYOK providers plus env tags, metadata and environment

Langfuse tracing was limited to the cline and cline-pass providers, so an
operator pointing LANGFUSE_* credentials at their own instance could not trace
OpenRouter or any other BYOK provider. CLINE_LANGFUSE_ALL_PROVIDERS=1 now opts
third-party providers in. They are exported only through the isolated direct
exporter and never through the host OTLP relay, so BYOK prompts cannot reach a
collector the operator did not configure themselves.

Benchmark and isolated-environment runs can label every trace from the
environment: LANGFUSE_TRACING_ENVIRONMENT sets the Langfuse environment on the
direct exporter, CLINE_LANGFUSE_TAGS adds comma-separated trace tags, and
CLINE_LANGFUSE_METADATA adds trace metadata as a JSON object or key=value
pairs. Env values merge under the runtime's own attributes, and env-only
attributes propagate even when a call supplies none of its own.

* docs(llms): state that Langfuse tracing covers streamed language requests only

Dedicated image generation uses the AI SDK's generateImage, which takes no
telemetry option and has no Langfuse integration hook, so it has never been
traced for any provider. Document that scope in the resolver and the runbook
rather than implying the BYOK opt-in covers it.
2026-10-03 00:33:13 +02:00
Mikołaj Kondratek ce74ef0914 fix(vscode): send Cline surface headers from commit message generation (#13593)
* fix(vscode): send Cline surface headers from commit message generation

Commit message generation builds a standalone ApiHandler through
`buildApiHandler` instead of going through ClineCore, and that path wired
no request headers at all. The Cline gateway only serves models that are
restricted to Cline product surfaces (the free models) to requests that
identify themselves, so "Generate Commit Message with Cline" failed with
HTTP 403 on a free model while normal chat on the same model worked.

Resolve the headers through the shared `resolveProviderRequestHeaders`
policy — the same one `local-runtime-bootstrap` uses for sessions — so
there stays one source of truth for provider header policy, and attach
them to the provider config the standalone handler is built from. The
caller now resolves the host client identity first, so a request reports
the same client/platform as a task from the same host. Custom provider
headers stored in providers.json are layered in as well; the commit path
dropped those too.

A standalone request is not a task, so no session id is claimed:
`sessionId` becomes optional in the resolver and `X-Task-ID` is omitted
rather than sent empty. The pre-SDK extension sent `X-Task-ID: ""` from
this same code path, so nothing depends on a meaningful id here.

Fixes #13590

* refactor(vscode): share one client-identity mapping between sessions and standalone requests

`resolveClineRequestClientContext` mapped the host identity to the SDK's
client shape, including the extension-identity fallbacks, and
`buildSessionConfig` built the same object inline. A commit-message request
and a task are supposed to report the same client, and two copies of that
mapping stop guaranteeing it the first time one is edited.

Move the resolver into the session factory and have `buildSessionConfig`
call it, so both paths share it by construction. The host-identity and
multi-root helpers go back to private.

* fix(vscode): send the core's own version and session source from standalone requests

The standalone path sent the extension's version as `X-CORE-VERSION` and an
inlined "vscode" source tag, while a task from the same extension sends the
core package's version via `local-runtime-bootstrap`. That made the "same
headers as a session" guarantee untrue for that header, and the reason given
for inlining the tag (avoiding a core import) didn't hold: this module already
sits behind `cline-session-factory`, which imports `@cline/core`.

Import `CORE_BUILD_VERSION` and `SessionSource.VSCODE` from `@cline/core` so
both paths agree by construction. The vitest `@cline/core` stub re-exports the
real values from SDK source.

* fix(vscode): honor a commit-generation cancel issued while the host identity resolves

`performCommitMsgGeneration` flips the SCM button to its stop action and
then awaits `buildApiHandlerWithHostContext()` (two host-bridge lookups)
before it created this generation's `AbortController`. A cancel in that
window aborted the previous controller, if any, and the request the user had
just cancelled was then sent anyway.

Create and install the controller before the first await, check it again
right before `createMessage`, and read it through a local so a later
generation can't swap the signal an earlier one checks. Two small follow-ons
from the same fix: a cancelled generation no longer reports "Failed to
generate commit message: This operation was aborted", and only the current
generation clears the context key, so one that settles late can't flip the
button back while a newer one runs.

* fix(vscode): cancel a running commit generation when a new one starts

Each generation now checks its own controller, but the stop action can only
reach the one stored in the module slot. The Generate keybinding, unlike
the SCM button and palette entry, stays live while a generation runs, so a
second invocation replaced the slot and left the first generation
unstoppable: Stop cancelled the newer request while the older one kept
writing into the commit input. Before the per-generation controller both
read the shared slot, which is why this only surfaced now.

A new generation now aborts the previous one before taking the slot, so at
most one runs and Stop reaches it by construction. The aborted one exits
without an error and leaves the context key to the new generation.

* fix(vscode): let Stop cancel every running commit generation instead of the newest

Having a new generation abort the previous one made Stop reach everything,
but it added a rule `main` never had, and it collided with "Generate for all
repositories": a generation started mid-run aborted the bulk run's current
repository, the bulk loop then moved to the next repository, and that one
aborted the newer request. The latest request was lost and a repository was
skipped.

Track every running generation in a set instead. Each registers its
controller before its first await and removes it when it settles; Stop
aborts all of them, and the context key stays set while any runs. Nothing
but Stop cancels a generation, so concurrent generations (the Generate
keybinding stays live while one runs, the bulk loop starts one per
repository) no longer cancel each other, and Stop still reaches all of them.

* test(llms): assert Cline surface headers on streaming requests too

The wire-level test only drove `doGenerate`, but commit-message generation
calls `createMessage`, which streams. A regression that dropped the surface
headers from streaming requests alone would have kept the test green while
the free-model 403 came back. Run the same assertions through `doStream`
(and check the request really is a streaming one) for both `cline` and
`cline-pass`.
2026-10-01 15:51:02 +02:00
Mikołaj KondratekandDang Zitou 07a8587479 fix(vscode): register the tool-approval resolver before posting state (#14643)
* fix(vscode): time out undisplayed tool approvals (#14065)

* fix(vscode): drop the approval timeout, keep the resolver-ordering fix

The five-minute timer denied every pending approval, including ones the
user could see but had not answered yet. postStateToWebview() resolving
does not mean the ask was displayed (state delivery is fire-and-forget),
so the timer could not tell an undisplayed approval from a slow answer.

Keep registering the resolver before the state post so Approve/Reject
works while the post is in flight, and deny on a failed post only if
that post's approval is still the pending one; a late failure no longer
denies a later, unrelated approval. Log when an approval starts waiting
so stalls can be told apart from provider stream stalls in the output
channel.

---------

Co-authored-by: Dang Zitou <dengzitao888@163.com>
2026-10-01 15:50:43 +02:00
8b03864dca fix(core): recover oversized tool results from a session memory cache (#14712)
* fix(core): cache oversized external results without changing history

* fix(core): use volatile tool result URIs with iteration eviction

* docs(core): clarify cached result reads and truncation fallback

* docs(core): update tool result cache miss message

* fix(core): generate cache fallback agent IDs with crypto.randomUUID

* docs(core): keep cache guidance in result notices

* fix(core): preserve result recovery notices after cache eviction

* fix(core): make cached result recovery readable and text-only

* refactor(core): retain existing read_files availability and line limits

* docs(core): clarify exclusive read_files access to cache URIs

* fix(core): serialize structured cached results as multiline YAML

* fix(core): align recovery admission with persisted preview sizing

* fix(vscode): preserve cache URIs in workspace file reader

* fix(core): exclude MCP image blocks from recovery text

* fix(core): wrap long cached result lines so read_files can page single-line payloads

Cache reads go through read_files' MAX_LINE_CHARS cap, so a minified JSON
or other single-line payload was cut at 2,000 chars with no way to reach
the rest by line range. Wrap cached text at 1,000 chars on the read path
only; stored bytes stay verbatim and cache line numbers are a paging view.

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

* Revert "fix(core): wrap long cached result lines so read_files can page single-line payloads"

This reverts commit 07e166f3a2.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-30 15:29:46 -07:00
BeeandCursor Agent 2a83dd1a0e fix(desktop): provider execution, browser metadata, and MCP paths - CLINE-3283 (#14206)
* llms/core: register catalog custom providers with the gateway on the agent run path

Custom providers added via Desktop's Add Provider dialog (persisted to
providers.json/models.json and registered in the @cline/llms model
catalog) were visible in every picker but failed at execution time with
'Unknown or disabled provider "<id>"'. The agent run path
(createAgentModelFromConfig -> createGateway) only seeded builtin
provider registrations, while the legacy ApiHandler path already bridged
catalog collections into the gateway via resolveProviderRegistrationSync.

Export that bridge from @cline/llms as
resolveGatewayProviderRegistration(Sync) and apply it in
createAgentModelFromConfig, mirroring the ApiHandler path.

Fixes cline/cline#14180

* desktop: resolve MCP settings through one canonical resolver

The sidecar carried its own homedir-based resolveMcpSettingsPath next to
the shared @cline/shared/storage resolver, and the Tauri shell had a
third copy. All three agree under default env, but the duplicates
ignored CLINE_DIR/CLINE_DATA_DIR (and the shared resolver's HOME
handling), so MCP reads/writes, the OAuth authorize command, and the
'Open MCP settings' window action could target different files.

Delete the sidecar duplicate so every sidecar command uses the shared
resolver, and mirror the shared resolver's precedence
(CLINE_MCP_SETTINGS_PATH > CLINE_DATA_DIR > CLINE_DIR/data >
~/.cline/data) in the Rust open_mcp_settings_file path.

Fixes cline/cline#14152

* refactor(desktop): resolve provider metadata in the host

* Authenticate provider model source requests

* Avoid iterable Headers requirement in VS Code compatibility build

* Refresh source catalogs when provider credentials or endpoints change

* fix(core): save provider settings when model discovery fails

* fix(core): propagate provider catalog persistence failures

* fix(core): restore provider settings when catalog writes fail

* fix(core): serialize provider mutations and persist settings once

* fix: preserve provider customizations and handle unknown auth metadata

* fix(core): track discovery ownership on initial catalog refresh

* fix(desktop): address provider catalog review feedback

- Treat an empty source catalog as best-effort during credential saves
- Prune models from source-backed entries saved before discovery ownership
- Format turn failures with the submitted provider's auth metadata
- Re-export ProviderLocalCli/resolveProviderLocalCli from @cline/llms for compatibility

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-09-30 10:23:37 -07:00
Saoud Rizwan f58bc118bd chore(vscode): release v4.1.22 2026-09-29 20:51:26 -07:00
Dominic Cooney 159951f82d ci(vscode): make the e2e job spend its time on the tests (#14607)
* ci(vscode): make the e2e job spend its time on the tests

Windows e2e runs hit the 20-minute limit after their tests had passed:
the job was saving a multi-gigabyte bun cache that only that pull request
could read. The job also repeated the type check and lint that Quality
Checks already runs, and downloaded Playwright browsers the tests never
start.

- Restore the bun cache on every run and save it only from main, right
  after the install, so a later test failure keeps it.
- Build the extension with build:production and load it as the
  extension under development. The tests already ran that copy; the VSIX
  they also installed was never used, and packaging it ran
  vscode:prepublish's checks.
- Install only Playwright's ffmpeg, which it records test videos with.
  The tests launch VS Code through Electron, not a Playwright browser.
- Let runs on main finish instead of cancelling them on the next merge.
- Raise the timeout to 30 minutes for the run on main that installs
  without a cache and saves a new one.

* ci(vscode): make the e2e workflow comments match its behavior

A run on main that is still waiting to start is replaced by the next merge; only a run that has started always finishes. The comments now say so, and name the directory VS Code loads the extension from.

* ci(vscode): run Quality Checks when the Playwright config changes

The e2e build no longer runs the type check and lint, and
playwright.config.ts was the one lintable e2e input outside Quality
Checks' path filter. A change to only that file could pass CI unlinted.

* ci(vscode): stop caching Bun's install cache in e2e

Restoring the cache and installing took about as long as an uncached
`bun install` in ext-vscode-test's jobs on the same commits: 3:48-4:31
against 2:39-5:06 on Windows, and 25s against 23s on Ubuntu. With no
cache to save, a run on main can be cancelled by the next merge again
and the job fits main's 20-minute timeout, so concurrency and the
timeout go back to main's settings.

The e2e path filter also drops apps/vscode/.vscodeignore: test:e2e no
longer packages a VSIX, so nothing in the job reads that file.
2026-09-29 21:18:37 +02:00
Dominic CooneyandDominic Cooney 647d8cb059 fix(vscode): restart an auth-failed task from a revised prompt (#14628)
When a new task fails Cline authentication before its session is
created, the footer offers Retry and, since #14395, the composer lets
the user submit a revised prompt. Retry sends yesButtonClicked, which
askResponse turned into a restart of the original prompt. The revised
prompt sends messageResponse, which askResponse treated as a follow-up
and tried to resume a task with no session, so signing in and
submitting the revised prompt started nothing.

askResponse now restarts that task for messageResponse too, with the
revised text when one was typed and the original prompt otherwise.

The retry prompt is now kept with the task that showed the error and is
used only by the next response to that task. A message typed after a
failed edit or checkpoint restore continues the existing conversation
instead of replacing it, and a task opened from history after a failed
start no longer restarts the failed prompt when resumed.

Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
2026-09-29 19:27:44 +09:00
Dominic CooneyandDominic Cooney dd261c6ca5 fix(vscode): look up Reset Code checkpoints when a message is edited (#14638)
* fix(vscode): look up Reset Code checkpoints when a message is edited

The Windows e2e for Checkpoint settings failed two ways. Reset Code stayed
disabled for a message whose checkpoint existed, and a follow-up bubble
never appeared in the transcript.

Reset Code availability was computed for every user message when the
session went idle and pushed with every state update. It read the
persisted transcript, which Core writes after it reports the turn done, so
on a slow disk the last user message was missing, the message could not
be mapped to its run, and "unavailable" was cached for the session.

The buttons are only visible while a message is being edited, so ask
then. A new checkpointExistsForMessage RPC answers for one message from
the live conversation of the active session, or from the persisted
transcript of a history task. The pushed map, its cache and its
idle-time refresh are gone; the webview keeps the same disabled states
and explanations while the answer is pending or negative.

The missing bubble came from continueIdleSession, which echoed the
user_feedback message at the old epoch and then bumped the epoch. A state
snapshot built across that boundary carried the new epoch with a
transcript copied before the echo, replaced the webview transcript
wholesale, and the bubble's old-epoch copies were then dropped as stale.
Bump the epoch before the echo, as the resume path already does, and
rebuild a snapshot whose epoch moved while it was being assembled.

The e2e test itself raced the turn end: a streamed reply is visible
before the turn completes, so it opened the editor and sent the next
message while the previous turn was still finishing. It now waits for
the footer's Cancel button to go and the chat input to regain focus, so
the input refocus cannot close the Reset Code popover and the last
message starts on the rebuilt, checkpoint-enabled session instead of
joining the old session's queue.

* fix(vscode): read the same conversation for the Reset Code lookup and edit

The checkpoint lookup read the live conversation of the active session
while editMessageAndRegenerate read the persisted transcript. Between
Core reporting a turn done and writing the transcript, the lookup could
enable Reset Code for a message the edit then failed to find. Both now
read through readCurrentMessages, as the history loader already did.

While the lookup runs, the Reset Code button shows a spinner and the
tooltip "Checking for a workspace checkpoint...", and does not act.
Before, it claimed no checkpoint existed until the answer arrived.

WorkspaceRestoreAvailability has only the webview as a consumer, so it
moves out of the shared ExtensionMessage module.

---------

Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
2026-09-29 17:35:32 +09:00
Mikołaj Kondratek 3ab7b564c1 feat(telemetry): label background command failures that produce no exit code (#14177)
* feat(telemetry): label background command failures that produce no exit code

task.terminal_execution records a failed background (child_process)
execution with an exit code only when the process ran and exited
non-zero. A shell that could not be started at all — ENOENT because it
is not on PATH, EACCES, EFTYPE — landed in the same success=false bucket
as aborts and closed terminals with nothing to tell them apart, so a
rise in "shell not found" was invisible. That matters now that the
Windows working-directory lookup change (#14171) makes every bare name
resolve through PATH only.

The executor rejects with CommandSpawnError when spawn fails, keeping
the message hosts already display and carrying the operating system
code. ENOENT alone is ambiguous: spawn reports it with the same message
when the executable is missing and when the working directory no longer
exists, so the error checks the directory at failure time and records
which path was absent.

The VS Code shell tool classifies every failure into an errorCode
dimension: the OS code for a spawn failure, ENOENT_CWD when the working
directory had vanished instead, "aborted" for a cancelled turn, "other"
otherwise; a non-zero exit keeps reporting exitCode as before.
TelemetryService accepts the new dimension.

* fix(telemetry): preserve termination reasons and standalone attribution

* test(core): restrict detached signal assertion to POSIX
2026-09-28 18:35:13 +09:00
Dominic Cooney dd169d48c3 fix(vscode): submit prompts after API errors (#14395)
* fix(vscode): submit prompts after API errors

* fix(vscode): recover from unanchored API errors; keep /compact typed

Most provider failures reach the error phase with no anchor message, so
recovery availability now follows the footer's Retry button rather than
an anchored api_req_failed ask. The composer, footer, embedded retry
controls and compaction then share one decision for every error that
offers Retry.

A /compact (or /smol, /newtask) typed during recovery is neither
compacted nor sent to the model as literal text; the draft stays in the
composer, matching the disabled header button.
2026-09-28 17:29:08 +09:00
Dominic CooneyandDominic Cooney 29d66f6fbc fix(vscode): disable Reset Code without checkpoints (#14496)
* fix(vscode): disable Reset Code without checkpoints

* fix(vscode): apply checkpoint toggles to active tasks

* fix(vscode): target checkpoint settings from Reset Code

* fix(vscode): use themed foreground for popovers

* fix(vscode): keep history state on checkpoint host failure

* fix(vscode): preserve follow-up order across rebuilds

* fix(vscode): hold follow-ups for checkpoint rebuild

* fix(vscode): preserve follow-ups across rebuilds

* fix(vscode): isolate checkpoint transitions

* fix(vscode): wire mode rebuild wait correctly

* fix(vscode): coordinate rebuild task transitions

* fix(vscode): repair checkpoint CI coverage

* test(vscode): synchronize checkpoint rebuild e2e

* fix(vscode): supersede running rebuilds on repeat requests

A rebuild request for a reason that is already rebuilding used to leave
the running rebuild current, so it installed the superseded setting and,
for checkpoints, could replay held follow-ups under it. isCurrent() now
means no newer request for the same reason has arrived. A superseded
rebuild stops before replacing the session, or keeps the replacement but
leaves held follow-ups to the newer rebuild, which runs next.

Cancellation could never reach a running rebuild: runTaskTransition
waits for the drain first. Drop the unreachable cancel path and the
unused waitUntilSettled.

* fix(vscode): queue follow-ups on Core during checkpoint rebuilds

A follow-up typed during a turn after Checkpoints changed was parked in
a private list on the config-change coordinator. Nothing displayed that
list, so the message disappeared from the chat until the turn ended and
the rebuild replayed it. CI's "Checkpoint settings" e2e caught this on
every platform.

Queue such follow-ups on Core like any other, so the webview shows them
at once. The checkpoint rebuild already carries Core's queue over to the
replacement session. If a newer change for the same setting arrives
while the replacement is starting, the superseded rebuild re-queues the
prompts instead of starting a turn, so no turn runs under the stale
configuration. This removes the held-follow-up transition, the rebuild
onCancel hook, and handlePendingRebuilds.

* fix(vscode): keep rebuilds off sessions with queued prompts

Core drains its own prompt queue the moment a turn ends. A passive
rebuild that started on the "idle" status could therefore replace the
session while Core was already running the queued prompt on it, and the
old turn's send promise then marked the busy session idle. The queued
follow-up in the checkpoint e2e lost its answer this way.

Track Core's queue length on the active session and treat a session as
idle for rebuilds only when it is not running and nothing is queued. A
send's completion no longer marks the session idle when a newer turn
began after it. With Core owning the queue there is nothing left for the
rebuild to replay, so the replay code goes.

A message queued during a turn runs with the settings in force when that
turn ends; the setting change applies from the next message. The e2e
now asserts exactly that.

---------

Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
2026-09-28 16:20:30 +09:00
Mikołaj Kondratekandzhanhuangchao c5b7c7a776 fix: migrate a bare AWS profile as Bedrock profile auth and label UI credential edits as manual (#14477)
* fix(vscode): treat GUI credential edits as manual token source

The provider settings manager inherits the previous entry's tokenSource
when the caller does not pass one, and the settings UI write path never
passed one. A migrated entry therefore kept the "migration" label on
every later GUI save, so a user who re-entered credentials by hand still
looked like they were running on migrated settings.

Credential-bearing patches (apiKey/aws/auth) now pass an explicit
"manual" source. Patches that do not touch credentials keep inheriting,
so OAuth entries are not relabelled by model-only edits.

* fix(core): migrate a bare AWS profile as Bedrock profile auth

Legacy state grew three ways to say "use a named AWS profile":
`awsAuthentication: "profile"`, the older `awsUseProfile` toggle, and,
on older installs, a bare `awsProfile` value with neither flag. The
provider-settings migration honoured the first two and dropped the
third, writing a Bedrock entry with no profile.

This is a consistency fix, not a regression fix. The legacy handler
also ignored a bare profile and fell back to the default credential
chain, while the legacy settings UI displayed "profile" as the selected
auth mode for that state. The Next extension resolves the same state as
profile auth from live settings, so VS Code already behaves that way;
providers.json, which the CLI and desktop app read directly, did not.
Treat a bare profile with no explicit auth choice as profile auth so
the migrated entry matches what the UI shows and what VS Code does. An
explicit IAM or API-key choice still wins over a stale profile.

Refs cline/cline#14095.

* fix(vscode): count header edits as manual credential edits

Follow-up to the manual token-source change: OpenAI-compatible endpoints
often carry the credential in a header (Authorization, api-key) rather
than the apiKey field, so a header edit from the settings UI is a
credential edit and should leave the "migration" label behind too.

Refs cline/cline#14095.

* fix(vscode): count only AWS auth fields as credential edits

The AWS block also carries region, prompt caching, endpoint, base-model
and inference-profile toggles. Editing those is not a credential edit,
so it should not relabel a migrated entry as manual. Only accessKey,
secretKey, sessionToken, authentication and profile (or clearing the
block) count.

Refs cline/cline#14095.

* fix(core): backfill the profile on already-migrated Bedrock entries

Migration never overwrites an existing entry, so an install that
migrated before the bare-profile inference has a Bedrock entry with no
profile and the fix would only reach installs that have not migrated
yet. Repair such an entry from the legacy state still on disk, but only
when it is untouched migration output: still tagged "migration", no
authentication choice, profile, static credentials or API key, and the
legacy state has a bare profile with no explicit authentication. A
manually saved entry or an explicit IAM/API-key choice is left alone.

Refs cline/cline#14095.

* fix(core): apply the Bedrock profile repair at most once

Legacy state is never deleted, so the repair re-read it on every launch
and would keep restoring a profile a user had removed through a path
that does not write legacy state back: a hand-edited providers.json, or
another client sharing the data directory. Record the repair in
providers.json and skip it once recorded.

Also carry `modes` across the migration's rewrite. It was rebuilt from
an empty stored object, so a migration that added a provider wiped any
stored voice-input selection.

Refs cline/cline#14095.

* chore(vscode): drop the unused Bedrock providers.json writer

`buildBedrockProviderSettings()` has no callers, and the rationale in
its doc comment is wrong: it claims the session's `providerConfig` does
not override the gateway registration, while `buildProviderConfig()` in
local-runtime-bootstrap.ts spreads the session config last, so live
settings do win over the stored entry. That comment is what sent
cline/cline#14100 after a bug that was actually in the migration.

Remove the function, its comment and its tests. `buildBedrockProviderConfig()`,
which the session factory does call, is unchanged.

Refs cline/cline#14095, cline/cline#14100.

---------

Co-authored-by: zhanhuangchao <1348752764@qq.com>
2026-09-28 15:29:21 +09:00
John Choi 2ed2ef288e test(e2e): raise dependency install timeout to 5 minutes (#14536) 2026-09-27 10:13:36 +09:00
Dominic CooneyandDominic Cooney 0271462032 fix(vscode): restore Zod exports in Vitest (#14504)
Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
2026-09-25 15:56:27 +09:00
Saoud Rizwan 787ad1b077 chore(vscode): release v4.1.21 2026-09-24 01:22:19 -07:00
Mikołaj Kondratek 8abfde69d8 refactor(shared): add toPosixSeparators and reuse it in core and vscode (#14452)
Four call sites carried the same helper under different names:
apps/vscode's @-mention file search (toPosixSeparators, from #14398),
core's plugin-install (toPosixPath), workspace/file-indexer
(toPosixRelative) and workspace/mention-enricher (inline). All split on
path.sep and join with "/", so a literal backslash in a POSIX filename is
left alone. Move that one implementation to @cline/shared and import it.

No behavior change: every migrated site already used the path.sep form.
The unconditional replace(/\\/g, "/") variants in cron-reconciler,
rule-conditionals and apps/vscode/src/utils/path.ts (String.prototype
.toPosix) are deliberately untouched; switching them is a POSIX behavior
change to decide per site, not a refactor.
2026-09-24 07:03:56 +02:00
Saoud RizwanandDominic Cooney 8c00a0820d fix(vscode): stop rendering the raw tool envelope for silent commands (#14239)
* fix(vscode): stop rendering the raw tool envelope for silent commands

Since #13816, a command that prints nothing reaches the chat as a
ToolOperationResult with result: "". extractToolOutputText only
recognized records with a non-empty result or error, so the empty
result fell through to the JSON.stringify fallback and the chat rendered
[{"query":"git add -A","result":"","success":true}] as the
command output. Treat an empty string result as a recognized, empty
output so the command row renders with no output section.

* fix(vscode): preserve structured MCP output

---------

Co-authored-by: Dominic Cooney <dominic.cooney@cline.bot>
2026-09-23 19:14:41 -07:00
James ArlenandTheRealSpencer bb203d435c security: raise js-yaml floor to 4.3.2 (CVE-2026-84375, T1 in rule/skill frontmatter loader) (#14032)
bun.lock: js-yaml@4.3.1 -> 4.3.2 (the vulnerable entry is deleted, not just overridden).
Root override becomes a plain top-level key ">=4.3.2 <5" per the bun-1.3 rule; apps/vscode
floor ^4.3.2. Closes 2 findings on cline (CVE-2026-84375, AIKIDO-2026-641142).

Produced by the VMP Automation, 2026-09-10 run

Co-authored-by: TheRealSpencer <32678829+TheRealSpencer@users.noreply.github.com>
2026-09-23 23:53:14 +02:00
Mikołaj Kondratek 7f5366556c fix(vscode): normalize @-mention search paths to forward slashes on Windows (#14398)
* fix(vscode): normalize @-mention search paths to forward slashes on Windows

Both @-mention search producers returned workspace-relative paths with
platform separators on Windows: the ripgrep walker via path.relative and
the host-index path via the JetBrains FilenameIndex host. Only the
open-tabs branch normalized to "/".

Downstream code assumes forward slashes. The webview picks the label over
the full path when the label differs from value.split("/").pop(), so on
Windows every nested README rendered as "/README.md"; the open-tabs
dedupe missed the same file with the other separator (#5747); and stored
mentions were not portable across OSes.

Normalize at both producers and reuse the helper for the open-tabs
branch. Two new tests cover the ripgrep and host-index branches; two
existing tests that expected "src\main.ts" on win32 now expect the
normalized form on every platform.

* fix(vscode): only rewrite the platform separator in @-mention search paths

Splitting on path.sep instead of replacing every backslash keeps POSIX
filenames that contain a literal backslash intact (review feedback).
Same approach as the SDK's workspace file-indexer. Tests now build their
inputs with path.sep, so the conversion is exercised on Windows CI and
stays a valid no-op on POSIX; a POSIX-only test covers the literal case.
2026-09-23 08:48:12 +02:00
Bee 636de8b2af fix(sdk): persist session errors across desktop and CLI navigation ENG-2524 (#14033)
* fix: persist display-only session errors across clients

* fix: record terminal failures after retries and guard stale history

* fix(core): exclude display-only history from compaction

* cli: type manual compaction input as MessageWithMetadata

The display-only filter reads `message.metadata`, which exists on
MessageWithMetadata but not on Message, so `@cline/cli` typecheck failed and
aborted the parallel Quality Checks run before the other packages reported.

The caller already passes MessageWithMetadata, and canonicalMessages only ever
returns input.messages, so widening both signatures matches the real data and
also hands createSessionCompactionState the metadata its source hash expects.

* test(desktop): expect formatted error message prefix
2026-09-22 11:43:07 -07:00
Saoud Rizwan 254f40c4b5 chore(vscode): release v4.1.20 2026-09-22 01:55:14 -07:00
Mikołaj Kondratek cedc3c6639 fix(vscode): deliver run-start hook context through the hooks adapter (#14160)
* fix(hooks): deliver run-start hook context and control on next

UserPromptSubmit/TaskStart/TaskResume hooks could not inject context on
the next engine, and at the SDK core layers their output was discarded
entirely (agent_start/agent_resume spawned detached with stdio ignored,
so even cancel was dead). Legacy delivered these as <hook_context>
blocks in the same turn as the user prompt.

Mirrors the tool-hook contextModification shape one level up:

- Contract: beforeRun now returns AgentRunStartResult, adding an
  appendContext channel next to stop/reason.
- Runtime: run-start context is collected in callBeforeRunHooks and
  injected as a <hook_context source="RunStart"> user message (hidden
  display role) after the run's input messages, so the model sees it on
  the run's first request. The existing post-tool flush is extracted
  into a shared helper, and stale contexts are cleared at run start. A
  stopping hook's context is never injected.
- SDK core layers (hook-file-hooks, subprocess): agent_start and
  agent_resume now run blocking with their control honored — cancel
  stops the run with the hook's message as the reason, context becomes
  appendContext with the existing 50KB truncation. prompt_submit stays
  a fire-and-forget message-added dispatch (the run input is not in the
  snapshot when beforeRun fires at these layers), but no longer fires
  for injected hook-context messages.
- Extension adapter: TaskStart and UserPromptSubmit contextModification
  now maps to appendContext (combined across both, cancel semantics
  unchanged).

* fix(hooks): make run-start context survive the orchestrator and harden the blocking path

Review fixes on top of the run-start context injection change:

- The session-runtime orchestrator's beforeRun merger returned undefined
  for every non-stopping result, discarding appendContext on every
  production path (VS Code, CLI, desktop) — only directly-constructed
  runtimes saw the feature. It now aggregates appendContext like its
  beforeModel/beforeTool siblings, with orchestrator-level tests.
- Two more message-added prompt dispatchers (the CLI's and the audit
  logger's) were still treating injected hook-context messages as user
  prompts; the adapter's latestUserPrompt likewise would have fed hooks
  their own previous output as the prompt on the next run. All filter on
  the system display role now.
- A blocking hook that spawned a background child sharing its stdout
  could hang the run forever: completion resolved on stdio close, which
  the grandchild holds open past the SIGKILL. The runner now settles
  shortly after process exit with whatever output arrived.
- Run-start hooks in the subprocess layer tolerate non-JSON stdout
  (their output was ignored for their whole prior life), instead of
  raising a dispatch error on every run; timeouts still surface.
- Run-start context is never inserted between a seeded trailing
  tool_use and its tool_result: the flush is deferred to the post-tool
  flush point, and the per-batch buffer reset that would have dropped
  the deferred context is removed (covered by a runtime test).
- A stopping layer now short-circuits the layer merge, so later layers'
  blocking hook commands don't run for an already-cancelled turn.
- Cleanup: the duplicated control mapper in hook-file-hooks collapses
  into stopOrContextResultFromControl (as subprocess.ts already did);
  the adapter's two inline stop-or-context tails use the shared mapper;
  the false 'input not in snapshot' rationale is corrected; the hooks
  README documents the blocking run-start contract and per-host
  timeouts.

* test(vscode): e2e-verify run-start hook context injection through the live pipeline

Extends the hooks e2e: the fixture UserPromptSubmit hook now records the
prompt it received and returns a contextModification naming a distinctive
fact, and the mock server answers the probe prompt according to whether
that fact is present in the model request it received. The spec asserts,
in a real VS Code window through the real adapter/orchestrator/runtime
pipeline: the injected context reaches the first and second model
requests; on the second turn the hook receives the real prompt rather
than its own previously injected block; the raw block never renders in
the transcript, live or when the task is reopened from history.

The prompt assertion matches on content: the next engine hands hooks the
stored form of the prompt, wrapped in user_input markup.

* test(vscode): size the hook-gated e2e reply waits to the hook budget

Both hooks specs waited for the model reply with Playwright's 5s expect
default, but a run-start hook executes before the model call and the
extension allows it up to 30s. On Windows the first prompt-sending spec
pays a cold PowerShell spawn that alone can exceed 5s: the failure
artifact shows the 'Hook: UserPromptSubmit Running' chip still up when
the assertion expired, and the same spec had been passing at 30.1s and
failing at 30.0s of wall clock - a coin flip on the deadline, not a
behavior change.

The reply assertions now cover the hook budget, and both specs are
marked slow so two hook-gated prompts plus VS Code startup cannot exhaust
the per-test timeout either.

* test(vscode): make the hook-context probe turn-specific

The mock answered "received" whenever the injected fact appeared anywhere
in the request, and turn 1's block stays in the conversation history, so the
second-turn assertion could not fail even if repeated run-start injection
regressed. Require the fact in a message after the latest probe prompt.
2026-09-21 22:52:47 +02:00
MaxandMax Paulus 🥪 5d765e2779 feat(vscode): capture conversation-linked Git telemetry (#14127)
* add requestId to afterModel. add git snapshot telemetry object

* update vscode session host to emit git telemetry onAfterModel

* fix late observers

* fix tests

---------

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-09-21 10:18:14 -07:00
Dominic Cooney ee46dc4bd7 fix(vscode): preserve drafts across retry (#14312) 2026-09-21 15:50:07 +09:00
Dominic Cooney 81f9eebf72 fix(vscode): stream background command output from tool updates (#14136)
* fix(vscode): stream background command output from tool updates

* fix(vscode): isolate streamed command output

* fix(vscode): keep stale command completions isolated
2026-09-19 12:07:36 +09:00
BeeandCursor Agent 2ba680ce6c fix: load and surface .cline/rules consistently across clients (#14207)
* shared: expose workspace/global rules path helpers and search redirected Documents

resolveWorkspaceRulesConfigPaths returns both supported workspace rule
layouts (.clinerules and .cline/rules) so hosts can stop hardcoding one
of them (#14186). resolveGlobalRulesConfigPaths returns every global
rules location and now also includes OneDrive Known-Folder-Move
Documents candidates: the VS Code extension resolves the real Documents
folder through the OS and creates global rules there, so on Windows
machines with redirected Documents the SDK resolver (used by the
desktop app and CLI) never found them (#14144).

* vscode: surface rules from every shared resolver location in the Rules panel

The Rules panel's toggle refresh was hardcoded to the workspace
.clinerules directory and the Documents global directory, so rules the
SDK runtime loads from .cline/rules (and global ~/.cline/rules,
~/Cline/Rules) were applied to the model but silently missing from the
panel (#14186). Synchronize toggles across every directory the shared
resolver searches, combining per-directory results the same way the
Cursor rules multi-location handling already does.

Also drop the ambient @cline/shared/storage stub: it shadowed the real
module declaration and hid every export the stub didn't re-declare;
tsc resolves the real dist types fine.

* desktop: classify .cline/rules workspace rules as Project in Customize view

The Rules tab grouped project rules only when their path contained
/.clinerules/, so rules in the .cline/rules layout were shown as Global
(#14186, #14144).

* fix(desktop): anchor rule scope to workspace roots and document layouts

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-09-18 17:36:06 -07:00
Mikołaj Kondratek 27adfdb1a6 fix(vscode): honor Azure API version in SDK sessions and mirror it to providers.json (#13761)
* fix(vscode): honor Azure API version in SDK sessions

The SDK session factory resolved bedrock/vertex/sap/ollama structured
options from legacy state but never Azure's: the webview's Azure API
Version and Identity fields (openai-compatible provider) only land in
legacy StateManager keys, so SDK sessions hit Azure deployment URLs
without ?api-version= and Azure rejects every request with "Resource
not found".

Resolve azureApiVersion/azureIdentity from legacy state (providers.json
azure block as fallback, matching the base-URL/API-key precedence) and
pass them as providerConfig.azure. Also mirror resolved values into
providers.json so the CLI - which reads only providers.json - sees the
same Azure configuration the extension uses; the one-shot legacy
migration never updates existing entries, so this is the only ongoing
sync for these fields.

Fixes #13655

* test(vscode): validate the Azure mirror against the real ProviderSettingsManager

The vitest '@cline/core' alias points at an in-memory stub whose
saveProviderSettings validates nothing, so the mocked-manager tests could
not catch a mirror payload the real ProviderSettingsSchema.parse would
reject (the resolver swallows save failures). Import the real manager by
relative path for one schema round-trip test, alias the @cline/shared/db
subpath its import graph needs, and re-pin the provider-settings-manager
mock in beforeEach so a test swapping in a real manager cannot leak it
into later tests.

* test(vscode): import the built @cline/core bundle instead of SDK source

The real-manager test imported provider-settings-manager from SDK source
by relative path, which pulled the SDK source tree into the extension's
TypeScript program and failed check-types with a TS6059 cascade (files
outside rootDir). Import ../../node_modules/@cline/core/dist/index.js
instead: it still bypasses the vitest '@cline/core' stub alias (aliases
match bare specifiers, not file paths) while typing against the package's
own declarations. Also assert the mirrored azure block on the temp file
itself, since the in-memory stub would satisfy the manager-level
assertions too.
2026-09-18 10:15:36 -07:00
Tomás BarreiroandSaoud Rizwan 7f75100da2 Add Client Type to Recommended Models requests (#14224)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-09-17 12:40:44 -07:00
Saoud Rizwan 27fe60d79f chore(vscode): release v4.1.19
4.1.18 was tagged but never reached the Marketplace: every publish attempt
timed out on upload. Its notes carry forward under 4.1.19 together with
what landed on main since, so the first version users actually receive
describes everything in it.
2026-09-17 00:23:12 -07:00
0905884ab5 Refresh Cline logo assets (shared, desktop, menubar) (#13965)
* feat(branding): add canonical Cline logo assets

* feat(vscode): refresh extension logo surfaces

* feat(web): update Hub and example branding

* docs: update Mintlify branding assets

* build(brand): lock asset generator tooling

* chore(branding): limit logo refresh to assets and required wiring

* fix(vscode): restore accessible logo names

* fix(branding): retain favicon contrast in dark browser tabs

* [2/3] Refresh desktop logo assets (#13966)

* chore(desktop): refresh native logo assets

* chore(menubar): refresh native logo assets (#13967)

part of logo migration

* docs: use latest adjusted navigation logo

* fix(hub): keep favicon visible in dark browser tabs

Point the Hub favicon at the dedicated favicon.svg and give it the same
prefers-color-scheme fallback as the example app. cline-logo-filled.svg
stays untouched since the sidebar renders it with dark:invert.

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

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-16 18:39:55 -07:00
charles 0cf47f5851 fix(providers): refresh CoreWeave branding and setup links (#14185) 2026-09-16 17:52:00 -07:00
Saoud RizwanandSaoud Rizwan 173837314b fix: stop Windows resolving bare program names through the workspace cwd (#14171)
* feat(shared): add disableCurrentDirectoryExecutableSearch for Windows

libuv resolves a bare program name on Windows by searching the child's
working directory before PATH, gated on the spawning process having
NoDefaultCurrentDirectoryInExePath defined. Cline spawns rg, git and
powershell with the user's workspace as cwd, so a repo shipping an rg.exe
would get it executed at index time. Expose a one-call helper that sets
Microsoft's documented opt-out, plus a Windows-only test that plants a
zero-byte cmd.exe and asserts the real one still runs.

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

* fix: stop Windows resolving bare program names through the workspace cwd

Call disableCurrentDirectoryExecutableSearch() at startup in every
process that hosts Cline core: the CLI (and the hub daemon it boots), the
desktop sidecar, the VS Code extension host, and the JetBrains cline-core
process. One environment variable covers every spawn site (file indexer,
search, simple-git, shell executor, hooks, MCP, taskkill) and is inherited
by children, which cmd.exe, libuv and Bun 1.4+ honor as well.

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

* fix: hand user-facing children the inherited NoDefaultCurrentDirectoryInExePath state

libuv reads the opt-out from the spawning process, so Cline's own protection
does not depend on children carrying it. But cmd.exe, Go, Bun 1.4 and
libuv-based children honor it as well, so letting them inherit Cline's
setting would silently change how a user's own bare program names resolve
(npm scripts running a cwd-local .bat through cmd.exe, MCP servers named
relative to cwd, hooks that spawn helpers).

disableCurrentDirectoryExecutableSearch() now latches the value the process
inherited, and withInheritedExecutableSearch() restores that state on the
child env at the spawn sites that run user-authored programs: the shell
executor, MCP stdio servers, hook subprocesses, the plugin subprocess
sandbox, and the VS Code host's HookProcess.

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

* Revert "fix: hand user-facing children the inherited NoDefaultCurrentDirectoryInExePath state"

This reverts commit b643517257.

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-16 17:30:11 -07:00
Mikołaj Kondratek 186ac22e29 telemetry: fire ui.panel_opened from initializeWebview on every host (#14101)
* telemetry: fire ui.panel_opened from initializeWebview on every host

ui.panel_opened was only captured from VscodeWebviewProvider, so the
JetBrains plugin never emitted it: the activation funnel there jumps from
user.extension_activated (core spawned on project open) straight to task
events, with no signal that the Cline UI was ever rendered.

The webview calls the initializeWebview RPC once on mount on every host,
so capture the same event there with source "webview_initialized".
Existing dashboards keyed on ui.panel_opened pick up JetBrains for free;
VS Code gains one extra source value on an event it already emits.

* telemetry: type the ui.panel_opened source

A PanelOpenedSource union on capturePanelOpened keeps the documented
source list and the call sites from drifting apart. Also note that on
JetBrains webview_initialized fires on every webview reload, so a
crash-restart loop emits one per restart.
2026-09-16 09:25:15 +02:00
Mikołaj Kondratek b675eb6189 telemetry: report host-provided core spawn ordinal and reason as metadata (#14104)
* telemetry: report host-provided core spawn ordinal and reason as metadata

Out-of-process hosts spawn cline-core and sometimes respawn it: after a
crash, on a rollout fallback or demotion, or when the user restarts the
agent. From core's side every spawn is just another
user.extension_activated, so a core that crash-restarted three times and a
user with three project windows open are indistinguishable in telemetry.

Read CLINE_CORE_SPAWN_ORDINAL and CLINE_CORE_SPAWN_REASON from the spawn
env, the same contract as IS_DEV and CLINE_ROLLOUT_VARIANT, and attach
them as core_spawn_ordinal / core_spawn_reason metadata on both telemetry
pipelines (classic TelemetryService and the SDK handle), so they land on
every event. Malformed values are dropped field by field; in-process hosts
set nothing and get no fields, like host_plugin_version on the CLI.

The JetBrains plugin sets the env in a companion change.

* telemetry: type core_spawn_reason as the shared CoreSpawnReason union

Define CORE_SPAWN_REASONS / CoreSpawnReason once in @cline/shared next to
TelemetryMetadata and use the union in every metadata contract (shared,
classic TelemetryService, vitest stub) and in the env parser, so a
programmatic setMetadata/updateMetadata cannot introduce a reason the
parser's allowlist would have rejected.

* telemetry: derive the classic metadata's spawn fields from CoreSpawnTelemetryMetadata

Index into the parser's type instead of redeclaring both fields and their
doc comment, mirroring how extension_variant defers to
RolloutTelemetryMetadata. Also fix import order flagged by biome check.
2026-09-16 15:09:57 +09:00
Saoud Rizwan 2198414245 chore(vscode): release v4.1.18 2026-09-14 23:56:01 -07:00
Dominic Cooney 36905f11cc fix(core): refine PowerShell shell guidance (#14055) 2026-09-15 11:11:29 +09:00
632fce5071 feat(llms): sampled cline-provider AI SDK tracing via the host OTLP exporter (#13974)
* fix(llms): limit Langfuse telemetry to Cline provider

Refs ENG-2505

* fix(llms): include ClinePass in Langfuse telemetry

Refs ENG-2505

* feat(llms): sampled cline-provider AI SDK tracing via the host OTLP exporter

Enables the collector-relay tracing path: when the host telemetry service
has registered an OTLP tracer provider, cline-provider streams can emit
AI SDK spans without Langfuse credentials in the process.

- CLINE_TRACE_SAMPLE_PERCENT (default 0 = off) gates emission; sampling
  is a deterministic FNV-1a hash of the task/session id, so whole tasks
  sample together and retries decide identically.
- Metadata-only by default (recordInputs/recordOutputs false): models,
  tokens, timings, errors — no prompt or completion content. Content
  requires CLINE_TRACE_RECORD_CONTENT=true explicitly.
- The direct Langfuse path (env credentials, hub/internal) is unchanged:
  full content, every request, provider-gated as before.

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

* feat(telemetry): client OTLP trace pipeline for cline-provider AI SDK spans

Extension side of the Langfuse collector relay:

- OpenTelemetryClientProvider gains a TracerProvider: when tracesExporter
  is configured, spans batch-export over OTLP (grpc / http/json /
  http/protobuf) to the same collector endpoint as logs/metrics.
  register() also installs the async context manager span parenting needs.
- tracesExporter plumbed through all three config sources: build constants
  (OTEL_TRACES_EXPORTER), runtime env (CLINE_OTEL_TRACES_EXPORTER), and
  remote config (openTelemetryTracesExporter) — the last is the no-release
  kill switch.
- SDK: a host that registers a traces exporter now defaults to 100% task
  sampling; CLINE_TRACE_SAMPLE_PERCENT reduces it (0 disables). Env reads
  are literal so build-time inlining works. Metadata-only stays the
  default; content still requires CLINE_TRACE_RECORD_CONTENT=true.
- CLI build inlines the new envs alongside the existing OTEL set.

No publish workflow sets OTEL_TRACES_EXPORTER yet, so shipped builds keep
tracing dark — activation is a separate one-line-per-workflow PR.

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

* fix(llms): honor the global telemetry opt-out on the trace relay path

AI SDK spans bypass the ITelemetryService wrapper that enforces the
user's opt-out for events and metrics, so an opted-out user's prompts
would still have traced once a host registered an OTLP exporter.
resolveAiSdkTelemetry now re-checks the shared global settings file
(telemetryOptOut) per stream — covering startup state and mid-session
opt-outs across extension and CLI. The credentialed direct Langfuse
path is unchanged: it only activates on explicit operator config.

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

* style: biome format

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

* fix(llms): one trace export path per host — direct Langfuse declines when an OTLP tracer is registered

Enablement is a per-stream boolean but export fan-out is processor-level:
attaching the direct LangfuseSpanProcessor to a host-registered tracer
provider ships every span twice (direct + collector relay). The direct
path now declines instead of cooperating when a recording provider
already owns the global slot; it only exports when it registers its own
provider. A host therefore configures exactly one of LANGFUSE_*
credentials or an OTLP traces exporter, and misconfiguring both is safe.

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

* feat(hub): daemon-distinct trace identity for Langfuse hub coverage (#14034)

The detached hub daemon already has everything needed to emit AI SDK
traces: it ships in the same compiled binary as the CLI (so the publish
build's OTEL_TRACES_EXPORTER / CLINE_TRACE_RECORD_CONTENT inlining
reaches it), its telemetry handle flows into the hub server and the
scheduled-run handlers, core's OpenTelemetryProvider registers a global
tracer when the exporter is configured, and shutdown already flushes
with a hard deadline.

What was missing is identity: spans carry only the OTel resource, not
per-event metadata, so daemon-emitted traces were indistinguishable
from CLI-local ones (both service.name=cline). The daemon now declares
serviceName=cline-hub-daemon (+ serviceVersion), via new optional
serviceName/serviceVersion fields on the shared OpenTelemetryClientConfig.

Opt-out holds for the daemon unchanged: it is a same-machine, same-user
process, so the shared global settings file the per-stream gate reads is
the requesting user's own (shared/cloud topologies tracked in ENG-2525).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* fix(llms): fail closed when the telemetry opt-out cannot be verified

The relay's opt-out guard treated every settings read failure as 'no
opt-out recorded', so a corrupted or unreadable settings file (including
a torn read of the non-atomic writer) silently re-enabled tracing for a
user who had opted out. Only a genuinely absent file (ENOENT, first run)
now reads as opted in; malformed JSON and every other read failure
disable the relay — consent that cannot be verified is not consent.

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

* fix(llms): identify the OTLP relay explicitly instead of inferring it from any recording tracer

'Some recording tracer exists' misclassified console-only tracing as the
collector relay (enabling the relay decision) and — for mutable
providers — newly suppressed the direct Langfuse attach that worked
before the one-path guard. The hosts that build tracer providers now
stamp a relay marker only when a real OTLP span processor was created
(console-only providers stay unmarked), and the trace decisions key off
that marker:

- relay path enables only behind a marked provider;
- the one-path decline fires only for marked providers — unmarked
  mutable providers keep the original cooperative attach behavior;
- a relay decline is no longer cached, so direct Langfuse gets to retry
  once a relay is disposed.

The marker lives on the provider instance (reached through the OTel API
global), which survives bundled module duplication where a module-level
registry would not.

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

* style: organize imports

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

* fix(telemetry): own and dispose remote trace clients across config updates

* fix(llms): wire per-call Langfuse telemetry and isolate direct export

* refactor(telemetry): reject unsupported providers before initialization

* test(desktop): raise sidecar vitest timeout above the module-graph import cost

The first test in each sidecar file pays the @cline/core → @cline/llms
import cost, which lands within ~100ms of the 5s default on CI runners
(commands-account failed at 5009ms; context.test.ts passed at 4923ms).

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: abeatrix <beatrix@cline.bot>
2026-09-11 15:13:54 -07:00
Dominic CooneyandSaoud Rizwan e4f1df2c5a fix(core): identify shell editions and discourage redundant wrappers (#14025)
* fix(core): identify shell editions and discourage redundant wrappers

* fix(shared): keep PowerShell edition export Node-only

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-09-10 18:01:20 -07:00
Bee 3d5070705c feat(desktop): Enable web search by default outside YOLO mode CLINE-3214 (#13957)
* feat(core): enable web search by default outside yolo

* fix(core): fail closed when tool settings cannot load
2026-09-08 22:11:14 +02:00
Mikołaj Kondratek 8ff5f22cf9 fix(webview): flag attached images the selected model can't use and offer a model switch (#13943)
* fix(webview): warn when an image is pasted or dropped for a text-only model

Pasting or dropping an image into the chat box did not check whether the
selected model accepts image input. The image was attached and shown as a
thumbnail, then silently replaced by a text placeholder before the API call,
so the user never learned it was ignored.

The chat box now refuses the image and shows an inline hint, using the same
overlay pattern as the existing dimension and unsupported-file errors.
Unknown capability data fails open, matching core.

* fix(model-catalog): let declared input modalities decide supportsImages

The provider layer prefers a model's declared input modalities over its
capability list when deciding whether image parts are sent. The adapter that
produces the legacy supportsImages flag only looked at capabilities, so a
model declaring text-only input without a capabilities array was reported as
image-capable to the UI while the request formatter still stripped images.

* fix(webview): keep unsupported images attached, badge them and offer a model switch

Refusing the paste/drop was the wrong shape: it dropped user content and
could not cover images attached before switching to a text-only model.

Images are now attached regardless. While the selected model has no image
input, each image thumbnail carries a warning badge and a notice under the
composer says the images will be ignored and links to the model picker. Both
are derived from the current model, so they appear and disappear as the model
changes.

* fix(webview): make the model-picker link in the images notice a native button

An anchor with role=button and no href is focusable but ignores Enter and
Space, so keyboard users could not open the model picker from the notice.

* fix(webview): keep the link colour on the model-picker button in the images notice

A native button inherits the notice's warning colour, which made the link
blend into the sentence; use the VS Code text-link colours instead.

* refactor(webview): rename imagesUnsupported to unsupportedImagesAttached

The flag also requires images to be attached, so the old name read like a
plain negation of modelSupportsImages.
2026-09-08 21:56:14 +09:00