Commit Graph
1504 Commits
Author SHA1 Message Date
John Choi e5a184cbb0 refactor(sdk): record queued activity at mutation boundaries 2026-10-14 13:20:17 -07:00
John Choi e7b35d2c8b fix(sdk): avoid renewing activity for rejected mutations 2026-10-14 13:18:52 -07:00
John Choi ab43548540 docs(sdk): clarify pending runtime activity retention 2026-10-13 18:33:03 -07:00
John Choi a2c978174b refactor(core): forward runtime activity without Hub bookkeeping 2026-10-13 12:39:48 -07:00
John Choi d42c249a39 refactor(core): reuse active run check for admission 2026-10-13 11:26:36 -07:00
John Choi 2b348cc3da feat(core): expose live runtime activity in Hub status 2026-10-13 11:16:07 -07:00
Saoud Rizwan c606894cb1 Revert "fix(vscode): preserve attachments in pending question responses (#14986)" (#15058)
This reverts commit f8997dac23.
2026-10-13 07:31:32 -07:00
Lucien f8997dac23 fix(vscode): preserve attachments in pending question responses (#14986)
* fix: preserve attachments in VS Code question responses

* fix(vscode): preserve pending question response ordering

* fix(core): preserve display text in Hub submitted prompt events
2026-10-13 15:09:05 +02:00
Saoud Rizwan 1a5c3f84e0 test(core): follow OpenCode Go's move to /inference/go/v1 in routing test 2026-10-13 01:11:16 -07:00
Saoud Rizwan f15ed3d98b chore(sdk): release v0.0.93 2026-10-13 00:52:03 -07:00
Saoud Rizwan 482b13d38a chore(llms): refresh model catalog and follow v0 / OpenCode Go upstream changes (#15047)
* test(llms): follow models.dev catalog changes for v0 and OpenCode Go

models.dev dropped the v0 provider and moved OpenCode Go from /zen/go/v1
to /inference/go/v1 (both endpoints serve the same model list).

* chore(llms): refresh generated model catalog
2026-10-13 00:29:19 -07:00
BeeandSaoud Rizwan 12a4c6bfe2 feat(core): load plugins in the hub process and surface plugin errors (#14957)
* feat(core): load plugins in the hub process and surface plugin errors

Plugins now load in-process through one registry per process instead of a
node/bun subprocess. The compiled CLI and the hub it starts no longer need a
runtime on PATH, and plugins can use the Bun global under the Bun-built hub.
The subprocess sandbox stays available with CLINE_PLUGIN_MODE=sandbox.

The registry wraps import, setup, every hook, tool, and command call with
timeouts and error capture, and tracks loading/ready/degraded/failed/disabled
per plugin with the last error's phase, message, stack, and counts. A failing
plugin no longer silently drops its tools:

- session.create takes a `plugins` policy shaped like toolPolicies, and its
  reply and session.created event carry `pluginIssues`
- failures after start (setup and hooks run on the first turn) reach each
  affected session as a status notice
- the hub answers plugins.list/status/reload and broadcasts
  plugin.status_changed; `cline hub plugins [--reload]` wraps them
- the daemon marks a plugin failed instead of shutting down when an uncaught
  error's stack points into it
- the CLI prints a warning naming the plugin and error, and
  `cline config tools --json` lists failed plugins as plugin_failure entries

Setup and tool contexts gain cwd and emitEvent; __clinePluginHost keeps
working as a shim. Compiled binaries fall back to jiti/static when babel is
not on disk next to them.

* fix(core): give each session its own copy of every plugin module

Plugins written for the per-session sandbox keep state at module level. The
cline-monitor plugin, for example, builds one plugin object per import and
throws "Create a separate monitor plugin handle for each session" on a second
setup(). Sharing one imported copy across hub sessions broke it on the
second session.

The registry now hands each session its own copy: the first session claims
the copy imported at startup, and later ones re-evaluate the module while
reusing jiti's transform cache (about 180ms for cline-monitor instead of
2.8s with no cache).

A setup() failure now costs only the session it happened in: other sessions
keep their copies, the next session tries again, and a successful setup
returns the status to ready. Import failures, attributed uncaught errors, and
repeated hook/tool failures still turn the plugin off for every session.

* docs(sdk): document the plugin hooks that exist and how hook failures behave

The Hook Stages list (snake_case stages such as tool_call_before) and the
Hook Policies table (mode, retries, queueLimit, maxConcurrency) described no
code: plugin hooks are the seven runtime hooks, and there is no per-hook
policy object. Replace both with the real hooks, what each can return, and
how hookErrorMode and the 3-second hook timeout apply.

* fix(core): report failed plugins as failed in settings

Settings marked a plugin "failed" only when the plugin tool report had no
entry for it. The report now lists failed plugins with their state, so they
showed as "available" (desktop Extensions page and other core settings
hosts). Use the reported state, and fill loadError with the failing phase
and message so the reason is visible.

Also tell only the calling session about a per-session setup failure;
other sessions' copies still work. Keep the hub log line's own message
instead of letting the plugin error text overwrite it.

* fix(core): address plugin registry review findings

Reload and generations: each session copy records the generation (import)
it came from. A reload starts a new generation without invalidating older
copies, so running sessions keep working. Failures and blocking are counted
per generation, so a broken reload cannot turn off copies that still work,
and an old copy's failures cannot change the current status. A stray error
attributed by stack turns off every live generation, since a stack cannot
tell copies of one file apart.

Sandbox mode: the hub daemon no longer preloads plugins in sandbox mode,
and tool listing and MCP settings sync inspect plugins through the sandbox.
The session plugin policy now applies in sandbox mode: paths whose file
name the policy turns off never reach the sandbox, and plugins matched only
by their exported name are dropped after import.

Calls: commands, rule content, and message builders go through the same
guarded path as hooks and tools (timeout, error capture, failure count), and
calls into a copy that is turned off, failed setup, or whose session ended
are refused. A failing rule contributes no text and a failing message
builder leaves messages unchanged.

Cleanup: ctx.onDispose registers per-session cleanup. Timers a copy creates
while its own code runs, including at import, are tracked and cleared when
the session releases the copy; host callbacks given to plugins run outside
that scope so host timers are never cleared with them. Release runs on
session stop and dispose, and now also when building the session runtime
fails. The automation-events example uses ctx.onDispose.

Registrations: automation event types are validated while setup runs. If
the host still rejects a registration on commit, the copy's setup is
recorded as failed and the error is rethrown so the partial registrations
are discarded.

Also: forward importTimeoutMs, hookTimeoutMs, and contributionTimeoutMs to
the in-process registry per session; key the plugin tool inspection cache on
registry status; keep the module copy imported by preload for the first
session; report settings-disabled plugins a session would use as issues
with reason "settings".

* fix(core): keep this bindings and route import-time plugin events

- Timer tracking forwards the runtime's this (the timer handle in Node and
  Bun) to plugin callbacks.
- Plugin methods keep their receiver: rule content, message builders,
  command handlers, and setup() are called on their own objects, including
  setup() in the existing in-process loader.
- Each plugin copy owns one async scope from import onward. Claiming the
  copy for a session fills in that scope's session id and event callback,
  so events emitted by work the module started at import reach the session.
- Each generation tracks its own health, so an older copy's first failure
  after a reload is reported to its sessions even though the status shows
  the reloaded generation.

* fix(core): bind plugin changes to sessions started afterwards

Plugins are bound to a session when it starts; added, edited, or reloaded
plugins reach only sessions started afterwards. Change detection used the
entry file's mtime and size, which missed edits to files the entry imports
(common for package plugins) and same-size edits within one clock tick, so
the next new session could claim a stale preloaded copy.

Detect changes with a content fingerprint of the entry file and every file
it reaches through relative static imports, for the registry, the plugin
tool inspection cache, and the plugin command service. Add a bootstrap test
that a session started before a plugin is added or changed keeps its
original tools even when its setup runs afterwards, and a registry test for
a change outside the entry file.

* fix(core): include side-effect imports in plugin change fingerprints

The fingerprint only followed `from`, `import()`, and `require()`
specifiers, so a fix to a file the entry imports only for side effects
(`import "./setup.js"`) left the fingerprint unchanged and new sessions kept
getting the failed entry.

Scan statement-level side-effect imports for the fingerprint only. They are
kept out of the dependency preflight, where a loose match on text inside a
string (`'run import "x" first'`) would reject a valid plugin.

* fix(core): retry failed plugin imports on a backoff

Change detection follows static and side-effect relative imports, but a
broken import can also be fixed where no fingerprint sees it: a dynamically
imported file, a file behind a block comment, or a dependency installed in
node_modules. Such plugins stayed failed until an explicit reload.

Retry a plugin whose import failed when a session starts, at most every 30
seconds, even if no change was detected. A broken plugin costs at most one
extra import attempt per 30 seconds, bounded by the import timeout.

Replace the SHA-1 fingerprint with a non-cryptographic checksum; it only has
to notice edits to plugin source.

* feat(desktop): show plugin failures in the chat

Plugin failures reached the desktop webview only as core logs written to the
devtools console. Show them as status lines in the chat:

- session start returns `pluginWarnings` for plugins the session asked for
  but could not load, and the webview adds one status line per warning
- plugin notices that arrive later on the session stream (setup and hooks
  run on the first turn) carry `metadata.pluginIssue`; the webview adds
  those as status lines too, while other core logs stay out of the
  transcript

* fix(desktop): keep plugin warnings when saved history reloads

Finishing a turn replaces the live transcript with saved history, which
never contains plugin warnings (they come from the start reply and stream
notices), so the explanation of a missing plugin disappeared after the first
turn. Carry the session's live plugin-warning lines into canonical history
at their original position by time.

* refactor(core): simplify the in-process plugin registry (#14999)

* refactor(core): simplify the plugin registry

Drop the pieces added in review rounds that cost more than they buy:

- Global timer tracking. It monkeypatched setTimeout/setInterval/
  setImmediate and their clear* counterparts on globalThis for the whole
  host process (the hub, the CLI, and the shared VS Code extension host),
  and attributed timers by async scope, so a host callback invoked from
  inside a plugin call had its timers cancelled when the session released
  its plugin copy. Plugins stop what they start in ctx.onDispose, which
  stays.
- Plugin generations and per-generation health. Blocking and consecutive
  failures are counted per plugin again; a re-import (reload or source
  change) resets them. Running sessions keep their copies callable unless
  the plugin is turned off, so a broken reload fails closed for them.
- Automatic retry of failed imports on a 30 s backoff, and the side-effect
  import scan in the change fingerprint. plugins.reload covers changes the
  fingerprint cannot see.
- The duplicated registerAutomationEventType validation; the commit
  try/catch already records a host-rejected registration as a setup
  failure.

* refactor(core): do not apply the session plugin policy in sandbox mode

The sandbox is an opt-in escape hatch and the per-session policy is new;
filtering sandbox results by file-derived and exported names added two
passes nobody needs yet. The policy is documented as in-process only.

* revert(desktop): drop the live plugin-warning merge into saved history

Reverts 90df99574. The merge re-inserted unpersisted warning lines by
timestamp after every history reload and still lost them on reopen.

* fix(core): keep plugin status consistent across a reload and a late block

- A re-import now resets `blocked` and the failure count only after it
  succeeds, so calls that hit the failure threshold while the import was in
  flight cannot leave the plugin blocked under a `ready` status.
- Sessions are notified when a plugin is turned off even if its status
  already read `failed` from another session's setup failure.
- Docs: say which relative imports the change fingerprint follows, that a
  failed reload turns the plugin off for running sessions too, and that the
  sandbox ignores the session plugin policy.

* fix(core): attribute stray plugin errors by the plugin's real path too

Module resolution follows symlinks and, on Windows, expands 8.3 short names
(the GitHub runner's temp dir is C:\Users\RUNNER~1\...), so a stack frame can
spell the plugin path differently from the configured one and the uncaught
error was never attributed. Match the realpath as well, and compare with
normalized separators and case on win32. Fixes the Windows-only failure of
"attributes a stray error to the plugin whose file is in its stack", which
also fails on the base branch.

* fix(core): match both realpath spellings when attributing plugin errors

On Windows fs.realpathSync keeps 8.3 short names while realpathSync.native
expands them, and module resolvers differ in which they use. Accept either
so the stack frame matches regardless.

* fix(core): do not count a stale plugin copy's failures against the reloaded module

Each copy is stamped with the entry's import counter. After a reload (or a
source change), failures from copies of the previous import are reported to
their own session but no longer bump the error counts, block the plugin, or
change its status, so a session still running a broken copy cannot turn off
the fixed plugin for everyone else. The pre-review registry had this guard
via its generation number; the simplification dropped it.

* fix(core): don't let a stale session import turn off a reloaded plugin

A session's copy import that failed after plugins.reload (or a source change)
had already re-imported the plugin was recorded against the current module:
the catch around claimInstance had no import identity, so recordFailure
blocked the plugin and dropped the freshly loaded copy for every session.

Capture the entry's importId before claiming. If it changed by the time the
import fails, report the failure to that session only and leave the current
module's status alone, matching how stale copies' call failures are handled.

* fix(desktop): don't duplicate a queued prompt behind a plugin start warning

Session start appends plugin warnings after the optimistic user bubble. The
chat_queued_prompt_start handler searched backward for that bubble and
stopped at the first non-user row, so it hit the warning, missed the bubble,
and appended the prompt a second time until saved history replaced it.

Skip plugin warning rows in that search.

* feat(core): default a worker-thread transport for isolated plugins (#15034)

* feat(core): add a worker-thread transport for isolated plugins

CLINE_PLUGIN_MODE=worker runs each session's plugins in a worker thread
inside the host runtime. It reuses the subprocess sandbox protocol and
bootstrap, so it needs no node/bun executable on PATH and works from the
compiled CLI and hub. terminate() stops a plugin stuck in a synchronous
loop, and an uncaught error or process.exit() ends only that worker.

- WorkerSandbox mirrors SubprocessSandbox (timeouts, idle reclaim,
  per-worker pending calls) over the worker message port.
- The bootstrap talks to parentPort in a worker and posts ready once it
  listens; the host holds calls until then because Bun drops earlier
  messages.
- Workers override process.cwd() instead of chdir, which Bun would apply
  to the whole host process.
- Uncloneable values fall back to JSON, matching the subprocess IPC.
- The sandbox test suite runs against both transports, plus worker-only
  tests for loops, stray throws, process.exit, cwd, and no runtime.

* fix(core): ship a self-contained plugin bootstrap and snapshot queued worker calls

The CLI copies plugin-sandbox-bootstrap.js beside the compiled binary,
where nothing can resolve @cline/shared or jiti, so a worker (or
subprocess) exited before initializing any plugin. Bundle every package
into the bootstrap and fail the core build if it imports anything but
Node builtins.

Calls made before a worker reports ready kept the caller's args object,
so later changes leaked into the call and an uncloneable value added in
that window threw from the message listener. Queue a structured clone
instead, skip calls that already timed out, and reject a call whose
flush fails.

* test(core): give the worker loop-recovery test room for a cold worker on CI

The recovery call boots a fresh worker and re-imports the plugin within
the same tool timeout, which 200ms did not cover on CI runners.

* fix(core): keep worker plugin stdout off the host's stdout under Bun

Bun workers write console.log and process.stdout straight to the host's
stdout (Bun ignores the Worker stdout option), so a plugin's logging
corrupted --json output and JSON-RPC streams. Discard stdout-bound output
inside the worker, matching the subprocess sandbox's stdio "ignore".

* fix(core): complete discarded stdout write callbacks in plugin workers

The worker's process.stdout.write replacement dropped the completion
callback, so a plugin awaiting a write hung until the call timed out.
Call it on the next tick for both write(chunk, cb) and
write(chunk, encoding, cb).

* feat(core): run plugins in workers by default, through the plugin registry

Worker mode now goes through PluginRegistry instead of around it, so it
keeps everything in-process mode has: plugin status and plugins.list/
status/reload, plugin.status_changed, degraded/failed tracking with the
failure threshold, session notices, the per-session plugins policy,
stale-copy protection, and per-session timeouts. Only where a copy runs
changes: each plugin gets its own worker per session, and the copy the
registry wraps is a proxy to it.

One worker per plugin means a worker that dies on its own (uncaught
error, unhandled rejection, process.exit) is that plugin's failure with
no stack attribution: the plugin is turned off and the host and other
plugins keep running. The hub preloads status in throwaway workers.

- CLINE_PLUGIN_MODE defaults to worker; in_process and sandbox are
  opt-ins. Hosts that ship no bootstrap fall back to in_process.
- WorkerSandbox reports unexpected exits; sandbox tool proxies honor a
  tool's own timeoutMs.
- The bootstrap is resolved lazily and can be set with
  CLINE_PLUGIN_BOOTSTRAP_PATH. The desktop app ships it as a Tauri
  resource and points the sidecar at it; the VS Code build copies it
  next to the bundle as .mjs.

* fix(core): carry setup context, tool context, and tool definitions into plugin workers

Worker plugins lost what in-process plugins get, now that workers are the
default:

- Setup context: ctx.cwd, ctx.emitEvent, and ctx.onDispose. Cleanup runs
  in the worker before it stops (session release, shutdown, idle
  reclaim); calls arriving during an idle stop wait for a fresh worker.
- Tool context: the abort signal (the host forwards aborts by call id),
  emitUpdate (routed back to the calling tool), and emitEvent.
- Tool definitions: lifecycle, executionMode, resultPolicy, maxRetries,
  and any other plain field now reach the runtime.
- Setup gets its own timeout inside the worker. A setup that throws,
  times out, or takes the worker down fails only that session's copy,
  instead of being reported as an import failure that turns the plugin
  off for every session.

* fix(core): don't treat an exit during plugin cleanup as a crash

stopGracefully() ran the dispose method before marking the worker as
stopping, so a disposer that called process.exit() or threw from a timer
looked like an unexpected exit and turned the plugin off for every
session. Mark the worker as stopping before cleanup starts.

* fix(core): apply the session plugin policy by a worker plugin's own name

The host checked the policy against the file-derived name before the
worker had imported the plugin, so a policy keyed by the plugin's
exported name misfired: "*" off plus the plugin turned on by name
skipped it for good, and turning it off by name still ran its setup().

The worker now applies the policy after import, against both names, and
before setup. The host skips the worker up front only once it has seen
the plugin's own name for its current code.

* fix(core): keep setup prompts and cleanup for worker plugins

Worker plugins run setup() while the session bootstraps, before it is
registered, unlike in-process plugins whose setup runs on the first turn.

- Prompts a plugin sends to its session during setup (steer_message,
  queue_message, pending_prompt) found no session and were dropped. The
  host now holds them until the session is registered, then delivers them.
- A setup that registered ctx.onDispose and then threw or timed out lost
  its cleanup, since only plugins that loaded are disposed later. The
  worker now runs that cleanup right away, so a child process started
  before the failure does not outlive it.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-10-12 22:29:54 -07:00
Bee d2cbeea6ff fix(composio): discover connector tools through meta search (#15041) 2026-10-12 16:40:08 -07:00
John Choiandabeatrix d3c82673ed feat(sdk): allow host-authenticated Composio requests (#14716)
* feat(cli): add Composio connector marketplace and installation

* feat(sdk): support host-supplied Composio requests and schemas

* docs(sdk): shorten connector host options note

* fix(sdk): normalize supplied Composio schemas consistently

* test(sdk): verify local connector auth through real resolver

* test(sdk): remove supplemental local auth verification suite

* test(desktop): mock shared integration auth token resolver

* refactor(sdk): move shared Composio helpers into connector services

* fix(connectors): use API availability instead of beta access

* feat(sdk): reuse Composio meta tools with host authentication

* feat(sdk): use host connector transport during session bootstrap

* test(sdk): initialize valid connector meta session in host regression

* feat(sdk): forward connector transport to scheduled sessions

* refactor(sdk): limit connector change to host transport support

* chore(sdk): clarify local auth scope and trim host regression setup

* fix(sdk): bound connector discovery requests

* chore(sdk): remove redundant connector option comment

* refactor(sdk): simplify connector session discovery

* refactor(sdk): read connector session responses sequentially

---------

Co-authored-by: abeatrix <beatrix@cline.bot>
2026-10-12 16:07:14 -07:00
John Choi 55d32c22ab fix(desktop): allow cancelling cloud account sign-in (#14943)
* fix(desktop): allow cancelling cloud account sign-in

* fix(desktop): keep cancel label stable during sign-in cancellation

* fix(desktop): swap account creation for cancel during sign-in

* fix(desktop): keep sign-in controls visible during account refresh

* fix(desktop): keep sign-in retry responsive during account refresh

* fix(desktop): ignore stale account reloads after sign-in retry

* fix(desktop): limit sign-in changes to cancellation and stop polling

* fix(desktop): keep sign-in cancellation in existing screen handlers

* refactor(desktop): share sign-in cancellation mechanics

* refactor(desktop): remove redundant sign-in error callback

* fix(desktop): preserve sign-in cancellation across screens

* test(desktop): simplify sign-in cancellation coverage
2026-10-12 13:48:01 -07:00
Younes Beriane 9f003b3a45 fix(shared): stop billing image base64 payloads at CHARS_PER_TOKEN (#14520)
* fix(shared): stop billing image base64 payloads at CHARS_PER_TOKEN (#14512)

estimateRequestInputTokens and createTokenEstimator both derive their
token estimate from JSON.stringify(x).length, which charges an
ImageContent block's base64 data string at the 3-chars-per-token rate
used for text. A single ~90KB base64 image (a very ordinary screenshot)
was reported as ~30k tokens when real vision-model costs top out around
1.6k, so a handful of images could trip shouldCompact well before the
transcript is actually near budget.

Add createImageAwareReplacer(), a JSON.stringify replacer that blanks
the data field of any {type: "image", data: string} node wherever it's
nested, plus a counter, and use it at both call sites. Add a flat
ESTIMATED_TOKENS_PER_IMAGE (1600) back onto the char-based estimate per
image found, so images still count toward the budget without scaling
with their base64 length.

The catch-fallback path (serializeMessage) already renders images as
"[User image]: mediaType" with no base64, so this was specific to the
primary JSON.stringify path.

* fix(shared): cover AgentImagePart shape and stop fallback double count

createImageAwareReplacer only matched ImageContent's {type:'image',
data:string}. AgentMessage's image part (agent.ts) carries its payload
under 'image' instead, and agent-message-codec.ts populates it with the
same base64 string; that shape reaches estimateRequestInputTokens via
gateway.ts on every streamed request and was still billed at
CHARS_PER_TOKEN. Blank the 'image' field too when 'data' isn't present.

Separately, both call sites read imageCount() even when the primary
JSON.stringify(..., replacer) pass threw partway through and fell back
to a full re-serialization (which includes images' raw base64). Images
counted before the throw then had ESTIMATED_TOKENS_PER_IMAGE added on
top of their already-included char cost. Only capture the count when
the replacer-driven pass completes.

* test: pin the flat per-image token charge
2026-10-12 22:05:36 +02:00
John Choi a48408909b fix(desktop): keep Cloud setup cards visible (#14954)
* fix(desktop): compact signed-out cloud welcome spacing

* fix(desktop): keep cloud sign-in card near window bottom

* fix(desktop): tighten signed-out cloud onboarding spacing

* fix(desktop): use existing setup card for welcome spacing
2026-10-12 10:32:32 -07:00
Dante 03ffa9ec3e fix(sdk): require string old_text in editor schema (#14400)
* fix(sdk): require string old_text in editor schema

* fix(editor): normalize unused null old_text at runtime

* docs: clarify old_text null compatibility rationale
2026-10-12 14:50:40 +02:00
Mark Cockram 62f690d6ca fix: load and watch symlinked rule files (#14912)
* fix: load symlinked rule files from config directories

* fix(core): automatically reload symlinked instruction targets

* fix(core): drop symlink target watching, keep link-path loading

Review feedback on #14912: reading rules through the link path already
fixes the reported bug, and each new session re-reads the rules. Watching
wherever links point is a larger change than the bug needs and carried
loose ends (directory links in the target path, a readlink per file on
every refresh).

- revert the target-directory watching, chain walk and broken-link
  retention from ab59fbca4
- keep stat-based symlink acceptance in the scanners, so a broken link
  is skipped as on main
- DOC.md: edits to a symlink's target are picked up on the next refresh
  or new session
2026-10-12 13:34:17 +02:00
Saoud RizwanandSaoud Rizwan 8eba11b270 fix(schedules): stuck pending runs, silent model failures, stale editor models (#14998)
* fix(schedules): claim queued runs under bun:sqlite

claimDueRuns bound unprefixed named parameters, which bun:sqlite binds
as NULL, so the desktop hub never claimed a queued run and every
scheduled run stayed pending with no error.

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

* fix(schedules): fail runs whose agent turn ends with an error

Provider failures such as a retired model id resolve the turn with
finishReason 'error' instead of throwing, so these runs were recorded
as successful with no error message.

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

* fix(desktop): load live models in the schedule editor

The editor only used the bundled provider catalog, so it kept offering
retired models (e.g. space-bunny) that the live list no longer has.

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

* fix(schedules): skip superseded queued cron occurrences

Once queued runs are claimable again, the backlog that piled up while
claiming was broken would replay every missed slot back to back. Cancel
queued cron occurrences that a newer queued occurrence of the same
schedule supersedes so only the latest runs.

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

* fix(schedules): keep usage and tool calls in error-finish run reports

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

* test(schedules): cover claimDueRuns under bun:sqlite

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-11 16:46:46 -07:00
Bee c3a4057276 fix(llms): portable reasoning off for all providers, and thinking levels for level-only Gemini models (#14991) 2026-10-10 11:09:10 -07:00
Saoud RizwanandSaoud Rizwan bf71bf7cd9 Default to the first Cline recommended model (Sonnet 5.5 fallback) across CLI, extension, and desktop (#14959)
* Default to the first Cline recommended model (Sonnet 5.5 fallback) across CLI, extension, and desktop

- @cline/shared: CLINE_DEFAULT_MODEL_ID is now the anthropic/claude-sonnet-5.5 fallback
- @cline/llms: the cline provider's defaultModelId prefers GENERATED_CLINE_RECOMMENDED_MODELS.recommended[0]
- CLI: resolve the provider's SDK default before falling back to the first catalog entry; drop the hardcoded sonnet-4.6 fallback
- VS Code: openRouterDefaultModelId -> anthropic/claude-sonnet-5.5; cline provider falls through to the SDK default

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

* Default the SDK openrouter provider to anthropic/claude-sonnet-5.5

The VS Code extension's OpenRouter model field is populated from this SDK
default via resolveModelInfo, so the extension-side openRouterDefaultModelId
constant alone did not change the fresh-install selection.

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

* Desktop: adopt the provider catalog default model once the catalog loads

A fresh install booted on CLINE_DEFAULT_MODEL_ID and kept it when the user
skipped onboarding, so the chat bar never picked up the Cline provider's
recommended-list default. Swap the bootstrap fallback for the catalog
default unless a model was already remembered for that provider.

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

* Scope the recommended-model default to the Cline providers

Only cline/cline-pass declare a curated default; other manifests may expose
a placeholder like "default" (ollama, lmstudio), so the CLI keeps falling
back to the first catalog entry for them. The desktop bootstrap swap now also
applies only to new Cline chats, never to hydrated sessions.

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

* Test both branches of the cline default model selection

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

* Simplify: drop the desktop bootstrap swap and connector catalog lookup

The desktop app keeps CLINE_DEFAULT_MODEL_ID as its pre-onboarding default
(onboarding already stores the provider's recommended default), and the
connector only swaps its hardcoded literal for the shared constant.

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

* CLI: only the cline provider uses the SDK default before the catalog

cline-pass's manifest default is just the first bundled model, not a curated
pick, so it keeps the live catalog's own ordering.

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

* Connectors: prefer the provider's declared default over the Cline fallback

Non-Cline connector sessions with no saved model were handed a Claude id.
Use the provider manifest default when it is a real model, skipping the
"default" placeholder that catalog-less local providers expose.

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

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-09 19:14:41 -07:00
Bee f877058449 feat(core): in-chat connector authentication via Composio meta tools (#14755)
* feat(core): in-chat connector authentication via Composio meta tools

Signed-in beta sessions create a meta-tool session through the Cline API
connectors proxy and register COMPOSIO_MANAGE_CONNECTIONS and
COMPOSIO_WAIT_FOR_CONNECTIONS, so the agent can hand the user a Connect
Link when a task needs an app that is not connected yet.

* test(core): verify connector meta-tool usage telemetry

* fix(core): tell the agent when to use connector meta tools

Composio's descriptions are generic, so models answered "I don't have
access to Gmail" instead of offering a Connect Link.

* fix(core): replace Composio meta-tool descriptions with our own

Composio's descriptions require COMPOSIO_SEARCH_TOOLS first, which this
session does not expose, so models refused to start the connection flow.
Also drop the provider's session_id input; the proxy supplies it.

* feat(core): expose Composio search and multi-execute meta tools

Register every meta tool the server allows with Composio's own
descriptions and schemas, which reference each other (search, manage,
wait, multi-execute). Drops the custom descriptions.

* fix(core): require the complete Composio meta-tool flow
2026-10-09 17:50:54 -07:00
BeeandSaoud Rizwan d20b337eb5 feat(desktop): add transcript outline with shared shadcn message scroller (#14948)
* feat(desktop): add transcript outline with shared message scroller

* fix(desktop): avoid repeated outline work during streaming

* fix(ui): preserve transcript position when replacing rows

* fix(ui): respect reduced motion in MessageScrollerButton

The shared scroll-to-end button always passed shadcn's default smooth
behavior. Resolve it from prefers-reduced-motion like the outline does,
keeping an explicit behavior prop as an override.

* test(desktop): cover saved-session row swap and streaming growth

Add a regression test for continuing a saved session: history hydrates
after the provider mounts, a new prompt pins near the top, then the
activity row is swapped for the first answer row in one commit and the
viewport must stay on the new prompt. Make the streaming test grow the
transcript and deliver the resize notification instead of only changing
answer text.

* chore(ui): tidy message-scroller check in smoke-package

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-10-09 12:22:02 -07:00
Nemanja MarinkovicandNemanja Marinkovic b006326612 fix(shared): scan .claude/skills in resolveSkillsConfigSearchPaths (#14846)
Co-authored-by: Nemanja Marinkovic <devKid88@users.noreply.github.com>
2026-10-09 14:47:52 +02:00
Andreas Keller bcc76e8793 fix(core): preserve resolved model limits for compaction (#14644) 2026-10-09 13:22:36 +02:00
John Choi fa840c741c fix(desktop): sync session titles changed on the web (#14935)
* fix(desktop): sync session titles changed on the web

* fix(desktop): preserve unopened session renames during discovery

* fix(desktop): keep title refreshes ahead of React commits

* fix(desktop): limit refreshed title reconciliation to cloud sessions

* fix(desktop): keep every renamed title ahead of refresh completion

* refactor(desktop): reuse session history lookup for chat header
2026-10-08 12:16:22 -07:00
Saoud Rizwan ae9e1ef713 chore(sdk): release v0.0.92 2026-10-07 21:48:07 -07:00
Saoud Rizwan 81e9561243 chore(ui): prepare 0.2.0-next.12 release 2026-10-07 21:33:02 -07:00
Saoud Rizwan 103e025ba5 fix(ui): style Streamdown links from accent tokens with a pointer cursor (#14937) 2026-10-07 21:31:54 -07:00
Saoud RizwanandSaoud Rizwan 3debb849a7 fix(desktop): let the composer model name use the available row width (#14936)
Drop the fixed max width on the composer's model picker so long model
names show in full when the settings row has room, truncating only when
it runs out. Keep the settings row on one line instead of wrapping,
reserve room for the model and thinking controls so a long branch name
can't squeeze the model name out, and give the conversation layout a
shrinkable grid column so the composer row truncates instead of being
clipped at narrow widths.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-07 21:23:33 -07: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
Mikołaj Kondratek f5b1c64ab1 fix(core): stop fabricating a sapaicore provider from the default orchestration flag (#14904)
The legacy migration treated a defined sapAiCoreUseOrchestrationMode as
evidence that SAP AI Core was configured. The extension writes that flag
as a default for every user, so every migrated providers.json gained a
phantom "sapaicore" entry with no credentials. Candidate collection now
relies on the SAP-specific fields only.
2026-10-07 14:15:45 +02:00
Dominic CooneyandDominic Cooney a7fa147e91 fix(cli): keep --header values when cline mcp add opens the wizard (#14901)
* fix(cli): keep --header values when cline mcp add opens the wizard

Without --yes, cline mcp add opens the add wizard with the name,
transport and URL filled in, but dropped every --header value. A user
who accepted the prefilled answers saved a remote server without its
Authorization header.

The wizard now selects "Static headers" and fills in the header field.
If the user leaves that field unchanged, the headers are saved exactly
as given on the command line, including values that contain commas,
which the comma-separated field cannot represent.

* fix(cli): parse wizard headers the way --yes does

The add wizard read every header from one comma-separated field and
split it with its own rules: it split on commas, dropped malformed
headers without a word, and accepted names and empty values that
cline mcp add --yes rejects. Its earlier fix kept unedited prefilled
headers by comparing text, so one edit sent them all back through the
comma split.

The wizard now asks for one header per prompt, prefilled from
--header, and checks each with parseMcpHeaders, the parser --yes uses.
A malformed header is rejected at the prompt with the --yes error
message, and placeholder warnings appear in both paths.

The test restores CLINE_MCP_SETTINGS_PATH with vi.unstubAllEnvs, so
an unset variable is removed instead of being set to "undefined".

* fix(cli): warn about placeholder headers before their wizard prompt

The add wizard warned about a placeholder header such as
"Bearer <token>" only after every header had been entered, too late to
fix it there. It now warns just before the prompt that shows a
prefilled placeholder, and again only if the user types a new one.

Clearing a prefilled header now drops that header alone. Before, an
empty answer ended the list, so clearing the first of two prefilled
headers also dropped the second.

---------

Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
2026-10-07 20:38:24 +09:00
Mikołaj Kondratek c747d494a2 chore(llms): align the AI SDK on one release line (ai 7.0.120, provider-utils 5.0.50) (#14862)
`ai` had stayed at 7.0.79 while single `@ai-sdk/*` packages were bumped on
their own. Each such bump brought its own `@ai-sdk/provider` /
`provider-utils`, whose types no longer unify with the ones `ai` pins, so
the hand-off to `ai` needed casts. Bumping `@ai-sdk/openai` alone for #14649
would have added two more.

Move `ai` and every `@ai-sdk/*` package to the releases built on
`@ai-sdk/provider-utils` 5.0.50 (all published 2026-09-28): ai 7.0.120,
gateway 4.0.98, anthropic 4.0.67, google 4.0.84, google-vertex 5.0.97,
mistral 4.0.53, openai 4.0.79, openai-compatible 3.0.58, otel 1.0.120,
react 4.0.123, provider 4.0.19. Bedrock 5.0.98 is already on this line and
stays. One copy of each is left in the lockfile; the only other `ai` is
Langfuse's own nested copy, as before. No casts are added.

This also fixes #14649: `@ai-sdk/openai` 4.0.79 knows which GPT-6 models
can turn reasoning off and drops the setting for the rest, so commit
message generation (which always turns reasoning off) and tasks with
reasoning set to "none" stop getting a 400 from GPT-6 Astra and 6.1 Sol.
GPT-5.x and GPT-6 Sol/Luna still get "none".

It fixes #13203 the same way: `@ai-sdk/anthropic` 4.0.67 leaves
`thinking` out for Claude models that can't turn thinking off (Fable 5,
Opus 5.5) instead of sending `thinking: {type: "disabled"}`, which they
reject. Other Claude models still get `disabled`.
2026-10-07 11:45:22 +02:00
Saoud Rizwan 0a93311c66 chore(sdk): correct v0.0.91 default-model changelog 2026-10-06 22:40:15 -07:00
Saoud Rizwan 3cb29e7802 chore(sdk): release v0.0.91 2026-10-06 22:33:41 -07:00
Bee e466f25342 fix(agents): recover incomplete responses with unknown finish reasons (#14869)
* fix(agents): continue unknown AI SDK completions once

* fix(agents): add hidden user continuation after unknown finish

* fix(core): preserve active attachments during hidden continuation compaction
2026-10-06 21:54:39 -07:00
Saoud Rizwan f832e8169d fix(desktop): let keyless configured providers (local models) start sessions (#14879)
* fix(desktop): let keyless configured providers start sessions

The webview's pre-flight credential gate demanded an API key for every
provider that was not OAuth or local-auth, so a local OpenAI-compatible
endpoint (LM Studio, vLLM, Ollama) migrated from VS Code or added via
Settings failed every turn with "Missing API key" even though the
sidecar already reported it as configured via base URL + model.

Carry the catalog's host-computed `configured` readiness into the chat
config and skip the API-key requirement when the host accepts the
provider, matching the runtime's own keyless handling.

* fix(desktop): gate sessions on the shared apiKeyOptional provider fact

Address review: instead of a desktop-only providerConfigured readiness
flag threaded through the chat config, make the credential requirement
explicit in the shared provider contract.

- @cline/shared: GatewayProviderMetadata.apiKeyOptional, mirrored into
  ProviderAuthInfo via resolveProviderApiKeyOptional (same pattern as
  localCliCommand -> localCli).
- @cline/llms: declare it on openai-compatible, ollama, lmstudio, litellm,
  vertex, and bedrock.
- @cline/core: listLocalProviders carries the fact in ProviderListItem.auth;
  user-added endpoints registered from providers.json / models.json declare
  it too (their key is already optional in the desktop form).
- desktop: resolveCredentialError consults auth.apiKeyOptional and no
  longer needs the extra config field.
2026-10-06 18:48:52 -07:00
Saoud Rizwan e5dd38d81a fix(core): give stdio MCP servers a 10s initialize budget (#14877)
The 3s default connect budget silently dropped every npx/uvx-launched
MCP server on Windows, where reaching initialize through cmd.exe
routinely takes 3-6s even with a warm cache. The model never saw the
server's tools and nothing was logged. 10s covers those launchers while
the doubled worst case (newline + framed attempts) still clears the 30s
hub session.create deadline with headroom.
2026-10-06 16:38:05 -07:00
b2c7148cd9 fix(desktop): render Mermaid diagrams inline (#13721)
* feat(desktop): render Mermaid diagrams in chat

Render model-authored Mermaid fences as interactive SVGs in Desktop without depending on model image-input support.

- extract the strict lazy Mermaid DiagramPlugin into @cline/ui
- keep shared defaults diagram-free and add explicit opt-in controls
- enable copy, download, fullscreen, and pan/zoom in Desktop
- preserve initial bundle size through dynamic Mermaid imports
- cover lazy loading, strict security, failures, and streaming fences

Refs #13568

* test(desktop): harden Mermaid rendering coverage

* test(desktop): stub scrollTo for streamdown 2.6 in Markdown interaction tests

streamdown 2.6 (pulled in by main) scrolls streaming code blocks with element.scrollTo, which jsdom does not implement. Stub it the same way chat-messages.test.tsx and channels-view.test.tsx already do.

* fix(ui): stop Mermaid diagram links from bypassing host link policy

Mermaid's `securityLevel: "strict"` blocks script execution and dangerous URL
schemes, but still renders a `click <node> "https://…"` directive as a live
`<a xlink:href>` anchor inside the SVG. Streamdown injects that SVG with
`dangerouslySetInnerHTML`, so those anchors never pass through the `a`
component a product supplies — bypassing the deceptive-link confirmation and
the external-open routing that ordinary Markdown links go through, with no
rel="noreferrer", and letting a click navigate the desktop webview away from
the app itself.

A diagram label is authored independently of its destination, so these links
are deceptive by construction: the desktop app's own isDeceptiveLink rule
would flag every one of them.

- neutralize rendered diagram links in @cline/ui so they cannot navigate on
  their own, preserving an http(s) destination on data-cline-diagram-href
- reattach them in Desktop to the existing confirmation modal, which shows the
  real destination before anything opens
- recover the anchor from pointerdown: Streamdown's pan/zoom surface takes
  pointer capture, so a real click is retargeted away from the anchor; a press
  that moves more than a few pixels is treated as a pan, not a link click
- drop non-http(s) destinations rather than surfacing them, so a renderer
  change cannot widen what the app will open
- assert the packed Mermaid control shape instead of a "not false" check that
  also accepted undefined or an empty object

Verified against real Mermaid 11.16.1 in a browser: script, event-handler,
iframe, foreignObject and javascript:/data:/vbscript:/file: payloads are all
neutralized by strict mode, and `%%{init}%%` / YAML frontmatter cannot
downgrade securityLevel. The http(s) `click` anchor was the one live escape.

Stacked on #13721, which adds the shared lazy Mermaid plugin this hardens.

Refs #13568

Co-authored-by: yiaany <ilyaantonievich@gmail.com>
Co-authored-by: yiheng-kkk <lij921093@gmail.com>

* feat(ui): theme Mermaid diagrams with the Cline design system

Replace Streamdown's built-in Mermaid block in Desktop with an owned
`@cline/ui` block (`createMermaidRenderer` from
`@cline/ui/components/mermaid-block`):

- Mermaid's `base` theme with variables resolved from the live design tokens
  (`--card`, `--foreground`, `--primary`, ...), following light/dark and the
  accent palette and re-rendering when either changes; a diagram's own
  `%%{init}%%`, frontmatter `config:`, `classDef` and `style` still win
- a filename header from the fence title (```mermaid title="my-diagram"),
  frontmatter `title:`, or a name derived from the diagram
- zoom, fullscreen, copy, and PNG / `.mmd` downloads; PNGs are opaque, 2-3x,
  capped at 4096px and kept under the image attachment limit
- renders are serialized through one queue, since Mermaid is a process-wide
  singleton that is not safe to render concurrently

The owned block injects the SVG itself, so diagram link neutralization moves
into the shared render service and covers both this block and the Streamdown
diagram plugin. Desktop keeps routing diagram link clicks through the existing
confirmation modal.

Refs #13568

* fix(ui): avoid polynomial backtracking in Mermaid helpers

* fix(ui): make diagram label extraction linear-time

Replaces the backtracking label-regexes CodeQL flagged (ReDoS, PR #14757) with single-pass scanners and line-column parsing.

* refactor(ui): share the Mermaid render service and drop dead naming helper

createLazyMermaidPlugin now delegates import/retry/queueing/link-neutralization to createMermaidService instead of duplicating that path, keeping only theme-keyed config. Removes deriveMermaidDiagramName, which had no consumer outside its own tests.

---------

Co-authored-by: harrisonofcline <harrison@cline.bot>
Co-authored-by: yiheng-kkk <lij921093@gmail.com>
2026-10-06 10:50:06 -07:00
Mikołaj KondratekandDante cd80a20e96 fix(llms): send Anthropic refusal fallback only to the official Claude API (#14834)
* fix: omit refusal fallbacks for custom Anthropic endpoints (#14816)

* fix(llms): gate Anthropic refusal fallback on the official endpoint

Move the endpoint check into an isOfficialAnthropicEndpoint URL helper and
keep it in the rule's build step so custom endpoints retain the generic
fanout suppression. Add an identity test that a custom endpoint changes
nothing but `fallbacks`, plus an HTTP-boundary test through the real
handler that checks the body and the server-side-fallback beta header.

Co-authored-by: Dante <duanjl.china@gmail.com>

---------

Co-authored-by: Dante <duanjl.china@gmail.com>
2026-10-06 11:02:59 +02: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
Mikołaj Kondratek 4d04fac76a fix(llms): snap portable reasoning effort to the model's advertised levels (#14835)
OpenAI-compatible adapters send the AI SDK's top-level reasoning value to
the endpoint unchanged as reasoning_effort, but the gateway resolved that
value from the raw request. The models.dev effort ladder was only applied
when composing provider options, after portable reasoning had already been
split off, so it never reached the wire. Endpoints with a short ladder
were sent levels they reject: Kimi K3 accepts only low, high and max, yet
received medium for the default thinking level and minimal for minimal.

For the openai-compatible and cline adapters, snap the portable level to
the nearest level the model advertises. When that nearest level is max,
which the portable setting cannot carry yet, keep the requested level
instead of lowering it. Adapters with their own per-model effort maps and
models without catalog facts are unchanged.
2026-10-05 22:13:13 +02:00
Bee 12e3a468f3 fix(llms): Update OpenAI-compatible provider options key (#14784)
* Add GitHub Actions workflow to deploy Storybook

* Update path for Storybook deployment

* Update Storybook deployment workflow for dependencies

* Update Storybook build command in workflow

* Update Storybook deployment path and build command

* fix(llms): stop emitting hyphenated providerOptions keys for openai-compatible providers

`@ai-sdk/openai-compatible` 3.0.30 reads `providerOptions[<name>]` and its
camelCase alias, and logs a deprecation warning on every stream chunk when
the hyphenated raw name is present:

  DeprecationWarning: AI SDK Warning (openai-compatible.chat / ...):
  Deprecated: "providerOptions key 'openai-compatible'". Use
  'openaiCompatible' instead.

Cline wrote its thinking, reasoning and prompt-cache patches under the raw
provider id (`openai-compatible`, `vercel-ai-gateway`, user-defined ids) as
well as the camelCase alias, so every chunk of every stream through the
generic provider spammed the warning to stderr.

Providers whose options target is `openai-compatible` now write only the
camelCase bucket. The SDK reads that alias for every provider name, so no
request-body passthrough is lost. Native and community vendor targets
(`openai-codex`, `claude-code`, ...) keep their raw id and alias as before.

* fix: resolve provider options PR review concerns
2026-10-05 11:31:31 -07:00
Harsha Varma 68b24a92b7 fix(core): support windows path separators in hook discovery telemetry (#14767) (#14818) 2026-10-05 09:24:13 +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
Saoud Rizwan a7ad50bae3 chore(sdk): release v0.0.90 2026-10-01 21:14:46 -07:00
Saoud RizwanandSaoud Rizwan e205ac38b5 fix(core): revive team run lastProgressAt when loading persisted state (#14756)
Restored team runs kept lastProgressAt as an ISO string, so team_list_runs
failed with "toISOString is not a function" on any resumed team that had a
run. Revive it alongside the other run dates in all three loaders.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-01 20:41:31 -07:00