* feat(vscode): show dedicated ai& provider settings
Selecting ai& in the extension showed the generic OpenAI-compatible form
(base URL, model ID, custom headers, Azure options). Register aiand as a
known provider so it gets the standard API key + model picker form, backed
by the SDK builtin (fixed base URL https://api.aiand.com/v1, catalog models,
default zai-org/glm-5.3).
* chore: drop unused changeset file
* 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
* 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
* 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>
* 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>
* 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
* 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>
The `hook` command's dispatcher had no case for `agent_error`, which the
shared hook schema accepts and the CLI runtime emits when a run fails. A
valid `agent_error` payload fell through to the default branch, so the
command exited 1 without a JSON response. It now returns `{}` like the other
agent events. Adds the first unit tests for the command.
Fixes#14808
* 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
The TUI's startup check now accepts a provider API key from the environment
(e.g. OPENROUTER_API_KEY) when the provider declares exactly one environment
variable, as the runtime does when no key is saved. The key is checked through
isProviderSettingsUsable, so the provider's other requirements still apply.
Providers with several environment variables (Bedrock, Vertex, ...) are
unchanged, and nothing is persisted.
Related to #14923
* docs: match the CLI automation pages to the CLI
The scheduling, teams, subagents, connector and sample pages told
readers to run commands and flags the CLI doesn't have, and the
samples downloaded files that are gone.
- Scheduling: `executions` is `history`; `--workspace` is required;
provider and model are separate flags; the other create options,
subcommands and cron names are listed. Routing results now names the
chat with `--delivery-thread` or `/schedule create` in the chat. A
schedule with only `--delivery-bot` never posts anything.
- Teams: start with `/team` and resume with `cline --id`, not the
hidden `--team-name`; there is no `--no-teams`; state is in
teams.db. The SDK teams guide gets the same CLI fix.
- Subagents: the page described the old read-only research tool.
`spawn_agent` subagents can edit files and run commands without
further approval, run in the CLI and Desktop only, and are turned
off with the `spawn_agent` tool.
- Connectors: gchat, WhatsApp and Linear flag names; Discord tools
are on by default.
- Samples: inline the scripts whose download links 404; filter
`run_result` for the final answer; `cline auth` needs `--modelid`;
current model ids and prices; the issue responder runs Cline with a
read-only token and posts from a second job, like the PR reviewer.
Part of #14853.
Co-authored-by: Harshil Mistry <125483347+harshil-mistry@users.noreply.github.com>
* docs: check commenter permission, fail scripts on failed runs
The @cline issue responder admitted any OWNER, MEMBER or COLLABORATOR,
and organization members or collaborators with read access pass that
check. A secret-free authorize job now looks up the comment author's
repository permission and lets the analyze job run only for write or
admin. It checks the comment's author rather than the actor, so editing
someone else's comment doesn't start Cline on their text. The analyze
job also stops setting CLINE_DIR from runner.temp, which isn't
available at job level.
Plan mode removes the editing tools and refuses common file-changing
commands, but python -c and node -e still write files. The scheduling
and subagent pages now say so instead of promising that Plan runs
can't edit files.
When a run fails, the CLI exits 1 but run_result still carries the
error text, so jq prints it and the pipeline succeeds. The orchestration
scripts now use set -euo pipefail, wait on each background review by
PID, remove output files from earlier runs and name their inputs
instead of globbing. analyze-issue.sh sets pipefail. The PR review
pattern names the pull requests the sample skips.
* docs: wait on saved review PIDs, list the current team tools
The parallel review samples waited on $(jobs -p). Bash 3.2, the macOS
/bin/bash, leaves finished jobs out of that list, so a review that failed
fast was never waited on and the next model read an empty review. Save
$! after each background pipeline and wait on those PIDs.
The SDK teams guide listed team_delegate_task, team_check_status and
team_get_result, which no longer exist, and a JSON layout under
~/.cline/data/teams/. List the tools in TEAM_TOOL_NAMES and say that team
state is in ~/.cline/data/db/teams.db, keyed by session and restored
when the session resumes.
---------
Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
Co-authored-by: Harshil Mistry <125483347+harshil-mistry@users.noreply.github.com>
* 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.
* fix(cli): reject schedule and history options that do nothing
Several options were accepted and saved, but nothing ever read them, so
scripts that used them succeeded and silently did nothing:
- `cline schedule create|update --autonomous`, `--no-autonomous`,
`--idle-timeout` and `--poll-interval` wrote `metadata.autonomous`.
Its only reader was the scheduler package, removed in 7a042fe25.
- `--delivery-channel` wrote `metadata.delivery.channelId`, which no
connector reads. Chat-created schedules and `/whereami` stop writing
and showing it too.
- `cline history --page` has been hidden and ignored.
These options are removed, so they now fail with "unknown option".
A delivery is also rejected when it can never be delivered. Connectors
post a scheduled result only to a chat they can find from the thread,
so `--delivery-adapter` or `--delivery-bot` without a thread was saved
and then never posted. `create` and `update` now fail unless the
delivery, after merging with the schedule's existing one, has a thread
and an adapter. `create` checks before starting the hub.
`schedule import` no longer passes its file through the delivery flag
merge; it never registered those flags, so the merge only copied the
file's metadata.
Schedules saved with the old keys still load: nothing reads them, and
`update` keeps metadata it doesn't recognise.
* fix(cli): check schedule deliveries set with --metadata-json
mergeScheduleMetadata checked the delivery only when a delivery flag was
set, so create --metadata-json '{"delivery":{"adapter":"telegram"}}'
saved a delivery with no thread, which no connector ever posts. It now
takes the stored metadata, the --metadata-json object and the flags
separately, and checks the delivery whenever the JSON or the flags set
it. An update that changes neither keeps a stored delivery as it is,
so editing other metadata on an old schedule still works, and
"delivery": null removes the delivery.
* refactor(cli): check every schedule delivery input in one place
A schedule's delivery can come from the stored schedule, from
--metadata-json or an import file's metadata, and from the delivery
flags. Each path checked it differently, and import didn't check it.
connectors/schedule-delivery.ts states what the connectors read from
metadata.delivery and checks a delivery once, returning issues that name
an abstract field (adapter, threadId, ...). It also rejects an adapter
that is not a connector in the catalog, which no connector would post.
commands/schedule/delivery-input.ts merges the three inputs in order of
precedence and remembers which input set each key, so every issue is
reported as the flag or JSON path the user would change:
"--metadata-json delivery.threadId", "<file> metadata.delivery.adapter"
or "--delivery-thread <id>". All issues are reported together. One
table defines the delivery flags, their keys and their names in
messages.
A delivery that only the stored schedule sets is still kept unchecked,
and "delivery": null still removes it.
---------
Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
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>
get_git_branch only needs the current branch, but it ran listGitBranches,
which also executes git for-each-ref. Over an SSH environment each command is
its own ssh login, so the 5s branch label poll opened two fresh SSH sessions
every 5 seconds against the remote host for as long as it was connected.
Run only git branch --show-current for the label, and poll it every 30s for
SSH environments (5s stays for local). Branch switches made through the picker
still refresh immediately, as do focus and visibility changes.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* fix(desktop): stop UPX-packing the Linux SSH remote helpers
Since the Bun 1.4.2 upgrade (#14631) the compiled helper lazily reads its
embedded module payload back from its own executable at runtime. On a
UPX-packed binary those reads return NUL padding, so the helper starts but
every --remote-hub-ensure fails with "SyntaxError: Invalid character:
'\0'" and SSH remotes cannot start (desktop 0.0.38 through 0.0.44).
Ship the helpers uncompressed again and drop the upx installs from the
publish workflow. Bundles grow by roughly 175 MB until the CLI-managed Hub
replaces the helper binary.
Fixes#14815Fixes#14781
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* ci(desktop): smoke-test the bundled SSH remote helper before publishing
Run each Linux job's native remote helper through --remote-hub-ensure and
--remote-hub-stop against an isolated CLINE_DIR and discovery record. The
UPX regression started the helper fine but failed on exactly this path,
and nothing in the publish workflow exercised it.
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
---------
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
* 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.
* 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.
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.
* docs: match the CLI reference to CLI 3.0.68
The CLI reference and the pages that repeat it had drifted from the CLI.
They named a `dev` command that no longer exists, swapped the --config
and --data-dir defaults, described `connect` as an IDE adapter, said
--thinking defaults to medium, and showed a JSON output shape (say/ask
messages) that the CLI never prints. The documented `jq -r .text`
example prints only nulls.
Replace the pasted help with the 3.0.68 output and document what it
lists: --compaction, --worktree, --update, --kanban, `skill`,
`dashboard`, the `auth` options, `config <target>`, `connect --stop`
and `--restart`, `mcp add`/`remove`, the `history` subcommands and
options, and the `hub` subcommands including `drain` and `upgrade`.
Rewrite the JSON section around the agent_event and run_result lines,
with a jq filter that prints the final answer. Note that -k also saves
the key, drop the `-i` image example (-i opens the TUI), and use the
Cline provider's model id in the API auth example.
Co-authored-by: Harshil Mistry <125483347+harshil-mistry@users.noreply.github.com>
Co-authored-by: MarMar Labs <marmar9615@icloud.com>
* docs: correct CLI defaults and JSON notes from review
--retries defaults to 3, not the 6 that `cline --help` prints; note the
discrepancy under the pasted help. Without --thinking, Cline uses the
reasoning level saved from the TUI before the provider's default. Drop
--page, which the CLI accepts but ignores. Say that a --timeout stops
the run without a run_aborted line, and that scripts should skip line
types they don't use.
Use Claude Sonnet 5.5 in the auth examples, and say "in your terminal"
where the pages described commands that need one.
* fix(cli): make --help match what the CLI does
`cline --help` said --retries defaults to 6, but runs use 3. The help
now reads the same CLI_DEFAULT_MAX_CONSECUTIVE_MISTAKES constant that
main.ts passes to the session, and a test ties the two together.
--thinking now says that, without the flag, the CLI uses the level
saved in the TUI before the provider default. -k says the key is also
saved as the provider's key. `history --page` is hidden from help: the
CLI never reads it, but it stays accepted so existing scripts keep
working.
The CLI reference shows the corrected help and no longer notes the
wrong --retries default. The CLI README's -k and --thinking rows match.
* fix(cli): name /model in the --thinking help
Readers don't know what "the TUI" is. The level --thinking falls back to
is the one chosen in the model picker, which users open with /model, so
the help, the CLI reference, the overview and the README now say that.
* fix(cli): warn that cline history --page is ignored
cline history lists the newest sessions up to --limit and has never
read --page. The option stays accepted so existing scripts keep
working, but it now prints a deprecation notice to stderr. The
terminal tests that expected --page in help, or ran it as if it
selected a page, are removed.
* fix(cli): say hub upgrade does not wait for --zen tasks
cline hub upgrade waits only until no client is attached. A --zen run
detaches once the hub accepts it, and scheduled runs never attach, so
the upgrade can stop both mid-run. The help text and the hub command
lists in the docs now say so.
* docs: drop dashboard from the root help listing
cline --help no longer lists dashboard; it is cline hub dashboard.
* fix(cli): require a count for --retries; fix connect --restart example
--retries was declared with an optional value, so a bare --retries set
it to true and the CLI crashed calling .trim() on it. The option now
requires a count, and a bare --retries fails with commander's
"argument missing" error.
cline connect passes options after the channel to the channel's
connector, so "cline connect telegram --restart" never reached
connect. The examples now put --stop and --restart before the
channel, and the reference says why.
* docs: scope the JSON line format to agent runs
The JSON section read as the format of every --json, but only agent
runs print typed lines. cline history --json prints one array, and
each cline config target prints its own shape. The heading and both
pages now say the line format is for tasks, and name the others.
---------
Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>
Co-authored-by: Harshil Mistry <125483347+harshil-mistry@users.noreply.github.com>
Co-authored-by: MarMar Labs <marmar9615@icloud.com>
* 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>
`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`.
* refactor(cli): scope hub options to their subcommands
* feat(cli): move dashboard under hub and document its usage
---------
Co-authored-by: Cline <cline@users.noreply.github.com>
The CLI logger wrote to CLINE_LOG_PATH when it was set, but cline doctor
built the default path itself, so `cline doctor log` opened the wrong
file and `doctor --verbose`/`--json` read recently spawned processes
from it.
Move the path into resolveCliLogPath() in logging/log-path.ts and call
it from the logger and from both doctor readers. The module is separate
from the logger so cline doctor doesn't load pino.
The config page, the CLI reference, the proxy troubleshooting page and
the CLI README now say that `cline doctor log` opens the CLINE_LOG_PATH
file.
Co-authored-by: Dominic Cooney <55120+dominiccooney@users.noreply.github.com>