Compare commits

..

171 Commits

Author SHA1 Message Date
Max Paulus 🥪 3402e30a65 fix broken ci after directory chages 2026-06-03 14:56:15 -07:00
Dominic Cooney e48560dc21 feat(vscode): add the VS Code Language Model (vscode-lm) provider
Run Cline inference through the VS Code Language Model API (vscode.lm), enabling
models contributed by any extension that registers a language model chat
provider with VS Code. GitHub Copilot is the most common such vendor, but the
implementation is vendor-agnostic — it selects models via
vscode.lm.selectChatModels and has no Copilot-specific logic.

- VsCodeLmHandler implements the Cline SDK ApiHandler and is registered with the
  SDK handler registry; the model selector travels as a vendor/family[/version/id]
  string in modelId and is parsed back here. Selector segments are
  percent-encoded so values containing slashes round-trip intact.
- Native tool calling: tool definitions are passed to sendRequest and tool calls
  are surfaced as tool-call chunks; tool results round-trip as
  LanguageModelToolResultPart, with structured tool output serialized to text and
  a trailing user message appended when a turn ends on tool results so models can
  read the output.
- Gated to VS Code: registration is conditioned on the vscode.lm API being
  present, and the provider is hidden in the UI on hosts without it (JetBrains).

Depends on @cline/{shared,llms,agents,core} 0.0.42-nightly.1780514867, the first
published SDK build with the custom-registered-handler routing this provider
needs.
2026-06-03 14:14:06 -07:00
Ara 9520826cc1 Fix approval chat replies rendering as tool errors (#11246)
* fix(vscode): route approval chat replies as user feedback

* fix(vscode): suppress approval reply denial errors

* fix(vscode): hide rejected approval tool failures

* chore(vscode): clarify denied approval suppression helper
2026-06-03 13:47:03 -07:00
Max Paulus 🥪 10a6c06a15 Persist OpenRouter provider config via catalog hook 2026-06-03 13:19:45 -07:00
Max Paulus 🥪 b475a0d029 persist openai-codex provider model settings
- also don't show a sign in button if openai-codex is the only provider
2026-06-03 11:20:00 -07:00
Max Paulus 🥪 1820360468 Persist Cline model selections to provider config 2026-06-03 10:52:05 -07:00
Dominic Cooney 39f5e564f6 fix(vscode): declare missing direct deps @grpc/proto-loader and @opentelemetry/api-logs
Both packages are imported directly from source but were never declared in
apps/vscode/package.json, so they only resolved transitively. On a clean
install this broke:

- @grpc/proto-loader — imported by scripts/proto-utils.mjs,
  src/standalone/utils.ts and src/standalone/hostbridge-client.ts; its absence
  made `npm run protos` (and therefore the whole build) fail on a fresh checkout.
- @opentelemetry/api-logs — imported by the OpenTelemetry telemetry providers;
  its absence produced TS2307 "Cannot find module" errors under tsc.

Versions are pinned to align with the existing dependency families already
declared in this package (@grpc/grpc-js ^1.9.x → proto-loader ^0.7.13;
the @opentelemetry/* 0.56.x line → api-logs ^0.56.0). The npm and bun
lockfiles are updated accordingly (the api-logs change also dedupes several
previously-nested copies to a single hoisted entry).
2026-06-03 10:43:52 -07:00
Max Paulus 🥪 3f2fe65c19 show legacy task history that is not saved in the ~/.cline folder 2026-06-03 10:13:30 -07:00
Max Paulus 🥪 ede87d82f7 add migration telemetry 2026-06-03 10:12:20 -07:00
Ara e6bb1a14ec fix(vscode): reuse approved tool rows (#11213)
* fix(vscode): reuse approved tool rows

* fix(vscode): clear stale approved tool rows
2026-06-03 09:56:40 -07:00
Ara 42ab1b94a2 fix(llms): strip Cerebras reasoning history (#11214) 2026-06-03 09:56:40 -07:00
Max Paulus 🥪 97d8a33db0 fix unauthed user flow
- show a small sign in button if user is unauthed with any provider
2026-06-03 09:56:40 -07:00
Robin Newhouse 4961bf2898 fix(vscode): compact Codex OAuth before input cap (#11194)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 09:56:40 -07:00
Robin Newhouse 47f3654b70 fix(vscode): wire auto compact into SDK sessions (#11197)
* fix(vscode): wire auto compact into SDK sessions

* test(sdk): cover both directions of useAutoCondense task override

The previous test left the global mock at `true` for both calls, so the
`taskSettings: true` branch would have passed even if task settings were
ignored entirely. Make the mock read a mutable flag and flip it to `false`
before the second call so both override directions — task `false` over
global `true`, and task `true` over global `false` — are genuinely
exercised.

* Fix mock return type in cline-session-factory test

The getGlobalSettingsKey mock inferred a literal 'false | undefined' return type, so later mockImplementation overrides returning 'true' failed type checking (TS2345). Annotate the implementation as 'boolean | undefined' to widen the inferred mock signature.

---------

Co-authored-by: Mikołaj Kondratek <19799111+mkondratek@users.noreply.github.com>
2026-06-03 09:56:40 -07:00
Dominic Cooney 7b4a0bf40a fix(vscode): keep in-progress MCP OAuth flow across reconnects
The MCP SDK calls redirectToAuthorization() on every connection attempt,
and a single server can be reconnected repeatedly (settings watcher,
reconnect handler, restart). Regenerating the OAuth `state` on each call
replaced the state stored for a flow whose authorization URL the user may
already have open, so the completed callback failed validation with
"Invalid OAuth state".

redirectToAuthorization() now keeps an in-progress, still-fresh flow
instead of starting a new one (freshness measured from when the flow
started, never extended, so a stale flow always expires). The PKCE
verifier is pinned to the kept flow so token exchange still validates.

Also add local dev/test tooling:
- src/dev/mcp-oauth-test-server: a zero-dependency OAuth AS + MCP
  StreamableHTTP server for exercising the flow locally, with
  fault-injection flags (--auto-deny, --slow-authorize, --code-ttl).
- src/extension.ts: a debug-only globalThis.__clineHandleUri hook (gated
  on CLINE_CAPTURE_BROWSER) so the debug harness can deliver simulated
  vscode:// OAuth callbacks; documented in the harness README.
2026-06-03 09:56:40 -07:00
Dominic Cooney cf814e1479 fix(vscode): forward Bedrock region + AWS auth to the SDK gateway
Bedrock requests built by the SDK adapter dropped the AWS region and
authentication mode, so a pasted Bedrock API key (awsBedrockApiKey +
awsAuthentication "apikey") was silently ignored and requests fell
through to the SigV4 credential chain with no region.

Two bugs, both verified end-to-end against a live Bedrock endpoint via
the debug harness:

1. The SDK ProviderConfig was built with only providerId/modelId/apiKey/
   baseUrl. New bedrock-config.ts maps the legacy ApiConfiguration onto
   the SDK's structured region + aws block (including the webview's
   "credentials" radio -> SDK "iam"), wired into both inference paths
   (buildSdkProviderConfig for utility calls, buildSessionConfig for the
   main task loop).

2. The main chat path's gateway config is built by core from the
   providers.json `stored` entry, which the session's providerConfig does
   not override. A stale Bedrock entry (e.g. a legacy migration with
   region us-east-1 + SigV4 keys) silently won, sending requests to the
   wrong region (403). buildSessionConfig now persists the
   StateManager-derived Bedrock settings to providers.json so `stored` is
   authoritative. The bearer apiKey is only persisted for api-key auth to
   keep stored clean for profile/iam.

Also documents the ELECTRON_RUN_AS_NODE debug-harness gotcha in
.clinerules/general.md.
2026-06-03 09:56:40 -07:00
Max Paulus 🥪 40e64baa0a fix xai provider
- xai provider settings now properly updates providers.json
2026-06-03 09:56:39 -07:00
Max Paulus 🥪 540b9234dc updat gitignore 2026-06-03 09:56:39 -07:00
Dominic Cooney 52828aab71 fix(vscode): widen ZAiProvider getEventValue to accept VSCodeDropdown event union
The nightly 'vscode test' job (npm run ci:build -> build:webview -> tsc -b) failed on both Linux and windows-latest:

  ZAiProvider.tsx: Argument of type 'Event | FormEvent<HTMLElement>' is not assignable to parameter of type 'Event'

VSCodeDropdown's onChange supplies 'Event | React.FormEvent<HTMLElement>', but the getEventValue helper (added when the zai provider was moved to providers.json) was typed to accept only Event. The helper only reads target.value, which exists on both, so widen the parameter to the same union the dropdown provides.

Note: this only surfaces under 'tsc -b' / 'tsc --noEmit -p tsconfig.app.json'; the webview's root tsconfig.json is a solution file with files:[] so a bare 'tsc --noEmit' checks nothing.

The separate 'test / test' job failure is an aggregate gate that fails because vscode-test failed; fixing this resolves it too.
2026-06-03 09:56:39 -07:00
Dominic Cooney 33dafb193b fix(vscode): load ambient vscode LM type decls in unit-test ts-node program
The nightly CI 'Unit Tests with coverage - Linux' step (npm run test:unit) failed in ts-node compilation:

  state-keys.ts: Module 'vscode' has no exported member 'LanguageModelChatSelector'

This surfaced as a misleading 'Cannot find package @shared/...' ERR_MODULE_NOT_FOUND: Mocha tried require() first (which threw the ts-node TSError), then fell back to import(), whose ESM resolver cannot resolve the @shared/* path alias.

tsconfig.unit-test.json is driven by ts-node, which defaults to files:false and compiles modules on demand from their imports. The loose ambient augmentation in src/types/vscode-language-model.d.ts was therefore never loaded, so state-keys.ts failed to compile.

Set ts-node.files=true and add src/types/**/*.d.ts to include so the augmentation is part of the unit-test program. Mirrors the earlier tsconfig.test.json fix for the separate build toolchain.
2026-06-03 09:56:39 -07:00
Max Paulus 🥪 643d945d65 fix litellm provider 2026-06-03 09:56:39 -07:00
Max Paulus 🥪 f3a215cd0e change zai provider to user providers.json instead of statemanager 2026-06-03 09:56:39 -07:00
Dominic Cooney 4fce10248e fix(vscode): include ambient vscode LM type decls in test tsconfig
The nightly CI "Build Tests and Extension" step (npm run ci:build) failed
in compile-tests (tsc -p tsconfig.test.json) with:
  getVsCodeLmModels.ts: Property 'lm' does not exist on type 'typeof import("vscode")'
  state-keys.ts: Module '"vscode"' has no exported member 'LanguageModelChatSelector'

@types/vscode is pinned to 1.84.0, which predates the Language Model API.
The repo compensates with an ambient augmentation in
src/types/vscode-language-model.d.ts, which the main tsconfig picks up via
its 'src/**/*' include. tsconfig.test.json overrides include to only
'src/**/*.test.ts'; listing src/types under typeRoots does not load a loose
.d.ts (typeRoots only auto-loads @types-style package folders), so the
augmentation was missing from the test program.

Add 'src/types/**/*.d.ts' to tsconfig.test.json include so the augmentation
is part of the test compilation.
2026-06-03 09:56:39 -07:00
Max Paulus 🥪 154f9e0e11 fix open task conversation file 2026-06-03 09:56:39 -07:00
Max Paulus 🥪 c041089a6d fix history view bugs
- deleting entries works
- favoriting works
2026-06-03 09:56:39 -07:00
Max Paulus 🥪 62f11cd1d3 improve OCA provider
- oca provider login now works
2026-06-03 09:56:38 -07:00
Dominic Cooney ea38d1049d fix(vscode): show Start New Task after multi-iteration completion turns
The completion signal (attemptCompletionSeen) was scoped to the message
translator's per-iteration reset(), so a turn that called the completion tool
and then ran another iteration before 'done' lost the signal — the turn ended
as awaiting_followup instead of completed and the footer showed no
'Start New Task' button despite the green Task Completed box.

Make the completion signal turn-scoped: reset() only clears streaming pointers;
a new clearTurnOutcome() clears it at genuine turn/task boundaries (initTask,
reinitExistingTaskFromId, askResponse). Also recognize the SDK's built-in
submit_and_exit completion tool (summary field) alongside the VSCode
attempt_completion tool (result field).

Removes the webview-message-state design/findings docs and rewrites the
related comments to describe the system as-is.
2026-06-03 09:56:38 -07:00
Max Paulus 🥪 cfb4ef49be improve openai compatible provider settings 2026-06-03 09:56:38 -07:00
Dominic Cooney a94d1b2c08 fix(vscode): fix stuck/missing footer buttons from TurnState regressions
Webview now gates turnState by seq (a stale snapshot can no longer revert
streaming->idle), never empties a live transcript on a lone newer-epoch partial,
and routes a follow-up after completed/awaiting_followup to askResponse instead
of starting a new task.

Backend phase emission fixes surfaced by live testing:
- post state on turn end even when the done event carries no messages
- askResponse sets phase=streaming (resume/continue shows Cancel, unblocks send)
- a post-cancel turn-complete straggler no longer clobbers resumable

Adds reducer, send-routing, and session-event-coordinator unit tests. See
src/sdk/docs/webview-message-state-design.md §11 for the full debugging log.
2026-06-03 09:56:38 -07:00
Dominic Cooney 5685c2fa36 refactor(vscode): translator hygiene — suppress ask_question row, drop done synthetic ask
S7 (a + c) of the message-state redesign — removes two order-dependent hacks now that
TurnState is authoritative.

(a) ask_question / ask_followup_question are suppressed from the generic say:"tool"
    renderer (both content_start and content_end). The SdkInteractionCoordinator services
    these and emits the proper ask:"followup"; the generic tool row was an orphan partial
    that never finalized and defeated the tail heuristics. The CLI already does this.

(c) The `done` handler no longer synthesizes a trailing ask:"completion_result" — that
    was the "must be last message" hack (ENG-1887) that existed only because the webview
    inferred UI mode from the array tail. `done` now emits no transcript message and only
    signals turnComplete; the webview reads phase from TurnState (completed when
    attempt_completion was used, else awaiting_followup). The green "Task Completed" box
    still comes from the say:"completion_result" emitted at attempt_completion content_end.

Translator unit tests updated to the new contract (done → 0 messages, turnComplete=true).

Deferred to a follow-up (lower risk if left): (b) collapsing the approval ask onto the
streaming tool's id (needs cross-coordinator id threading), and (d) the mistake_limit
forced abort (now harmless since phase is authoritative). The persisted-history renderer
still appends its own trailing ask so reopened tasks show the resume affordance —
intentional.

428 SDK unit tests pass; tsc + biome clean.
2026-06-03 09:56:38 -07:00
Dominic Cooney ba28c556b4 fix(vscode): cancel raises the epoch fence before aborting (no post-cancel stragglers)
S6 of the message-state redesign. sdkHost.abort() is cooperative — the SDK can emit a
few more events after it. Previously cancelTask aborted first and only the (post-abort)
!isRunning filter stripped two trailing ask types, letting say:* stragglers land after
the resume_task ask and wedge the UI.

Now cancel raises the fence SYNCHRONOUSLY before the abort:
- SdkController.cancelTask sets turnState.phase = "resumable" (already in S4), then
- SdkTaskControlCoordinator.cancelTask calls raiseCancelFence() (epoch bump) BEFORE
  awaiting sdkHost.abort().

Any event the SDK emits after the abort request therefore carries the OLD epoch and is
dropped by the webview's convergent reducer; the authoritative phase is "resumable"
(Resume Task), independent of the message tail. Order matters and is covered by a unit
test (fence before abort).

Usage accounting is exempt from the fence — it was never gated by the message filter, so
a post-cancel usage event still bills the tokens the provider actually generated.

The legacy !isRunning ask-only filter is now redundant but kept as defense-in-depth.
428 SDK unit tests pass; tsc + biome clean.
2026-06-03 09:56:38 -07:00
Dominic Cooney 0fbcbc45f5 fix(vscode): drive webview footer + buttons from TurnState (fixes RC1)
S5 of the message-state redesign. The webview decided "thinking vs approving vs done"
and which buttons to show by inspecting the TAIL of clineMessages. Because the backend
appends bookkeeping (api_req_started usage) after content and even after approval asks,
the tail routinely meant the wrong thing — producing stuck "Thinking", vanishing
Approve/Reject, and the footer disagreeing with the buttons (RC1, reproduced on camera).

Now the webview reads the authoritative backend-owned TurnState (added in S4):

- buttonConfig: add buttonsForPhase(turnState, anchoredMessage) and the dispatcher
  getButtonConfigFromState(messages, turnState, mode). The button SET is chosen by phase;
  approval labels (Approve vs Save, Run Command, MCP, subagents) come from the anchored
  message (turnState.anchorTs). When turnState is absent (classic/older state) it falls
  back to the legacy tail-walking getButtonConfigForMessages.
- ActionButtons reads turnState from useExtensionState and uses getButtonConfigFromState.
- MessagesArea.isWaitingForResponse short-circuits to `phase === "streaming"` when
  turnState is present (and only shows the footer loader until a content row is actually
  streaming); the legacy tail inference is kept as the fallback.

Button actions (approve/reject/proceed/new_task) already send a fixed responseType
independent of clineAsk, and the SDK backend resolves the pending approval/followup
promise — so routing is correct under TurnState. Classic fallback paths are untouched.

New unit tests cover buttonsForPhase (every phase + anchored-label selection +
mistake_limit-vs-api_req_failed) and getButtonConfigFromState (prefers TurnState over a
trailing bookkeeping tail; legacy fallback). 68 webview chat tests pass; tsc + biome clean.
2026-06-03 09:56:38 -07:00
Dominic Cooney b37d8e466b feat(vscode): add authoritative TurnState (backend-owned UI mode)
S4 of the message-state redesign. Introduces the single source of truth for the
webview's UI mode so it no longer has to be inferred from the tail of clineMessages
(the root of RC1: missing/stuck "Thinking", vanishing approval buttons, footer and
buttons disagreeing).

- Add TurnPhase / TurnState to shared types and ExtensionState.turnState (rides inside
  state_json; no proto change).
- Add TurnStateTracker, owned by SdkController, sharing the one id/seq/epoch authority.
  Each transition stamps a fresh seq so the webview keeps only the newest TurnState.
- Set the phase at the exact lifecycle points where the backend knows it:
    streaming         — initTask / reinit / user responded (resolvePending*)
    awaiting_approval — handleRequestToolApproval (anchored on the ask)
    awaiting_followup — handleAskQuestion; and a turn that ends WITHOUT attempt_completion
    completed         — turn ends and attempt_completion was used
    error             — onSendError; mistake_limit
    resumable         — cancelTask (set before abort)
    idle              — clearTask
  The completed-vs-awaiting_followup decision uses
  MessageTranslatorState.wasAttemptCompletionSeen().
- getStateToPostToWebview now ships turnState in every snapshot.

This is ADDITIVE: the webview does not read turnState yet (S5 wires footer/buttons to
it and deletes the tail heuristics). Classic/legacy paths leave turnState undefined and
keep the legacy behavior. 427 SDK unit tests pass (incl. new turn-state-tracker tests);
tsc and biome clean.
2026-06-03 09:56:37 -07:00
Dominic Cooney 482ae279f8 fix(vscode): converge webview transcript via a pure reducer (fixes last-message-missing)
S3 of the message-state redesign. The webview received the same conversation over
two unordered, fire-and-forget channels — incremental partial messages and full
state snapshots — and the state handler REPLACED clineMessages wholesale ("// HACK:
Preserve clineMessages if currentTaskItem is the same"). A stale snapshot captured
before the last message landed could clobber the transcript, dropping the final
message and leaving the UI stuck on "Thinking…" (reproduced earlier on camera).

Introduce a pure convergent-replica reducer (messageReducer.ts) keyed on the three
extension-stamped quantities from S2:
  - ts    : identity / merge key
  - seq   : freshness (higher seq wins for the same ts)
  - epoch : conversation/replica fence (newer replaces, older is dropped, equal merges)

applyMessage / applyStateSnapshot are total and side-effect free:
  - older epoch  -> drop (straggler from a previous task/render)
  - newer epoch  -> replace the transcript wholesale (new task / history load)
  - same epoch   -> merge by ts keeping the higher seq; a snapshot may ADD/UPDATE
                    rows but NEVER truncate, so it can't drop a message the partial
                    stream already delivered. Stale (older stateVersion) snapshots are
                    ignored wholesale.

ExtensionStateContext now feeds both subscription callbacks through the reducer via a
replicaRef, replacing the wholesale-replace HACK and the findLastIndex append. Classic/
legacy state is unstamped (epoch 0 / version 0) and merges by ts exactly as before.

Tests (messageReducer.test.ts): deterministic cases mapping 1:1 to the bugs (stale
snapshot must not shrink the transcript; lower-seq ignored; partial->final in place;
older-epoch straggler dropped; older-version snapshot ignored), PLUS an
order-independence proof — all 120 permutations of a causal log, with duplication and
with non-final drops, converge to the same canonical state. This is the high-confidence
guarantee that the webview cannot get stuck regardless of delivery timing.

62 webview chat tests pass; tsc and biome clean.
2026-06-03 09:56:37 -07:00
Dominic Cooney ddb16f7dc6 refactor(vscode): stamp seq/epoch on messages and state; fire-and-forget delivery
S2 of the message-state redesign. Lays the groundwork for the convergent-replica
webview reducer (S3) so the webview can never get stuck on stale/out-of-order
delivery.

Stamping (extension-owned, synchronous, from the single MessageIdMinter):
- Every ClineMessage flowing to the webview is stamped in SdkMessageCoordinator with
  a fresh `seq` (freshness) and the current `epoch` (conversation/replica fence),
  before it is stored or emitted. The same object references go to both the message
  state handler and the partial-message stream, so both channels carry identical
  stamps. An updated message (partial -> final, same ts) passes through again and
  gets a higher seq, so the freshest copy always wins regardless of arrival order.
- Every state snapshot is stamped in SdkController.getStateToPostToWebview with a
  fresh `stateVersion` (sampled from the same counter) and the current `epoch`.
- `epoch` is bumped at every conversation boundary via a new
  resetMessageTranslatorAndFence() wired into the existing resetMessageTranslator
  sites (task start/clear, history open, reinit, mode rebuild, new-session
  follow-up). iteration_start streaming resets do NOT bump it.

Transport:
- ClineMessage proto gains seq/epoch (fields 24/25) and the conversions carry them.
- ExtensionState gains stateVersion/epoch (ride inside state_json, no proto change).

Fire-and-forget delivery:
- sendPartialMessageEvent and sendStateUpdate no longer await postMessage to the
  webview. A hidden/reloaded/closed webview can make postMessage hang or resolve
  false; awaiting it could stall the backend turn loop. Correctness no longer
  depends on any single delivery — the webview will be a convergent replica (S3).

All new fields are optional/default-0 so the classic/legacy path is unaffected.
Cancel's epoch bump + fence-before-abort and the remaining one-off Date.now() mint
sites are handled in later steps (S6/S2-tail). 423 unit tests pass; tsc clean.
2026-06-03 09:56:37 -07:00
Dominic Cooney 4aace9e226 refactor(vscode): unify ClineMessage id minting behind one MessageIdMinter
Message ids (ClineMessage.ts) were minted from Date.now() in two independent
generators: the live message translator (pure ++counter seeded once) and the
interaction coordinator (Math.max(Date.now(), last+1)). Because the translator
counter drifts behind wall-clock, it can later catch up to a clock-based id
minted by the interaction coordinator, producing colliding ids for different
messages. That breaks any merge-by-id scheme on the webview side.

Introduce a single process-wide MessageIdMinter (pure monotonic id/seq/epoch
counters, never reads the clock) owned by MessageTranslatorState and shared by:
- live SDK event translation,
- the interaction coordinator (tool approval / ask_question / user_feedback),
- history rendering (sdkMessagesToClineMessages).

This makes every id globally unique within the process, so regenerated history
ids never overlap live-session ids. Behavior is otherwise unchanged.

Also adds the message-state pipeline design + investigation docs under
src/sdk/docs.

S1 of the message-state redesign; seq/epoch stamping and the remaining one-off
Date.now() mint sites follow in S2.
2026-06-03 09:56:37 -07:00
Dominic Cooney 437f7eb745 docs(vscode): refine sdk-consolidation TODOs for live model fetching
After inspecting the SDK's generic models-URL fetcher
(sdk/packages/core/src/services/providers/model-source.ts
`fetchModelIdsFromSource` + `resolveModelsSourceUrl`), update the TODOs on the
bespoke refresh*Models handlers to capture the real constraint discovered:

- The SDK fetcher exists and is provider-agnostic, but returns model *ids only*
  (unknown ids get placeholder ModelInfo with no real pricing/capabilities).
- `mergeKnownModels` treats a registered `modelsSourceUrl` as the authoritative
  "installed" list (Ollama/LM Studio semantics) and DISCARDS the curated catalog
  when the live fetch returns results.

So simply registering `modelsSourceUrl` for Groq/Baseten/Hicap/HuggingFace/
Vercel/OpenRouter would regress rich model metadata. Proper consolidation needs
an SDK enhancement first (merge-mode or richer per-provider parsing), then the
extension handlers + RPCs can be deleted. refreshGroqModels.ts carries the
detailed note; the others reference it.
2026-06-03 09:56:37 -07:00
Dominic Cooney 1107df80d3 refactor(vscode): delegate Cline recommended-models fetch to the SDK; TODO others
refreshClineRecommendedModels now delegates the HTTP fetch + response
normalization + offline fallback to the SDK's fetchClineRecommendedModels
(@cline/core), removing ~80 lines of duplicated logic. The extension wrapper
keeps its distinct behavior: the CLINE_RECOMMENDED_MODELS_UPSTREAM feature-flag
gate, the in-memory TTL cache, and in-flight dedup. The proxy-aware fetch
(@/shared/net) and the configured apiBaseUrl are passed through to preserve
network/proxy behavior. The SDK's offline fallback list is identical to
CLINE_RECOMMENDED_MODELS_FALLBACK, so offline behavior is unchanged.

Because the module now imports the ESM-only @cline/core, its unit test moves
from mocha to vitest (joining the other SDK-touching models tests): added to
vitest include + mocha ignore, and fetchClineRecommendedModels added to the
vitest @cline/core stub (and the mocha/integration @cline/core mocks for
completeness). Rewrote the test vitest-native, asserting flag-gate, delegation,
and flag re-check.

Also:
- Add TODO(sdk-consolidation) notes to the remaining bespoke live-model-refresh
  handlers (Groq, Baseten, Hicap, HuggingFace, Vercel AI Gateway, OpenRouter)
  documenting the path to share them with the CLI via the SDK (register
  modelsSourceUrl) and then delete the extension-only handlers + RPCs. These
  are NOT migrated yet because the SDK does not currently live-fetch those
  providers (only ollama/lmstudio register modelsSourceUrl), so deleting them
  today would regress to the curated catalog.
- Remove an unused local type (RuleLoadPart) found while reviewing biome
  noUnusedVariables output.

tsc --noEmit clean; vitest 439 passing.
2026-06-03 09:56:37 -07:00
Dominic Cooney a1a88c4258 refactor(vscode): remove dead sapAiCoreModelDescription const
Unused leftover from the deleted SAP AI Core provider handler (found via biome noUnusedVariables). tsc + vitest green.
2026-06-03 09:56:37 -07:00
Dominic Cooney 5320885770 refactor(vscode): delete genuinely-unused telemetry/auth helpers
- services/telemetry/events/EventHandlerBase.ts: abstract base for telemetry
  event handlers whose concrete subclasses were removed with the classic task
  code; no remaining references (it was the only file left in events/).
- services/auth/AuthServiceMock.ts: test mock with no importers after the
  associated test was deleted.

Note: the rest of services/* (TelemetryService, McpHub, FeatureFlagsService,
ErrorService and their IFoo interfaces) IS live — the interfaces only looked
unreachable to the esbuild oracle because they're consumed via type-only
imports (erased at emit). Verified via importer cross-check; tsc + vitest green.
2026-06-03 09:56:37 -07:00
Dominic Cooney 6fcbd039fa refactor(vscode): remove dead cost utils and createOpenAIClient
- Delete src/utils/cost.ts (+ test): calculateApiCostAnthropic/OpenAI/Qwen had
  no consumers left after the provider handlers were removed (only the test
  referenced them).
- Remove createOpenAIClient from src/shared/net.ts (no remaining callers) and
  its now-unused openai + EnvUtils imports. The proxy-aware fetch and
  getAxiosSettings exports remain.

tsc --noEmit clean, vitest 436 passing.
2026-06-03 09:56:36 -07:00
Dominic Cooney 79ffd2f5fb chore(vscode): drop npm deps only used by deleted provider handlers
Remove dependencies that became unused after the legacy API provider handlers
and tree-sitter service were deleted (no remaining imports in src, webview, or
build config):

  @anthropic-ai/vertex-sdk, @aws-sdk/client-bedrock-runtime,
  @aws-sdk/credential-providers, @azure/identity,
  @cerebras/cerebras_cloud_sdk, @google-cloud/vertexai, @mistralai/mistralai,
  @sap-ai-sdk/ai-api, @sap-ai-sdk/orchestration, @sap-cloud-sdk/connectivity,
  ollama, tree-sitter-wasms, web-tree-sitter

Also remove the now-dead copyWasmFiles esbuild plugin (it only copied
tree-sitter WASM files for the deleted code-definition service). Kept openai,
@anthropic-ai/sdk, @google/genai, and aws4fetch — still imported by live code.

Verified: extension + standalone esbuild builds succeed, tsc --noEmit clean,
vitest 436 passing.
2026-06-03 09:56:36 -07:00
Dominic Cooney 9a83fcb4fa fix(vscode): drop dead barrel re-exports of deleted files
The previous deletion commit (2c9bd62) removed
core/assistant-message/parse-assistant-message.ts and
core/permissions/CommandPermissionController.ts, but the corresponding barrel
edits (removing their re-exports from index.ts) were dropped by lint-staged's
stash and never committed, leaving HEAD referencing deleted modules. Remove the
dead re-exports so the barrels only export live members.
2026-06-03 09:56:36 -07:00
Dominic Cooney 0ad9de2317 chore(vscode): add scripts/find-dead-src.mjs dead-code oracle
Computes src files unreachable from the shipped entry points (extension host + standalone host used by JetBrains/CLI) plus webview shared refs, using esbuild metafile reachability. Used to drive the post-SDK-migration dead-code deletions; keep for future pruning. Note: results still need a tsc-gated importer cross-check because esbuild drops import-type-only edges.
2026-06-03 09:56:36 -07:00
Dominic Cooney 1bba06ae6f refactor(vscode): delete more dead classic code (hooks, permissions, claude-code, misc)
Removes additional source files unreachable from any shipped entry point
(extension host, standalone host, webview) after the SDK migration, verified
via esbuild reachability + a per-file importer cross-check that excludes any
file still referenced by a live non-test survivor or generated/test glue, then
gated on tsc --noEmit + vitest (436 passing):

- services/ripgrep, integrations/notifications, utils/string, utils/tabFiltering
- core/assistant-message/parse-assistant-message
- core/hooks: hook-model-context, notification-hook, precompact-executor,
  PreToolUseHookCancellationError
- core/permissions/CommandPermissionController
- core/workspace/detection
- integrations/claude-code: run, message-filter
- integrations/editor: FileEditProvider, detect-omission
- integrations/misc: extract-file-content, extract-images

Dropped the now-dead re-exports from core/assistant-message/index.ts and
core/permissions/index.ts (those barrels stay; they still export live types
used by generated host glue).
2026-06-03 09:56:36 -07:00
Dominic Cooney 5575f681f2 refactor(vscode): delete dead tree-sitter code-definition service
src/services/tree-sitter/** (language parsers + queries for the classic
list_code_definition_names path) is unreachable from any shipped entry point
after the SDK migration. Verified via scripts/find-dead-src.mjs (esbuild
reachability from extension + standalone entries, plus webview shared refs) and
an importer cross-check (no live value or type importers), gated on
tsc --noEmit + vitest (436 passing).
2026-06-03 09:56:36 -07:00
Dominic Cooney 4922935564 refactor(vscode): delete dead classic system-prompt + slash-command code
The classic system-prompt builder (core/prompts/system-prompt/**), the
deep-planning prompt variants, core/prompts/commands.ts, and the
core/slash-commands handler are no longer reachable from any shipped entry
point (extension host, standalone host, or webview) after the SDK migration:
the SDK provides prompt construction (buildClineSystemPrompt) and slash-command
handling. Removed them and their orphaned tests.

Kept core/prompts/responses.ts (still live) and its tests.

Dead-code reachability was computed with scripts/find-dead-src.mjs (esbuild
metafile reachability from src/extension.ts + src/standalone/cline-core.ts,
plus webview src/shared references), then gated on tsc --noEmit + vitest.
2026-06-03 09:56:36 -07:00
Dominic Cooney 7e39120191 refactor(vscode): delete legacy API provider handlers and dead transforms
Now that buildApiHandler routes through the @cline/llms SDK, the legacy
per-provider handler classes and their supporting code are unused. Remove them:

- apps/vscode/src/core/api/providers/** (all 40+ handler classes, types, tests)
- apps/vscode/src/core/api/transform/** except stream.ts (format/stream/
  tool-call helpers only the handlers used)
- apps/vscode/src/core/api/utils/** (messages/responses API support)
- apps/vscode/src/core/api/retry.ts (+ test)
- apps/vscode/src/shared/sdk-handler-models.ts (getProviderModelFromSdk;
  only the deleted handlers' getModel() used it)

core/api now contains just index.ts (types + SDK re-exports), transform/stream.ts
(ApiStream types still referenced by the local ApiHandler interface), and
adapters/.

Supporting changes to keep everything compiling/working:
- context-window-utils: drop the dead `api instanceof OpenAiHandler` DeepSeek
  branch (handlers are SDK GatewayApiHandlers now); the 64k switch case already
  handles DeepSeek context sizing.
- Relocate fetchLiteLlmModelsInfo from the deleted litellm handler into
  core/controller/models/fetchLiteLlmModels.ts (used by refreshLiteLlmModels).
- Move the `declare module "vscode"` Language Model API augmentation (previously
  carried by the vscode-lm handler) into src/types/vscode-language-model.d.ts so
  live consumers (getVsCodeLmModels, vsCodeSelectorUtils) keep their types.
- Move the @google/genai test mock out of the deleted providers dir to
  src/test/fixtures/google-genai-mock.ts and repoint test-setup.js.
2026-06-03 09:56:36 -07:00
Dominic Cooney 8c36159c43 refactor(vscode): route buildApiHandler through the SDK; remove OpenRouter handler
Replace the legacy per-provider buildApiHandler factory with an SDK-backed
handler (apps/vscode/src/sdk/sdk-api-handler.ts) built via @cline/llms
createHandler(). The two standalone callers (commit-message generation and
explain-changes) now import buildApiHandler directly from the SDK module;
@core/api stays types-only (re-exporting SDK types) so it can keep being
imported widely without pulling the SDK runtime graph into activation.

Also:
- Delete the now-dead OpenRouterHandler and its test (createOpenRouterStream
  stays; it is still used by ClineHandler).
- buildSdkProviderConfig never sends both reasoning.effort and
  reasoning.max_tokens (some providers reject it), and supports
  disableReasoning for fast one-shot utility calls; commit-message and
  explain-changes opt in.
- commit-message generation surfaces the real SDK stream error instead of a
  generic "empty API response".
- getGitDiff: run git diff --staged even before the first commit; only gate
  the git diff HEAD fallback on having commits (fixes "no changes" for the
  initial commit of a new repo).
2026-06-03 09:56:35 -07:00
Dominic Cooney a193f19468 refactor: remove legacy Cline model overrides, wire reasoning effort through SDK
- Delete refreshClineModels.ts, refreshClineModelsRpc.ts, and test
- Remove refreshClineModelsRpc proto RPC
- Remove clineModels state from ExtensionStateContext, StateManager cache, disk
- ClineModelPicker and useOnboardingModels use SDK catalog directly
- Replace ThinkingBudgetSlider with ReasoningEffortSelector for Cline provider
- Use supportsReasoning from SDK catalog instead of hardcoded model names
- Add ProviderReasoningPatch to proto and ProviderConfigPatch contract
- Wire reasoning effort changes through writeProviderConfig to SDK ProviderSettingsManager
- Remove hardcoded Claude switch in openrouter-stream.ts
- Add supportsReasoning to supportsReasoningEffort check in openrouter-stream
2026-06-03 09:56:35 -07:00
Dominic Cooney 855d31c86f fix(vscode): load real @cline/llms in unit-test harness; fix/trim provider tests
The mocha unit-test harness stubbed @cline/llms with an empty catalog, so the SDK-migrated provider handlers (which read the real catalog) failed ~51 tests under the nightly suite.

- src/test/requires.ts now loads the real @cline/llms by resolving its package directory and requiring the ESM entry by absolute path (Node 22 require(esm)), bypassing the package's import-only exports map. This restores real catalog data to provider unit tests.
- vertexModelSupportsGlobalEndpoint: also match legacy ':' context-window/speed suffixes (e.g. claude-opus-4-7:1m), not just '@' snapshot variants.
- refreshClineModels: derive prompt-cache support from reported input_cache_read pricing for any provider, not just openai/google prefixes.
- Delete the failing tests for the classic provider handlers (bedrock cross-region/native-tool-calling, gemini metadata, cline/openrouter qwen cache, wandb unknown-model). These handlers are only reached via buildApiHandler (explain-changes + commit-message generation) and the provider/catalog domain is owned and tested by the SDK; they will be removed when buildApiHandler is retired.
2026-06-03 09:56:35 -07:00
Dominic Cooney 1acacda3f5 fix(vscode): fix webview provider-model tests and a Windows path test
The nightly publish runs the full webview-ui suite and the Windows extension suite, which surfaced failures the extension-only run does not.

- Harden useProviderModels against a missing providerModelsByProvider map so a partially-mocked ExtensionStateContext no longer crashes the hook.
- Update the SapAiCoreModelPicker and APIOptions specs to provide the provider model-list context the components now read from the SDK catalog, and seed the model ids each test asserts. Removes the now-dead @shared/api sapAiCoreModels mock.
- Compare resolveDataDir() against path.join() instead of a hardcoded POSIX path so the CLINE_DIR fallback test passes on Windows.
2026-06-03 09:56:35 -07:00
Dominic Cooney 6f2f159f7e fix(vscode): support OpenAI Compatible provider on the SDK adapter
Selecting the OpenAI Compatible provider failed with 'Unknown provider "openai"', and manually entered model ids were displayed as the catalog default (gpt-4o).

- Map the extension's 'openai' provider id to the SDK's 'openai-compatible' built-in at the SDK boundary (toSdkProviderId), and convert before handing the provider id to core when building a session config.
- Treat openai-compatible as a custom-model-id provider so model resolution honors a user-entered model id instead of coercing it to the catalog default. Adds providerAllowsCustomModelIds() as the shared signal.
- Bump @cline/core, @cline/llms, @cline/shared, @cline/agents to ^0.0.42 (which registers the openai-compatible built-in) and dedupe the dependency tree.
- Carry the tool name on reconstructed Anthropic-format tool_result blocks to satisfy the SDK's ToolResultContent contract.
- Update tests for the refreshed SDK model catalog (Gemini default).
2026-06-03 09:56:35 -07:00
Dominic Cooney 96da30d8c7 chore(vscode): clean up SDK migration branch (comments, dead code, scree, tests)
- Remove porting scree: docs/sdk-model-catalog/* planning docs, TODO-resume-session.md, and stale doc references in code/comments.

- Rewrite before/after narrative comments in the 'eternal now' style; drop transient 'Step N'/'Phase N' labels.

- Remove leftover [HistoryPerf] diagnostic logging and a dead try/catch rethrow; minor readability/naming.

- Restore .clinerules/network.md (still relevant) and update sdk-migration.md to drop dangling references.

- Fix latent circular-init TDZ in openai-codex-models (lazy catalog build).

- toggleRemoteConfigSetting no longer returns a never-resolving promise.

- Move vitest config into apps/vscode and rename script test:sdk -> test:vitest; wire it into CI.

- Repair/reimplement the SDK-adapter vitest suites (auth-service, provider-migration, sdk-task-history) to match current behavior; all 432 tests pass.
2026-06-03 09:56:35 -07:00
Dominic Cooney 7a0d48c2e4 fix(vscode): preserve provider model selection fields 2026-06-03 09:56:35 -07:00
Dominic Cooney 18a29b7563 refactor(vscode): source provider model catalogs from @cline/llms SDK (ENG-2116)
Migrates the vscode extension off its hand-curated static model catalogs
in apps/vscode/src/shared/api.ts and on to the @cline/llms SDK as the
single source of truth for provider/model metadata, end-to-end across
the extension host and the webview.

Net impact on the static catalog file:
  apps/vscode/src/shared/api.ts: 5092 -> 468 lines (~90% gone).

What changed at each layer
--------------------------

SDK / catalog plumbing (apps/vscode/src/sdk/model-catalog/):
  - New `ProviderCatalog.peekModels(providerId)` synchronous cache read.
  - `resolveModelInfo` rewritten: committed selection -> catalog peek
    -> await catalog.resolveModels on cache miss. No race with a
    background warmer; if the catalog truly has nothing, returns
    source: "unknown" and the webview renders a neutral loading state.
  - `applyHostModelInfoOverrides` is the canonical seam for the few
    fields the SDK does not yet carry. Today it carries only the Vertex
    `supportsGlobalEndpoint` allowlist (vertex-global-endpoint.ts, with
    a TODO to upstream into the SDK).
  - `ProviderListing` extended with SDK metadata (`is_popular`,
    `popular_rank`, `usage_cost_display`, `capabilities[]`) and plumbed
    through proto + conversion.

Extension-host handlers (apps/vscode/src/core/api/providers/):
  - New shared helper `apps/vscode/src/shared/sdk-handler-models.ts`:
    `getProviderModelFromSdk(providerId, requestedModelId, committedInfo?)`
    returns `{ id, info }` from `getProviderCollectionSync` with
    Vertex global-endpoint overrides applied.
  - 27 handlers converted to a one-liner `getModel()` through that
    helper. Per-handler nuances preserved:
      * Anthropic: strips `:fast` and `:1m` host-side suffixes before
        SDK lookup; carries them back on the returned id so the
        per-request betas still flip.
      * Bedrock: keeps the custom Application Inference Profile ARN
        branch; base-model info from the SDK.
      * Cerebras: keeps the `qwen-3-coder-480b-free` -> `qwen-3-coder-480b`
        paid alias.
      * Qwen / ZAi: SDK has a single catalog each; handlers keep the
        regional base-URL switch but no longer fork the catalog.
      * Wandb: keeps the "unknown id falls through to safe defaults"
        escape hatch via `MODEL_COLLECTIONS_BY_PROVIDER_ID`.

Refresh-models background tasks:
  - `refreshBasetenModels`, `refreshGroqModels`, `refreshHuggingFaceModels`
    source their offline-fallback catalog from the SDK via
    `getProviderCollectionSync` + `adaptSdkModelInfo`. Live fetch path
    unchanged; only the seeding/fallback data changed.

Webview (apps/vscode/webview-ui/):
  - `useNormalizedApiConfiguration` always routes through gRPC
    `resolveModelInfo`. Removed the `isMigratedSdkProvider` /
    `MIGRATED_SDK_PROVIDER_IDS` feature flag and the legacy
    `normalizeApiConfiguration` switch entirely.
  - New `useStaticProviderSelection` hook for the 22 settings
    components whose catalog is now SDK-driven, and
    `useDynamicProviderSelection` for the 12 dynamic-list pickers
    (openrouter, cline, openai-compatible, ollama, lmstudio, requesty,
    litellm, hicap, groq, baseten, huggingface, vercel-ai-gateway,
    aihubmix, oca, huawei-cloud-maas, dify, fireworks, together,
    vscode-lm) so all of them stop calling the legacy switch.
  - `ModelInfoView` reads its `isGemini` check via
    `useProviderModels("gemini")` instead of importing `geminiModels`.
  - `App.stories.tsx` ships a small inline fixture instead of
    importing `bedrockModels`.
  - `ExtensionStateContext` no longer seeds `groqModelsState` /
    `basetenModelsState` from the deleted catalog; the slices start
    empty and the SDK-curated catalog is layered in by the pickers at
    render time.

Misc:
  - `src/utils/model-utils.ts`: `isAnthropicModelId` consults
    `MODEL_COLLECTIONS_BY_PROVIDER_ID["anthropic"]` instead of the
    deleted `anthropicModels` map.
  - `src/shared/storage/provider-keys.ts`: `getProviderDefaultModelId`
    no longer hard-codes 24 per-provider defaults. The function now
    consults the SDK catalog and only keeps an override map for
    providers whose default is intentionally not the SDK default
    (openrouter-shared dynamic providers and local-only providers).
  - `src/shared/openai-codex-models.ts`: relative path for
    `shape-adapter` import so both the extension and webview build
    contexts resolve it identically.

Tests retargeted to assert SDK behavior, not static-map shapes
--------------------------------------------------------------
  - claude-code, anthropic, bedrock, vertex, wandb, provider-keys
    test suites had assertions tied to deleted shapes. Rewrote them
    to either assert through the SDK catalog
    (anthropic compares against `adaptSdkModelInfo(sdkCollection.models[id])`,
    wandb uses the SDK-declared default, etc.) or focus on the
    host-side semantics (bedrock's "global endpoint" block now tests
    `vertexModelSupportsGlobalEndpoint` directly).
  - claude-code test trimmed its 8 `[1m]`/version-pin variants down
    to three SDK-shaped cases. The 8 deleted assertions exercised
    extension-only model-id derivations that the SDK does not carry;
    matching the CLI's behavior was the explicit goal.
  - resolveModelInfo test rewritten around the new peek -> await
    -> unknown contract.
  - proto-lint: added missing `go_package` option to
    `proto/cline/remote_config.proto`.

Verification (npm scripts under apps/vscode/):
  - npm run protos          OK
  - npm run check-types     OK  (apps/vscode + apps/vscode/webview-ui)
  - npm run lint            OK  (biome + proto-lint)
  - npm run build:webview   OK  (tsc -b && vite build)
  - node esbuild.mjs        OK  (dist/extension.js produced)
  - Runtime smoke test: 27/27 provider collections resolve from the
    SDK with correct model counts, defaults, and usage-cost-display
    flags. openai-codex returns cost=hide as expected; every other
    provider returns cost=show.
2026-06-03 09:56:34 -07:00
Max Paulus 🥪 3d8a849f03 fix soft-lock on auth fail retry 2026-06-03 09:55:38 -07:00
Max Paulus 🥪 e7e0e2b559 fix sesion usubscriptions 2026-06-03 09:55:13 -07:00
Max Paulus 🥪 695492a97b instead of listHistory, use host.get(sessionId) instead 2026-06-03 09:55:13 -07:00
Dominic Cooney 7460d460ac sdk migration: squashed pre-2026-05-27 work
Squashed foundational SDK-migration work older than one week (author dates
up to 2026-05-26), combining the previous "squashed pre-2026-05-22 work"
base commit with subsequent older commits:

- sdk migration base (pre-2026-05-22 squash)
- fix(mcp): accept CLI-authored nested transport format, preserve oauth/metadata, improve schema error messages
- add telemetry to sdk extension
- improve task startup perf
- harden perf improvements
- remove timing code
- chore: fix lint and format on the vscode app
- fix: declare missing direct dependencies in apps/vscode
- fix integration tests
- fix(test): stub telemetry helpers in unit-test @cline/core mock
- ci: run publish-nightly job inside apps/vscode workspace
- remove old md files
- remove outdated samples
- step one for removing src/core/api folder
2026-06-03 09:55:13 -07:00
Dominic Cooney 8ae99cd69f feat(llms,core): route custom registered handlers through the agent runtime (#11235)
* feat(llms,core): route custom registered handlers through the agent runtime

Expose the handler-registry helpers (hasRegisteredHandler, getRegisteredHandler,
getRegisteredHandlerAsync, isRegisteredHandlerAsync) from @cline/llms, and have
core's createAgentModelFromConfig consult the registry: when a handler is
registered for a provider, build it via createHandler and adapt its ApiHandler
surface onto the AgentModel contract (the inverse of the gateway's
toApiStreamChunk).

This lets hosts register provider handlers that need host-only dependencies
(e.g. a vscode.lm-backed handler) and have them used by the main agent loop,
not just standalone createHandler callers.

* fix(core): resolve registered handlers lazily and avoid double finish

Address review feedback:
- createAgentModelFromConfig built the handler eagerly with the sync
  createHandler, which throws for providers registered via registerAsyncHandler.
  The adapter now accepts a handler factory and resolves it on the first stream
  via createHandlerAsync, supporting both sync- and async-registered handlers.
- Guard the adapter's catch-block finish with sawFinish so a handler that emits
  an explicit done chunk and then throws does not produce two finish events.

* fix(core): preserve thought signatures and finish-reason semantics in adapter

Further review feedback on the ApiHandler -> AgentModel adapter:
- Reasoning and tool-call thought signatures are now surfaced under
  metadata.thoughtSignature (the key downstream adapters read), instead of being
  stored as metadata.signature / dropped.
- A done chunk whose incompleteReason indicates max output tokens now maps to
  finish{reason:"max-tokens"} rather than "stop".
- A turn that ends with tool calls (no explicit done) now terminates as
  finish{reason:"tool-calls"}, matching the gateway/AI-SDK adapters.

* Apply remaining changes

* fix(core): report lazy handler-factory rejection as a finish(error) event

The lazy handler resolution (await source()) ran outside the adapter's
try/catch, so a rejecting factory (e.g. when the host API is unavailable at
stream time) escaped as a raw generator exception instead of a terminal
finish{reason:"error"} event. Move the resolution inside the try block so all
failure paths converge on the same terminal finish.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-04 01:40:59 +09:00
Max 845970ba7d improve cline provider migration (#11242)
- user's who are signed in with oauth in old extension were not properly
migrating their token. this commit handles that

Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-03 09:38:18 -07:00
Max 444a9be6ec allow baseUrl field for anthropic vendor-type providers (#11227)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-02 20:01:49 -07:00
Saoud Rizwan ade7775337 feat(cli): install official plugins by slug (#11230)
* feat(cli): install official plugins by keyword

* fix(cli): harden official plugin clone

* fix(cli): remove official plugin repo env override
2026-06-02 18:24:37 -07:00
Tomás Barreiro e147682945 Set-up global greptile rules, kanban and other SDK files (#11233)
* Set-up global greptile rules, kanban and other SDK files

* Fix path

* Fix stale path

* Update vitest workspace config
2026-06-03 03:24:07 +02:00
Saoud Rizwan ae78fb422c docs(sdk): add custom model provider plugin example (#11234)
* docs(sdk): add custom model provider plugin example

Add an OpenRouter-backed example plugin demonstrating the providers
capability and registerProvider. It registers an OpenAI-compatible
provider plus its model catalog with the gateway so the agent can run
inference against an endpoint Cline does not bundle.

Registers under a distinct id (openrouter-plugin) to avoid colliding
with the built-in openrouter provider.

* docs(sdk): drop redundant provider section from plugin examples readme

* docs(sdk): drop provider demo line from plugin examples readme

* fix: support plugin model providers

* docs: remove provider plugin demo

* docs: address provider example review
2026-06-02 18:22:06 -07:00
Tomás Barreiro 220a21bdcf Move sdk/apps/ to apps/ (#11200)
* Move the apps to the root dir

* Update all references from sdk/apps/ to apps/

* Update dependencies

* Install bun types

* Fix types

* Fix types

* Fix linter

* Ingore apps from vscode

* Fix security warning

* Fix windows install

* Enable windows dev mode

* Revert "Enable windows dev mode"

This reverts commit a46c99282e.

* Revert "Ingore apps from vscode"

This reverts commit 47f7b265d2.

* Revert "Fix windows install"

This reverts commit 1dabba1556.

* update the repo root

* fix root dir

* fix path

* fix other path

* Fix unrelated changes

* fix: address apps move follow-up blockers (#11228)

* fix: update root app command paths

* fix: include moved apps in root checks

* fix: clean up moved app path references

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-03 01:49:57 +02:00
Saoud Rizwan 6aef7f5280 docs(cli): refine supply-chain scan alerts sample (#11224)
- Fold provider/model setup into a single `cline` run; drop the auth command
- Remove the /yolo on step from the Telegram setup
- Present scheduling as two clear options (Telegram chat vs terminal with
  delivery flags)
- Clarify how to find the schedule id before triggering a test run
2026-06-02 14:57:48 -07:00
Saoud Rizwan 4e56ed6922 docs(cli): add supply-chain scan alerts sample (#11222)
* docs(cli): add supply-chain scan alerts sample

Walkthrough for scheduling the Cline CLI to run Perplexity's Bumblebee
scanner and deliver compromise alerts to Telegram. Covers installing the
CLI, cloning/building Bumblebee and how it stays read-only, the Telegram
connector, and creating a scheduled scan that texts a clean/alert verdict.

* docs(cli): drop unsupported --delivery-thread from supply-chain sample
2026-06-02 14:04:33 -07:00
Saoud Rizwan 3a0f182408 fix(cli): show skills in slash autocomplete (#11220) 2026-06-02 13:18:30 -07:00
Saoud Rizwan 1f7adbd87e feat(cli): group plugin skills in settings (#11219) 2026-06-02 13:17:05 -07:00
Saoud Rizwan b0590554da feat: add skills bundled with plugins (#11161)
* feat: discover skills bundled with plugins

* fix: scope plugin bundled skills to active plugins

* fix: prevent ancestor skill discovery for plugins
2026-06-02 12:50:15 -07:00
Ara af2454f8d9 chore: bump version and update changelog (v3.87.0) (#11211) 2026-06-02 10:19:08 -07:00
Shantanu Gontia 1a4bf98e31 Update Sambanova Models (#11008)
* Update Sambanova Models

* moved to vscode/

* fix context windows

* Update Sambanova Models

* fix context windows

* Update api.ts

* Update sambanova prices
2026-06-02 18:52:00 +02:00
Ara 4139db4127 feat: add MiniMax M3 model (#11210) 2026-06-02 09:16:38 -07:00
Saoud Rizwan d55916e3ab fix(cli): show MCP OAuth errors in TUI (#11196)
* fix(cli): surface MCP OAuth errors in TUI

* chore(cli): reuse MCP status label helper
2026-06-01 20:08:23 -07:00
Bee 386ded5126 feat(cli): bundle and serve Cline Hub dashboard with cline dashboard (#11195)
* feat(cli): bundle and serve Cline Hub dashboard with cline dashboard

Add the @cline/cline-hub workspace dependency to the CLI and build the
Hub webview as part of CLI packaging. Copy the generated dashboard assets
into platform-specific CLI distributions so the dashboard is available in
built artifacts.

Refactor the Cline Hub server startup into an exported function so the CLI
can start and stop the dashboard server programmatically.

* fix(cli): resolve dashboard webview in wrapper installs

Detect the platform-specific CLI package from the published wrapper layout
and use its bundled cline-hub webview assets when no explicit dist path is
set. Add test coverage for resolving assets via CLINE_WRAPPER_PATH.

* patches

* patch

* fix server detachHub on stop

Imported detachHub.
Changed ClineHubDashboardServer.stop to () => Promise<void>.
Made stop() idempotent with a stopped guard.
Clears the health interval.
Calls server.stop(true).
Always calls await detachHub(ctx) in a finally, so hub client teardown still happens if the HTTP server stop throws.

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-06-01 17:35:24 -07:00
WaylandYang 44e15319e4 fix(plugin-sandbox): expose CLINE_PLUGIN_IMPORT_TIMEOUT_MS env override (#11065) (#11084)
* fix(plugin-sandbox): expose CLINE_PLUGIN_IMPORT_TIMEOUT_MS env override

The hardcoded 4000 ms importTimeoutMs default is too tight on Windows
cold-start; the plugin-sandbox tests already use 30_000 ms for the same
reason. This patch lets hosts raise the ceiling via env var without
touching code or adding a CLI flag, with explicit options.importTimeoutMs
still taking precedence.

Precedence: options.importTimeoutMs > env var > 4000.

Refs: #11065

* fix(plugin-sandbox): tighten env parsing + use vi.stubEnv (PR feedback)

- Number.parseInt accepts trailing garbage ("4000ms" -> 4000); switch
  to Number() + Number.isInteger() so malformed env values fall back
  to the default instead of silently consuming the numeric prefix.
- Replace manual process.env save/restore in the regression test with
  the idiomatic vi.stubEnv() / vi.unstubAllEnvs() pattern.

Per Greptile review on #11084.
2026-06-01 17:34:13 -07:00
Saoud Rizwan e424b28702 feat(cli): add plugins slash command (#11193)
* feat(cli): add plugins slash command

* fix(cli): address plugins command review feedback
2026-06-01 16:59:38 -07:00
Robin Newhouse db9971890e Add SDK telemetry for run_commands timeouts (#11149)
* feat(sdk): add run_commands timeout telemetry

* docs(sdk): document timeout telemetry event

* docs(sdk): move telemetry catalog to core docs

* fix(sdk): omit undefined timeout telemetry fields

* fix(sdk): mark timed out run_commands unsuccessful

* docs(sdk): defer telemetry catalog entry

* fix(sdk): limit run_commands timeout success override

* fix(sdk): tighten timeout telemetry plumbing
2026-06-01 16:22:04 -07:00
Tomás Barreiro d7cc9b6155 Move bun from the sdk/ to root (#11104)
* Move bun to root

* Fix scripts and pre-commit

* Update scripts

* Update workflows

* Fix cd

* fix pre-commit

* fix cli publish
2026-06-01 22:29:41 +02:00
Saoud Rizwan 05042d3ff7 docs(sdk): add env-blocker plugin example (#11192)
* docs(sdk): add env-blocker plugin example

Adds a beforeTool hook plugin that deterministically blocks the agent
from reading .env secret files via read_files, editor, or run_commands
(e.g. cat .env), while leaving .env.example/.sample/.template readable.
Demonstrates moving a security policy out of an AGENTS.md rule (a
suggestion the model can ignore) and into the execution path.

* docs(sdk): install env-blocker globally in usage examples

A secret-protection guard is most useful applied to every project, so
drop the --cwd . project-scoped install in favor of the global default.

* docs(sdk): trim env-blocker usage docs

* docs(sdk): limit env-blocker to read paths only

It is a read blocker, so only guard read_files and run_commands.
Drop the editor case (and with it the symmetric apply_patch concern),
keeping the example focused and simple.

* docs(sdk): rename env-blocker helpers for readability

collectPaths -> extractFilePaths, collectCommands -> extractShellCommands
so the beforeTool call sites read clearly at a glance.

* docs(sdk): rename commandTouchesEnv to commandReadsEnv

* docs(sdk): drop console.error from env-blocker hook
2026-06-01 12:12:16 -07:00
aikido-autofix[bot] dc2c662de6 [Aikido] Fix 53 security issues in @xmldom/xmldom, basic-ftp, axios and 14 more (#11145)
* fix(security): update dependencies

* fix: set unbounded axios fetch adapter limits for 1.16.0

---------

Co-authored-by: aikido-autofix[bot] <119856028+aikido-autofix[bot]@users.noreply.github.com>
Co-authored-by: TheRealSpencer <spencer@cline.bot>
2026-06-01 10:15:40 -07:00
Max 4b68826cb5 update changelog and bump version (#11184)
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-06-01 09:26:14 -07:00
Mikołaj Kondratek 85be70bb25 fix(vscode): probe @vscode/ripgrep-universal layout for VS Code ≥1.122.0 (#11178)
VS Code 1.122.0 migrated its bundled ripgrep from `@vscode/ripgrep` to
`@vscode/ripgrep-universal`, which ships per-platform/arch subdirectories
(`node_modules/@vscode/ripgrep-universal/bin/<platform>-<arch>/{rg|rg.exe}`)
instead of the previous flat `node_modules/@vscode/ripgrep/bin/{rg|rg.exe}`.
The migration commit (microsoft/vscode@bf19e5ca / @c4471e24) landed on
`release/1.122` and ships in stable 1.122.0+. Microsoft hit the identical
bug in their own sandbox engines and patched it in microsoft/vscode#317978;
our `getBinaryLocation` was still on the retired layout.

Symptom: on VS Code 1.122.x, all four `checkPath` probes in extension.ts
miss, `getBinaryLocation("rg")` throws `Could not find ripgrep binary`,
and `searchFiles` returns `{results: [], errorReason: "unknown"}`. The
@-mention picker shows "No results found" immediately, regardless of
workspace or query.

Telemetry confirmed the regression bisects cleanly to the VS Code
version boundary, not any Cline release — `mention_failed` events with
`errorType=unknown` jumped from ~300/week on 1.121.0 to ~100k/week on
1.122.0/1, while JetBrains versions (which don't use this code path)
stayed flat. Every historical Cline version is affected when the user
is on 1.122+.

Fix: probe the new `@vscode/ripgrep-universal/bin/<platform>-<arch>/`
layout first (both regular and `.asar.unpacked` variants), then fall
through to the four legacy probes so users on ≤1.121.x keep working.
`<platform>-<arch>` is `${process.platform}-${process.arch}`, matching
the directory naming Microsoft documented in #317978
(darwin-arm64, darwin-x64, linux-x64, linux-arm64, linux-arm,
linux-ia32, win32-x64, win32-arm64, win32-ia32, etc.).

This also closes the residual #11105 reports that survived #11166:
Yufeng's PR moved the failure mode from `unknown` to
`ripgrep_spawn_failed` (bare `rg`/`rg.exe` on PATH fallback) but didn't
restore actual functionality for users on 1.122.x — they got
spawn-ENOENT instead of file-not-found. With this patch the bundled
binary resolves correctly and ripgrep runs as before.

Refs: https://github.com/cline/cline/issues/11105
Refs: https://github.com/cline/cline/issues/11142
Refs: https://github.com/microsoft/vscode/pull/317978
2026-06-02 00:11:02 +09:00
Saoud Rizwan c824d8380a bump version and update changelog (#11172) 2026-05-31 23:33:45 -07:00
Yufeng He fb80840086 fix: keep file search fallback alive (#11166)
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-31 23:28:28 -07:00
morning-verlu 42e4ea60db Fix marketplace getting started link (#11170)
Co-authored-by: morning-verlu <258725120+morning-verlu@users.noreply.github.com>
2026-05-31 23:12:08 -07:00
Saoud Rizwan 31a118fc0c test(core): expect Opus 4.8 default in legacy provider migration 2026-05-29 12:15:37 -07:00
Saoud Rizwan c33c3176ef chore(cli): release v3.0.15 2026-05-29 12:06:12 -07:00
Bee f5a3c591c8 chore(sdk): Model Catalog v1780081026557 (#11140)
Updated model catalog to v1780081026557 with `bun run build:models`

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-29 12:00:26 -07:00
Bee be930a69a8 feat(plugin): support rule contributions in sandbox (#11127)
* feat(plugin): support rule contributions in sandbox

Add plugin rule registration to the sandbox descriptor and handler state so
plugins can contribute static or dynamic rule content.

Update plugin installation to omit peer dependencies and use
legacy-peer-deps to avoid peer resolution failures during isolated installs.

* feat(cli): support participant mute targets in Discord

Resolve /mute and /unmute targets from Discord user mentions and raw
user IDs so a specific participant can be muted within a thread.

Update Discord system rules to guide agents toward thread-level and
participant-level mute commands, and add tests for target parsing.

* Update sdk/apps/cli/src/utils/chat-commands.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(cli): normalize addressed bot command suffixes

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-29 11:59:58 -07:00
Ara 3651fe9a55 fix: stop Discord connector after repeated errors (#11121)
* fix(cli): clear connector sessions on hub shutdown

* fix: stop Discord connector after repeated errors

Added error tracking to Discord connector to prevent spam:
- Tracks errors by message within a 1-minute window
- After 3 identical errors, shuts down connector instead of posting
- Prevents repeated error messages flooding Discord channels
- Logs shutdown reason for debugging

* fix: address Greptile feedback

1. Error tracker now per-thread (includes thread.id in key)

   - Prevents cross-thread error aggregation

   - One thread having errors will not kill the whole connector

2. Use fixed time window instead of sliding window

   - Track firstSeen timestamp, not just lastSeen

   - Prevents indefinite spam from errors every 61s

   - Window properly resets after ERROR_WINDOW_MS from first error

3. Remove redundant delete in clearBindingSessionIds

   - binding.state.sessionId already deleted in earlier block

   - Cleanup was misleading/unnecessary
2026-05-29 11:52:17 -07:00
Saoud Rizwan 526d8e9c93 fix(cli): make oauth urls clickable in tui (#11139) 2026-05-29 11:50:12 -07:00
Bee fad8271f41 feat: Cline Hub web app (#10969)
* feat: Cline Hub web app

Add a Cline Hub app for monitoring connected clients, viewing and driving sessions, streaming assistant output, and restarting the local hub. Document local, LAN, and tunnel usage with room-secret gating, ignore generated Cline cache/config data, and update lockfile entries.

* feat: Cline Hub UI

* feat: provider config schema

* run command update

* Use Workspace versions

* fix: rename routines to schedules

* feat(schedule): add routine summary and update support

Include last execution data in routine schedule overviews and cache the
summary state in the UI to reduce unnecessary reloads.

Add support for updating routine schedules from the hub server, validate
required fields, and trigger schedules asynchronously after confirming they
exist.

* UI for Connectors

* Fix UI switch for telemetryOptOut

* Expands Recent Sessions UI - allow title update

* feat: Cline Hub routes

* Add "health" and "version" routes

* Refactor server.ts nto 17 focused modules

* Provider Model list search box

* Extensions -> Customizations

* fix: restore discord connector catalog

* fix

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-29 11:25:50 -07:00
Bee d6837edda8 feat(cli): support participant mute targets in Discord (#11126)
* feat(cli): support participant mute targets in Discord

Resolve /mute and /unmute targets from Discord user mentions and raw
user IDs so a specific participant can be muted within a thread.

Update Discord system rules to guide agents toward thread-level and
participant-level mute commands, and add tests for target parsing.

* patches

* update prompt
2026-05-29 11:16:10 -07:00
Ara d0f20ca135 fix(sdk): stabilize Windows test suite (#11128) 2026-05-29 10:57:09 -07:00
Robin Newhouse b2a113a2a5 test(sdk): fix Windows CI test failures (#11132) 2026-05-29 10:46:21 -07:00
Robin Newhouse 5efa8cfd3f fix: discover symlinked SDK skill directories (#11113)
* fix: discover symlinked SDK skill directories

* test: cover circular symlink skill discovery

* test: avoid native config watchers in snapshot tests
2026-05-28 17:57:21 -07:00
Ara 9f42aea85d feat(sdk): support global AGENTS rules (#11103)
* feat(sdk): support global AGENTS rules

* fix: address global AGENTS review feedback

* fix: classify global AGENTS by exact path
2026-05-28 16:56:55 -07:00
Bee b87f61f9e4 fix(cli): stabilize core tests on Windows (#11125)
* fix(cli): stabilize core tests on Windows

Avoid several Windows-specific failure modes in the core CLI test suite.

Vitest already runs test files inside worker pools. The workspace file indexer was lazily spawning a nested worker from a transformed TypeScript module via import.meta.url, which is fragile on Windows and can cause Vitest to report only a generic worker fork crash. Disable that worker path under VITEST and use the deterministic fallback indexer for tests.

Quote process.execPath in bash executor shell-string tests. Windows Node/Bun paths commonly contain spaces, so unquoted command strings can fail under PowerShell or cmd even though they work on Unix paths.

Make detached hub probing defensive by routing probeHubServer calls through a safe wrapper, so rejected or malformed probe results are treated as unreachable instead of destabilizing startup/prewarm flows.

Also clear CLINE_RUN_AS_HUB_DAEMON in daemon test setup so tests do not inherit daemon-mode state from the surrounding CLI environment except where explicitly set.

Validation: bunx vitest run --config vitest.config.ts src/hub/daemon/index.test.ts src/services/workspace/file-indexer.test.ts src/services/workspace/mention-enricher.test.ts src/extensions/tools/executors/bash.test.ts --reporter=dot

Validation: bun run typecheck

Validation: bun run test:unit

* patch
2026-05-28 16:41:49 -07:00
Bee 38f1c7eb14 fix(cli): steer active connector sessions across turn keys (#11115)
* fix(cli): bind discord sessions to individual message authors

Resolve Discord participants from normalized message author data and persist
participant-specific thread state in bindings. Restore or create sessions per
participant so different Discord users do not accidentally share chat state.

Also add coverage for bot author handling and owner user configuration.

* patches

* fix(cli): steer active connector sessions across turn keys

Detect active connector turns by session ID when the current turn key
does not match, so replies steer the existing runtime session instead of
starting a duplicate session.

Also treat queued runtime turns as a non-error completion and log the
queued state for connector transports.

* fix(cli): steer active connector sessions across turn keys

Detect active connector turns by session ID when the current turn key
does not match, so replies steer the existing runtime session instead of
starting a duplicate session.

Also treat queued runtime turns as a non-error completion and log the
queued state for connector transports.

* add /idel
2026-05-28 13:21:09 -07:00
Robin Newhouse 81121663a4 fix(sdk): pin SAP AI provider for smoke install (#11116) 2026-05-28 13:02:26 -07:00
Bee 854ac75fe0 feat(cli): bind discord sessions to individual message authors (#11114)
* fix(cli): bind discord sessions to individual message authors

Resolve Discord participants from normalized message author data and persist
participant-specific thread state in bindings. Restore or create sessions per
participant so different Discord users do not accidentally share chat state.

Also add coverage for bot author handling and owner user configuration.

* patches
2026-05-28 12:53:37 -07:00
Ara 107f0f8337 bump version and update changelog (#11112) 2026-05-28 10:58:07 -07:00
Dominic Cooney e330695cb5 chore(codeowners): replace @candieduniverse with @dominiccooney (#11111)
Eve Killaby (@candieduniverse) has left Cline; transfer her /.github/ codeowner slot to @dominiccooney so .github changes still have four code owners able to approve.
2026-05-28 10:38:42 -07:00
Saoud Rizwan c2879dba43 feat(models): add Claude Opus 4.8 provider support (#11110)
Add claude-opus-4-8 (200k) and claude-opus-4-8:1m model variants across the
Anthropic, Claude Code, Bedrock, and Vertex catalogs, mirroring the Opus 4.7
setup (same pricing, 1M tiers, global endpoint, adaptive thinking).

- Wire the OpenRouter/Vercel AI Gateway 1m suffix handling and Cline/OpenRouter
  model refresh derivation for anthropic/claude-opus-4.8
- Register 4.8 in adaptive thinking detection so it uses the reasoning-effort
  selector path
- Bump the Claude Code "opus" alias to 4.8
- Add context window switchers in the Cline and OpenRouter model pickers
- Add provider tests for the new model ids
2026-05-28 10:37:44 -07:00
Mikołaj Kondratek f2d692cfc2 ci: gate ext-jb-test-integration auto-trigger on PR author association (#11108)
* ci: gate ext-jb-test-integration auto-trigger on PR author association

Extend the existing MEMBER/OWNER/COLLABORATOR allow-list (already used
for the /test-jetbrains comment path) to pull_request_target [opened,
reopened] as well, so the same trust model applies regardless of how
the workflow is triggered. PRs from non-trusted authors no longer
auto-trigger; a maintainer can still opt them in via /test-jetbrains.

* ci: replace hardcoded app-id with CLINE_JETBRAINS_WORKFLOW_ID var

Matches the convention already in use in cline/intellij-plugin and lets us change the App ID without touching workflow code.

* Update .github/workflows/ext-jb-test-integration.yml

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* ci: rename CLINE_JETBRAINS_WORKFLOW_KEY to CLINE_JETBRAINS_APP_KEY

The secret holds a GitHub App private key. Matches the rename of the matching app-id var in the previous commit.

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-05-28 10:36:38 -07:00
Ara 0c90bd9bcf feat: add Moonshot Kimi K2.6 model (#11109) 2026-05-28 10:17:50 -07:00
Bee 33e521e551 fix: SAP AI Core uses AI SDK community provider (CLINE-2307) (#11075) 2026-05-27 20:12:01 -07:00
Ara 9e942fbb6b Fix Discord connector registration (#11077)
* fix(cli): register discord connector

* fix(cli): scope Discord reply fallback

* docs(cli): expand Discord connector setup

* fix(cli): move Discord empty reply fallback to adapter
2026-05-27 18:47:20 -07:00
Bee 6f609e8945 fix: writeDiagnostic for logging ACP output (#11091)
Replace writeErr with writeDiagnostic for logging ACP output so that they don't show up as error.
2026-05-27 14:14:49 -07:00
Ara 762e3c42ab fix(vscode): show Qwen 3.7 Max cache support (#11079)
* fix(vscode): route Qwen cache requests

* fix(vscode): keep qwen cache alias request-scoped

* fix(vscode): mark Vercel prompt-cache models

* fix(vscode): show Qwen 3.7 Max cache support
2026-05-27 13:39:28 -07:00
Tomás Barreiro 49e8c1b324 Update CLI to 3.0.14 (#11094) 2026-05-27 12:06:31 -07:00
Tomás Barreiro 71b8f43a7a Fix OTEL variable bundling (#11092) 2026-05-27 20:58:48 +02:00
Saoud Rizwan 3068fcfedf docs(sdk): note single-file plugin dep limit and pluginPaths dir form (#11076)
Single-file plugins can only import Node builtins and @cline/*. As soon
as a plugin needs an npm dep it has to ship as a package. Adds one
sentence each to the writing-plugins guide (with dependencies in the
example package.json) and plugin-install (noting pluginPaths accepts a
package directory for fast iteration).
2026-05-27 11:21:08 -07:00
Ara 7530900166 fix: repair vscode nightly publish workflows (#11072) 2026-05-26 12:14:25 -07:00
Dominic Cooney 2b45b7b7aa Remove the VSCode Nightly (SDK) publish workflow; we are just running the regular publish workflow from the SDK branch now. (#11074) 2026-05-26 11:34:25 -07:00
Tomás Barreiro 791d238996 Move vscode to apps (#10961)
* Move all vscode related files to /apps/vscode

* Fix launch and biome

* Ignore generated files

* Remove unused icons

* fix tsconfig

* Update workflows (#10962)

* Add default branch

* Move files to the right dir

* fix: add vscode publish README placeholder

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-25 21:35:59 +02:00
Ara 9ee618111d bump version and update changelog (#11036) 2026-05-25 10:47:32 -07:00
tjandy98 4bec5931f6 add gpt-5.5 (#11032) 2026-05-25 06:30:21 -07:00
Saoud Rizwan 3115679031 feat: add DeepSeek V4 models (#11027)
* feat: add DeepSeek V4 models

* fix: align DeepSeek V4 cache pricing
2026-05-25 06:29:49 -07:00
Saoud Rizwan 8a6441fddd chore(cli): release v3.0.13 2026-05-22 17:41:54 -07:00
Saoud Rizwan 7ef5b1e7af test(llms): add provider VCR smoke tests to prevent e.g. ChatGPT regressions (#11012)
* test(llms): add provider vcr smoke tests

* fix(llms): harden provider vcr recording cleanup
2026-05-22 17:37:20 -07:00
Saoud Rizwan 3e58e7b024 fix(cli): show loading dialog for history resume (#11013) 2026-05-22 17:20:59 -07:00
Willis d16667f0b8 fix: use correct base URL for Vertex AI global endpoint with Claude models (#10288)
* fix: use correct base URL for Vertex AI global endpoint with Claude models

The AnthropicVertex SDK constructs the API hostname as
`${region}-aiplatform.googleapis.com`, which produces
`global-aiplatform.googleapis.com` when region is "global".
This hostname does not exist and returns 404.

Per Google Cloud docs, the correct global endpoint hostname is
`aiplatform.googleapis.com` (no region prefix). This fix overrides
the baseURL when region is "global" to use the correct hostname.

Fixes #10287

* chore: add changeset for vertex global endpoint fix

* fix: rebase on main and re-apply global endpoint baseURL override
2026-05-22 17:11:15 -07:00
Saoud Rizwan a740c49524 fix(cli): defer empty session creation after reset (#11000)
* fix(cli): skip empty clear session restart

* fix(cli): defer empty session creation after reset

* fix(cli): clarify session start race guard

* fix(cli): avoid re-resuming after new session reset
2026-05-22 17:02:47 -07:00
Saoud Rizwan 3bc1ee8382 feat(shared): add VCR request body contracts (#10997)
* feat(shared): add VCR request body contracts

* fix(shared): tighten VCR request body contracts

* test(shared): cover legacy VCR cassette playback
2026-05-22 16:00:36 -07:00
Saoud Rizwan ecf354c753 chore(cli): release v3.0.12 2026-05-22 15:14:29 -07:00
TheRealSpencer a66c5ee973 chore(deps): pin protobuf to 7.5.8 via overrides (#10998) 2026-05-22 15:11:06 -07:00
Saoud Rizwan 46659eebeb fix(cli): show loading dialog during model settings transitions (#10999)
* fix(cli): show loading dialog during model settings transitions

* docs(cli): explain loading dialog render yield
2026-05-22 14:54:16 -07:00
Saoud Rizwan 899ee0bfea fix(cli): add inline ask question tool prompt (#10989)
* fix(cli): inline runtime tool prompts

* fix(cli): address inline prompt review
2026-05-22 13:57:01 -07:00
Bee db4598e310 Cline SDK 0.0.42 (#10994)
Version bump
2026-05-22 13:31:48 -07:00
Saoud Rizwan 627e539266 fix(cli): bypass release age gate for manual updates (#10987)
* fix(cli): bypass release age gate for manual updates

* fix(cli): use yarn env override for update age gate
2026-05-22 13:29:25 -07:00
Saoud Rizwan 87ff61915f chore(cli): release v3.0.11 2026-05-22 13:03:25 -07:00
Saoud Rizwan a305556452 fix(llms): revert implicit output cap regression (#10990)
* fix(llms): avoid implicit output token caps

* test(llms): cover ChatGPT OAuth output token regression
2026-05-22 12:59:43 -07:00
Robin Newhouse ca12e97288 fix(cli): make config footer toggle hint contextual (#10976) 2026-05-22 12:34:48 -07:00
Bee 0c89716849 feat(core): includes tool names in tool results across messages (#10975)
* fix(llms): Use Google auth for Vertex Gemini

- Pass `providerConfig.gcp.projectId` through to Vertex Gemini as `googleAuthOptions.projectId`
- Disable Vertex API-key express mode when GCP project config is present so `google-auth-library` handles auth
- Add coverage for `AgentConfig.providerConfig.gcp` forwarding and Vertex Gemini provider creation
- Fix vertex model list only contains Claude models issue

* feat(core): includes tool names in tool results across messages

Updated `tool_result` content blocks to consistently include the `name` of the tool being executed. This change propagates through provider helpers and applies the new schema across all associated unit and live tests, ensuring proper tracking and logging of tool interactions within messages.
2026-05-22 11:36:32 -07:00
Robin Newhouse 1c401dbe1a Fix SDK LLM live provider configs (#10977) 2026-05-22 11:04:33 -07:00
Bee ed78404e4b fix(llms): Use Google auth for Vertex Gemini (#10974)
- Pass `providerConfig.gcp.projectId` through to Vertex Gemini as `googleAuthOptions.projectId`
- Disable Vertex API-key express mode when GCP project config is present so `google-auth-library` handles auth
- Add coverage for `AgentConfig.providerConfig.gcp` forwarding and Vertex Gemini provider creation
- Fix vertex model list only contains Claude models issue
2026-05-22 11:02:35 -07:00
Saoud Rizwan 0157ed9efb chore(cli): release v3.0.10 2026-05-21 20:23:07 -07:00
Saoud Rizwan 5574c95ff2 docs(cli): note ignore-scripts local pack guard quirk in publish-cli skill 2026-05-21 20:22:44 -07:00
Saoud Rizwan 31ee8eb744 feat(cli): install plugins from file URLs (#10945)
* feat(cli): install plugins from file URLs

* fix(cli): harden remote plugin installs

* docs: document plugin file URL installs

* docs: simplify plugin file URL wording

* docs: trim CLI plugin example
2026-05-21 19:58:20 -07:00
Renee Huang 7952e230ae Add Ollama API key note in TUI settings (#10947)
* Add Ollama API key note in TUI settings

* refactor: centralize provider config field metadata

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-05-21 16:27:31 -07:00
Bee 6bcfa34ba9 feat(sdk): track idle and pending session states (#10959)
* feat(runtime): track idle and pending session states

Propagate idle and pending as non-terminal session statuses across the CLI, hub records, and active-session checks. Update runtime handling so interactive sessions remain active while idle or awaiting approval instead of being treated as ended.

Removed `status` from showing up in `cline history` for now.

* hydrate: false no longer reads message files for every session.
2026-05-21 16:09:45 -07:00
Ara cf18793434 feat(sdk): add Poolside provider (#10956) 2026-05-21 16:04:38 -07:00
Saoud Rizwan a7ba25d6e1 feat(uri): add /lg-task webhook integration for LG dashboard flow (#10194)
* feat(uri): add LG task deeplink webhook integration

* feat(uri): include prompt-file path in LG task prompt

* refactor(uri): move LG webhook setup into integration service
2026-05-21 16:03:21 -07:00
Ara 2af2028861 feat: add Gemini 3.5 Flash to Gemini providers (#10955) 2026-05-21 15:43:44 -07:00
Ara 932c8e68e1 Infer Telegram bot username from token (#10954)
* fix(cli): infer Telegram bot username from token

* fix(cli): address Telegram connector review feedback

* test(cli): confirm Telegram schedule delivery bot metadata

* fix(core): publish schedule completion events to connectors

* Revert "fix(core): publish schedule completion events to connectors"

This reverts commit 0d64f037b9.
2026-05-21 15:03:36 -07:00
Bee 3cd13352eb fix(cron): publish failed schedule execution events (#10937)
* fix(cron): publish failed schedule execution events

Add cron runner execution event publishing for completed and failed runs.
Update connector adapters to react to failed schedule executions so clients are notified when scheduled work does not complete successfully.

* add unit tests
2026-05-21 14:57:26 -07:00
Tomás Barreiro 2c842328a8 Inject OTEL variables into the cli at buildtime (#10958)
* Inject OTEL variables

* Add telemetry to the nightly

* Add variables to the build script
2026-05-21 23:43:30 +02:00
Saoud Rizwan d3b3ff1c33 fix(cli): satisfy config item hook lint (#10968) 2026-05-21 14:38:36 -07:00
Robin Newhouse 2508e76af8 Fix SDK model catalog token-limit semantics ENG-2100 (#10946)
* fix(llms): preserve catalog output limits

* chore(llms): regenerate model catalog
2026-05-21 14:19:35 -07:00
Ara a702bf9b38 fix(cli): soften rejected tool call display (#10871) 2026-05-21 14:00:55 -07:00
Robin Newhouse a8cea1dca3 Fix disabled skill availability in SDK CLI ENG-2058 (#10876)
* fix(sdk): hide skills tool when skills are disabled

* fix(cli): refresh skill slash commands after toggle
2026-05-21 11:23:14 -07:00
Bee 2a351ffdd5 fix: Bedrock legacy migration for awsProfile (#10943)
The new Bedrock path is not failing because credential_process is unsupported. Both old and new code use AWS SDK v3’s fromNodeProviderChain, which can load credential_process.

The practical difference is that the migrated config does not include the AWS profile name:

```
"aws": {
  "region": "us-east-1",
  "authentication": "profile"
}
```

There is no `"profile": "bedrock"` So the new llms provider never targets [profile bedrock]. It calls fromNodeProviderChain({ ignoreCache: true, clientConfig: { region } }), which means AWS SDK will use AWS_PROFILE if present, otherwise default.

## Cause

The migration code only migrates awsProfile when legacy awsUseProfile is true. But the old extension treats profile auth as active when awsAuthentication === "profile" too:

```
profile:
  legacyGlobalState.awsAuthentication === "profile" || legacyGlobalState.awsUseProfile
    ? trimNonEmpty(legacyGlobalState.awsProfile)
    : undefined
    ```

## Fixes

The migration now preserves awsProfile when awsAuthentication === "profile", even if the old awsUseProfile flag is missing. It also treats profile-based Bedrock settings as enough to migrate Bedrock without static AWS keys.
2026-05-20 16:46:38 -07:00
Bee 69f148bad9 refactor: cache global settings reads by file metadata (#10933)
* refactor: cache global settings reads by file metadata

Avoid repeated global settings file reads by caching parsed settings and
validating the cache with path, mtime, and size. Invalidate the cache after
writes so updates remain visible, and clarify the legacy skills config name.

* refactor for performance

mtime-keyed cache of the parsed GlobalSettings — repeated reads do statSync + 4 comparisons instead of readFile + JSON.parse + zod (~30-100× speedup on hot path).
statSync(filePath, { throwIfNoEntry: false }) — avoids exception construction on missing-file path.
Cache invalidated on write — doesn't rely on filesystem mtime resolution.
loadSettingsFromDisk helper — pulls the read/parse/validate flow into one place, eliminates the previous three duplicated settingsCache = {...} assignments.
toggleDisabledTool cleaned up — single set construction + single write call, no branched copy of writeGlobalSettings.

* add unit tests for caching logic

* object freeze
2026-05-20 12:11:46 -07:00
Bee 6ca92794e1 chore: model catalog updated 1779302019893 (#10934)
All files automatically changed and formatted by `cd sdk && bun run build:models`

Generated model catalog version updated to 1779251127504

This includes the new X AI build
2026-05-20 11:52:25 -07:00
Bee 27bd4c6c65 chore: generated model catalog update (#10921)
version 1779251127504
2026-05-20 09:03:39 -07:00
Saoud Rizwan 015b61924f chore(cli): release v3.0.9 2026-05-19 19:48:23 -07:00
Saoud Rizwan d238239a0f fix(core): handle hub abort cleanup failures (#10918) 2026-05-19 19:38:42 -07:00
Bee e81c35d7c0 fix: Speed up CLI plugin loading and config toggles ENG-2082 (#10884)
* fix: Speed up CLI plugin loading and config toggles

Load sandboxed plugins concurrently during initialization while preserving
existing duplicate override ordering. Update plugin tool discovery to use a
single sandbox per listing and cache descriptor results by plugin path stats,
provider, and model.

Make CLI plugin/tool config toggles persistence-only from the data loader and
update the TUI optimistically, avoiding full config reloads and repeated plugin
imports when users disable tools or plugins.

* patches

* fix: refresh plugin tools when config update lacks data

Reload config data with plugin tools included when a plugin action does not return updated data. This keeps the config view in sync and clears stale plugin tool errors after refresh.

* fix(cli): preserve config item state on missing toggle data

Only update the dialog item when toggle responses include a matching item. This avoids applying fallback enabled-state changes that can desync the UI when returned config data is missing or incomplete.

---------

Co-authored-by: Tomás Barreiro <52393857+BarreiroT@users.noreply.github.com>
2026-05-19 18:18:47 -07:00
Ara 1ac5525fee fix: route Poolside Laguna models through next-gen prompts (#10910) 2026-05-19 16:40:19 -07:00
Saoud Rizwan a4d0937030 fix(cli): restore fuzzy file mention ranking (#10909)
* fix(cli): restore fuzzy file mention ranking

* fix(cli): address mention autocomplete review feedback
2026-05-19 16:09:43 -07:00
Bee 6d5c61f044 chore: gitignore SDK session and database files (#10907)
Ignore .cline session data, temp directories, SQLite artifacts, and generated metadata to prevent local SDK state and user data from being committed.
2026-05-19 13:54:53 -07:00
Mark Percival 8a6d031e8f fix(cli): keep interactive session live after cancel (#10903)
* fix(cli): keep interactive session live after cancel

* fix(sdk): use shared finish reason type
2026-05-19 13:44:59 -07:00
Renee Huang 3f808b369e sdk: add ClineCore CLI agent example (#10895)
* add a separate CLI agent using ClineCore, in comparison to the one built using Agent

* update SDK lockfile for ClineCore CLI example
2026-05-19 13:22:04 -07:00
Robin Newhouse e3b3e2306e fix(cli): accept dash-prefixed prompts after separator (#10905) 2026-05-19 12:47:07 -07:00
Tomás Barreiro b601b6e623 Update diff to 8.0.4 (#10904)
* Update diff to 8.0.4

* Remove types diff
2026-05-19 21:30:18 +02:00
Robin Newhouse ed008bd36e Route GLM thinking via provider metadata ENG-2019 (#10692)
* Route GLM thinking via provider metadata

* Address GLM provider routing review feedback
2026-05-19 12:12:51 -07:00
Saoud Rizwan 6e964c3ef0 chore(cli): release v3.0.8 2026-05-19 10:39:16 -07:00
2881 changed files with 124710 additions and 132855 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"claude-dev": patch
---
fix: use correct base URL for Vertex AI global endpoint with Claude models
+128
View File
@@ -0,0 +1,128 @@
# Debug Harness
HTTP-controlled debugger for the VSCode extension at `src/dev/debug-harness/server.ts`.
## Quick start
```bash
# Build extension first if needed (protos + esbuild):
npm run protos && IS_DEV=true node esbuild.mjs
# Launch (skip-build if already built):
npx tsx src/dev/debug-harness/server.ts --skip-build --auto-launch
# In another terminal:
curl localhost:19229/api -d '{"method":"status"}'
```
## Data Isolation
The debugee runs with `CLINE_DIR=~/.cline2` by default, separate from your real `~/.cline`.
This prevents the debugee's logout from logging out the debugger, and vice versa.
Override with `--cline-dir /tmp/test-dir`. Check with `status()``clineDir`.
## Browser Capture & OAuth
The debugee runs with `CLINE_CAPTURE_BROWSER=1`, which intercepts `openExternal()` in
`src/utils/env.ts`. URLs are captured instead of opening a real browser:
- Logged to `$CLINE_DIR/data/debug-captured-urls.jsonl`
- POSTed in real-time to `/captured-url` on the harness server
- Queryable via `oauth.captured_urls`
### OAuth API
- **`oauth.captured_urls`** `{clear?}` — URLs the debugee tried to open
- **`oauth.read_stored_token`** — Check auth token presence in secrets.json
- **`oauth.simulate_callback`** `{path, code?, state?, provider?, token?}` — Build vscode:// callback URI
- **`oauth.read_captured_urls_file`** — Read on-disk JSONL of captured URLs
### OAuth testing flow
For **Cline OAuth** (SDK local callback): The SDK starts a local HTTP server, the auth URL
is captured. To complete: open the captured URL in a real browser (it redirects back to the
SDK's callback server), OR extract the callback port and `curl http://127.0.0.1:PORT/callback?code=...`.
For **MCP/Provider OAuth** (vscode:// URI): The redirect goes to a vscode:// URI.
`oauth.simulate_callback` only *builds* the URI — it does not deliver it, and the ESM
extension host can't `require()` the handler. To actually deliver the callback, call the
debug-only hook via `ext.evaluate` (with `awaitPromise: true`):
`globalThis.__clineHandleUri("vscode://saoudrizwan.claude-dev/...?code=...&state=...")`.
It runs the same `SharedUriHandler.handleUri` as VSCode's real URI handler and exists only
when `CLINE_CAPTURE_BROWSER` is set (the harness always sets it; never ships in prod).
For end-to-end MCP OAuth, get a real `code` from the local MCP OAuth test server
(`npm run dev:mcp-oauth-test-server`).
## Navigating Views — Use Commands, Not Clicks
Don't try to find/click small sidebar icons. Use VSCode commands via command palette.
Registered in `src/registry.ts`:
| Command | View |
|---------|------|
| `cline.accountButtonClicked` | Account / sign-in |
| `cline.historyButtonClicked` | Task history |
| `cline.settingsButtonClicked` | Settings |
| `cline.mcpButtonClicked` | MCP servers |
| `cline.plusButtonClicked` | New task (chat) |
| `cline.worktreesButtonClicked` | Worktrees |
```bash
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
```
## Key commands
All via `POST localhost:19229/api` with `{"method":"...", "params":{...}}`:
- **`launch`** / **`shutdown`** — lifecycle
- **`ui.screenshot`** — screenshot to `/tmp/cline-debug/`; returns `{path}`**use `read_file` on the path to examine, do NOT `open` the file** (Preview.app covers the VSCode window)
- **`ui.open_sidebar`** — open the Cline sidebar
- **`ext.set_breakpoint`** `{file, line, condition?}` — breakpoint by source file (sourcemap-resolved)
- **`ext.evaluate`** `{expression, callFrameId?}` — eval in extension host
- **`ext.resume`** / **`ext.step_over`** / **`ext.step_into`** — stepping
- **`ext.call_stack`** — inspect when paused
- **`web.evaluate`** `{expression}` — eval in webview
- **`web.post_message`** `{message}` — send postMessage to extension host via exposed vsCodeApi
- **`wait_for_pause`** `{timeout?}` — block until breakpoint hit
- **`ui.locator`** `{role?, testId?, text?, frame?}` — Playwright locator (auto-retries on stale sidebar frame)
- **`ui.react_input`** `{text, selector?, clear?, submit?}` — set React textarea value via `execCommand('insertText')`; works reliably across multiple tasks
- **`ui.send_message`** `{text, images?, files?, responseType?}` — send chat message bypassing the textarea entirely (via gRPC postMessage)
- **`ui.command_palette`** `{command}` — run VSCode command
## Typical Session
```bash
# 1. Launch
curl localhost:19229/api -d '{"method":"launch","params":{"skipBuild":true}}'
# 2. Open sidebar + dismiss overlays (ALWAYS do this first)
curl localhost:19229/api -d '{"method":"ui.open_sidebar"}'
curl localhost:19229/api -d '{"method":"web.evaluate","params":{"expression":"document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
# 3. Navigate to view
curl localhost:19229/api -d '{"method":"ui.command_palette","params":{"command":"cline.accountButtonClicked"}}'
# 4. Check captured OAuth URLs if testing auth
curl localhost:19229/api -d '{"method":"oauth.captured_urls"}'
# 5. Verify
curl localhost:19229/api -d '{"method":"ui.screenshot"}'
```
## Caveats
- **⚠️ Dismiss promotional overlays FIRST**: On fresh launches, full-screen promo overlays block the sidebar. **Dismiss immediately after `ui.open_sidebar`**, before any other interaction or screenshot. May need to run twice:
```bash
curl localhost:19229/api -d '{"method": "ui.open_sidebar"}'
curl localhost:19229/api -d '{"method": "web.evaluate", "params": {"expression": "document.querySelectorAll(\".sr-only\").forEach(el => el.parentElement?.click())"}}'
```
- **Screenshots — don't open the file**: `ui.screenshot` and `ui.sidebar_screenshot` save PNGs to `/tmp/cline-debug/` and return the `{path}`. Use `read_file` on that path to examine screenshots. Running `open <path>` launches Preview.app on macOS which covers the VSCode window.
- **Scripts count = 0 after launch**: CDP connects after extension host starts, so scripts parsed during startup aren't tracked. Breakpoints still work via sourcemap resolution.
- **Port 9230**: Extension host inspector. If another VSCode instance uses this port, the harness will fail to connect. Kill other debug instances first.
- **macOS only** for now (Playwright Electron launch behavior).
- **Webview CDP**: `connect_webview` may fail depending on Electron version. `web.evaluate` still works via Playwright's `frame.evaluate()` fallback.
- **Sourcemap paths**: esbuild outputs relative paths like `../src/extension.ts` in the sourcemap. The resolver handles this, but if a file isn't found, use `ext.source_files` to see exact paths.
- **OAuth with fake codes**: Browser capture intercepts the URL but doesn't provide a valid auth code. For real OAuth testing, open the captured URL in a browser. For unit testing, mock the token exchange.
See `src/dev/debug-harness/README.md` for full API reference.
+89 -87
View File
@@ -13,11 +13,55 @@ This file is the secret sauce for working effectively in this codebase. It captu
**What NOT to add:** Stuff you can figure out from reading a few files, obvious patterns, or standard practices. This file should be high-signal, not comprehensive.
## Miscellaneous
- Avoid provider-specific string matching / hardcoded provider branches when fixing provider/config plumbing. Prefer provider metadata, shared catalog/defaults, explicit protocol/client capabilities, or centralized normalization utilities that apply by data shape rather than `providerId === "..."`. If a provider exception seems necessary, stop and explain why instead of adding ad-hoc string matching.
- This is a VS Code extension—check `package.json` for available scripts before trying to verify builds (e.g., `npm run compile`, not `npm run build`).
- When creating PRs, contributors should not create changelog-entry files. Maintainers handle release versioning and changelog curation during the release process.
- When adding new feature flags, see this PR as a reference https://github.com/cline/cline/pull/7566
- Additional instructions about making requests: @.clinerules/network.md
## Searching the Codebase — Avoiding Build Output
Several directories contain build output or generated code that produces
noisy or unusable results with `search_files` / `grep`:
| Directory | What it is | Why it's a problem |
|-----------|-----------|-------------------|
| `out/` | esbuild bundle output | Mirrors `src/` structure as minified JS — every search gets duplicate hits on single-line files |
| `dist/` | Packaged extension | Entire extension bundled into one minified `extension.js` (~1 long line) |
| `dist-standalone/` | Standalone build output | Same minification issue |
| `src/generated/` | Generated protobuf code | Auto-generated from `proto/`; not the source of truth |
| `src/shared/proto/` | Generated proto type defs | Auto-generated from `proto/`; not the source of truth |
| `node_modules/` | Dependencies | Huge, not project source |
### How to skip build output
**`search_files`** — Point at `src/` (not the project root) and use `file_pattern`:
```
search_files(path="src/core", regex="myFunction", file_pattern="*.ts")
```
The `file_pattern` parameter is the most effective filter — e.g. `"*.ts"`,
`"*.tsx"`, `"*.proto"`.
**`grep` directly** — Exclude build dirs and restrict to source extensions:
```bash
grep -rn "myFunction" src/ --include="*.ts" --exclude-dir={out,dist,node_modules,generated}
```
### When you must search minified files
Sometimes you need to verify what got bundled (e.g., checking if a change
made it into the build). Minified files are typically one long line, so
normal `grep` shows the entire file as context. Use these approaches:
- **`grep -oP`** to extract just the match with limited surrounding context:
```bash
grep -oP '.{0,40}myFunction.{0,40}' dist/extension.js
```
- **`read_file`** on files in `out/src/` — these have source maps and are
more readable than `dist/extension.js` (which is the fully bundled output).
- **Source maps** — `out/src/*.js.map` and `dist/extension.js.map` can be
used to trace minified output back to original source locations.
## gRPC/Protobuf Communication
The extension and webview communicate via gRPC-like protocol over VS Code message passing.
@@ -48,93 +92,6 @@ The extension and webview communicate via gRPC-like protocol over VS Code messag
- `src/core/controller/task/explainChanges.ts` - Handler implementation
- `webview-ui/src/components/chat/ChatRow.tsx` - UI rendering
## Adding a New API Provider
When adding a new provider (e.g., "openai-codex"), you must update the proto conversion layer in THREE places or the provider will silently reset to Anthropic:
1. `proto/cline/models.proto` - Add to the `ApiProvider` enum (e.g., `OPENAI_CODEX = 40;`)
2. `convertApiProviderToProto()` in `src/shared/proto-conversions/models/api-configuration-conversion.ts` - Add case mapping string to proto enum
3. `convertProtoToApiProvider()` in the same file - Add case mapping proto enum back to string
**Why this matters:** Without these, the provider string hits the `default` case and returns `ANTHROPIC`. The webview, provider list, and handler all work fine, but the state silently resets when it round-trips through proto serialization. No error is thrown.
**Other files to update when adding a provider:**
- `src/shared/api.ts` - Add to `ApiProvider` union type, define models
- `src/shared/providers/providers.json` - Add to provider list for dropdown
- `src/core/api/index.ts` - Register handler in `createHandlerForProvider()`
- `webview-ui/src/components/settings/utils/providerUtils.ts` - Add cases in `getModelsForProvider()` and `normalizeApiConfiguration()`
- `webview-ui/src/utils/validate.ts` - Add validation case
- `webview-ui/src/components/settings/ApiOptions.tsx` - Render provider component
## Responses API Providers (OpenAI Codex, OpenAI Native)
Providers using OpenAI's Responses API require native tool calling. XML tools don't work with the Responses API.
**Symptoms of broken native tool calling:**
- Tools get called multiple times (e.g., `ask_followup_question` asks the same question twice)
- Tool arguments get duplicated or malformed
- The model responds but tools aren't recognized
**Root causes to check:**
1. **Provider missing from `isNextGenModelProvider()`** in `src/utils/model-utils.ts`. The native variant matchers (e.g., `native-gpt-5/config.ts`) call this function. If your provider isn't in the list, the matcher returns false and falls back to XML tools.
2. **Model missing `apiFormat: ApiFormat.OPENAI_RESPONSES`** in its model info (`src/shared/api.ts`). This property signals that the model requires native tool calling. The task runner in `src/core/task/index.ts` checks this and forces `enableNativeToolCalls: true` regardless of user settings.
**When adding a new Responses API provider:**
1. Add provider to `isNextGenModelProvider()` list in `src/utils/model-utils.ts`
2. Set `apiFormat: ApiFormat.OPENAI_RESPONSES` on all models that use the Responses API
3. The variant matcher and task runner will handle the rest automatically
## Adding Tools to System Prompt
This is tricky—multiple prompt variants and configs. **Always search for existing similar tools first and follow their pattern.** Look at the full chain from prompt definition → variant configs → handler → UI before implementing.
1. **Add to `ClineDefaultTool` enum** in `src/shared/tools.ts`
2. **Tool definition** in `src/core/prompts/system-prompt/tools/` (create file like `generate_explanation.ts`)
- Define variants for each `ModelFamily` (generic, next-gen, xs, etc.)
- Export variants array (e.g., `export const my_tool_variants = [GENERIC, NATIVE_NEXT_GEN, XS]`)
- **Fallback behavior**: If a variant isn't defined for a model family, `ClineToolSet.getToolByNameWithFallback()` automatically falls back to GENERIC. So you only need to export `[GENERIC]` unless the tool needs model-specific behavior.
3. **Register in `src/core/prompts/system-prompt/tools/init.ts`** - Import and spread into `allToolVariants`
4. **Add to variant configs** - Each model family has its own config in `src/core/prompts/system-prompt/variants/*/config.ts`. Add your tool's enum to the `.tools()` list:
- `generic/config.ts`, `next-gen/config.ts`, `gpt-5/config.ts`, `native-gpt-5/config.ts`, `native-gpt-5-1/config.ts`, `native-next-gen/config.ts`, `gemini-3/config.ts`, `glm/config.ts`, `hermes/config.ts`, `xs/config.ts`
- **Important**: If you add to a variant's config, make sure the tool spec exports a variant for that ModelFamily (or relies on GENERIC fallback)
5. **Create handler** in `src/core/task/tools/handlers/`
6. **Wire up in `ToolExecutor.ts`** if needed for execution flow
7. **Add to tool parsing** in `src/core/assistant-message/index.ts` if needed
8. **If tool has UI feedback**: add `ClineSay` enum in proto, update `src/shared/ExtensionMessage.ts`, update `src/shared/proto-conversions/cline-message.ts`, update `webview-ui/src/components/chat/ChatRow.tsx`
## Modifying System Prompt
**Read these first:** `src/core/prompts/system-prompt/README.md`, `tools/README.md`, `__tests__/README.md`
System prompt is modular: **components** (reusable sections) + **variants** (model-specific configs) + **templates** (with `{{PLACEHOLDER}}` resolution).
**Key directories:**
- `components/` - Shared sections: `rules.ts`, `capabilities.ts`, `editing_files.ts`, etc.
- `variants/` - Model-specific: `generic/`, `next-gen/`, `xs/`, `gpt-5/`, `gemini-3/`, `hermes/`, `glm/`, etc.
- `templates/` - Template engine and placeholder definitions
**Variant tiers (ask user which to modify):**
- **Next-gen** (Claude 4, GPT-5, Gemini 2.5): `next-gen/`, `native-next-gen/`, `native-gpt-5/`, `native-gpt-5-1/`, `gemini-3/`, `gpt-5/`
- **Standard** (default fallback): `generic/`
- **Local/small models**: `xs/`, `hermes/`, `glm/`
**How overrides work:** Variants can override components via `componentOverrides` in their `config.ts`, or provide a custom template in `template.ts` (e.g., `next-gen/template.ts` exports `rules_template`). If no override, the shared component from `components/` is used.
**Example: Adding a rule to RULES section**
1. Check if variant overrides rules: look for `rules_template` in `variants/*/template.ts` or `componentOverrides.RULES` in `config.ts`
2. If shared: modify `components/rules.ts`
3. If overridden: modify that variant's template
4. XS variant is special—has heavily condensed inline content in `template.ts`
**After any changes, regenerate snapshots:**
```bash
UPDATE_SNAPSHOTS=true npm run test:unit
```
Snapshots live in `__tests__/__snapshots__/`. Tests validate across model families and context variations (browser, MCP, focus chain).
## Modifying Default Slash Commands
Three places need updates:
- `src/core/slash-commands/index.ts` - Command definitions
- `src/core/prompts/commands.ts` - System prompt integration
- `webview-ui/src/utils/slash-commands.ts` - Webview autocomplete
## Adding New Global State Keys
Adding a new key to global state requires updates in multiple places. Missing any step causes silent failures.
@@ -203,3 +160,48 @@ const isGenerating = explanationInfo.status === "generating" && !wasCancelled
**See also:** `BrowserSessionRow.tsx` uses similar pattern with `isLastApiReqInterrupted` and `isLastMessageResume`.
**Backend side:** When streaming is cancelled, clean up properly (close tabs, clear comments, etc.) by checking `taskState.abort` after the streaming function returns.
## Debug Harness: clear inherited VSCode/Electron env vars before launching
The debug harness (`apps/vscode/src/dev/debug-harness/server.ts`) launches a child
VSCode via Playwright's `_electron.launch({ env: { ...process.env, ... } })`. If you
run the harness from a process that was itself spawned by VSCode (e.g. the Cline
extension host, an integrated terminal, or an agent running inside VSCode), the
parent's VSCode/Electron env vars leak into the child and break the launch.
The fatal one is **`ELECTRON_RUN_AS_NODE=1`**: it makes the child VSCode binary run
as plain Node, so it rejects every VSCode CLI flag. Symptom:
```
.../Visual Studio Code.app/Contents/MacOS/Code: bad option: --extensionDevelopmentPath=...
Error: Process failed to launch! (Playwright _electron.launch)
```
This is NOT the macOS Playwright flakiness mentioned in the harness README — it's
env inheritance. Fix: strip the inherited vars before starting the harness:
```bash
env -u ELECTRON_RUN_AS_NODE -u ELECTRON_NO_ATTACH_CONSOLE \
-u VSCODE_CLI -u VSCODE_CODE_CACHE_PATH -u VSCODE_CRASH_REPORTER_PROCESS_TYPE \
-u VSCODE_CWD -u VSCODE_ESM_ENTRYPOINT -u VSCODE_HANDLES_UNCAUGHT_ERRORS \
-u VSCODE_IPC_HOOK -u VSCODE_NLS_CONFIG -u VSCODE_PID -u VSCODE_L10N_BUNDLE_LOCATION \
npx tsx src/dev/debug-harness/server.ts --auto-launch --skip-build
```
Check your own env with `env | grep -iE 'electron|vscode_'` first; `ELECTRON_RUN_AS_NODE=1`
present means you must scrub before launching.
Other harness notes confirmed in practice:
- The extension host is **ESM** (`VSCODE_ESM_ENTRYPOINT`), so `ext.evaluate` has no
`require` and module-internal functions aren't reachable as globals. To inspect
internal builders (e.g. `buildBedrockProviderConfig`), set a breakpoint with
`ext.set_breakpoint` and read locals via `ext.evaluate` with the paused `callFrameId`
— don't try to `require()` the bundle.
- `web.evaluate` wraps the expression as a single returned expression; multi-statement
snippets must be an IIFE `(() => { ...; return x; })()`, otherwise you get
`SyntaxError: Unexpected token ';'`.
- Webview settings inputs are `vscode-text-field` web components with debounced React
onChange. Setting `.value` + dispatching events via `web.evaluate` is unreliable for
some fields; focus the inner shadow `input` then use real keystrokes (`ui.type` +
`ui.press Tab`, or click the dropdown option) to make the value persist.
+26
View File
@@ -0,0 +1,26 @@
# SDK Adapter
The VSCode extension runs on the Cline SDK (`@cline/core`, `@cline/llms`,
`@cline/shared`) through an adapter layer in `apps/vscode/src/sdk/`. The
webview still talks gRPC; the adapter translates between gRPC handlers and SDK
calls. See `apps/vscode/src/dev/debug-harness/README.md` for the debug harness.
## Conventions
1. **Look up SDK APIs, don't guess.** Use `kb_search(name="sdk", query="...")`
before implementing against an SDK surface.
2. **Reference the pre-SDK implementation when replacing a module.** Add a
`// Replaces classic src/core/... (see origin/main)` header and use
`kb_search(name="cline", commit="origin/main")` or
`git show origin/main:path` to consult the prior implementation.
3. **Single entry point.** There is one codepath — the SDK adapter. No
`CLINE_SDK` env flag.
4. **Use `{appBaseUrl}`**, never hardcode `app.cline.bot`.
5. **Avoid `as` casts.** Use explicit conversion functions with tests. The
branded types in `apps/vscode/src/sdk/model-catalog/contracts.ts` exist so
casts are unnecessary outside parse/compute boundaries.
## Debug harness
- **Dismiss the Kanban/promo overlay** before any debug harness interaction.
- **Use the command palette** to navigate tabs in the debug harness.
+1 -1
View File
@@ -1,2 +1,2 @@
/.github/ @saoudrizwan @arafatkatze @maxpaulus43 @candieduniverse
/.github/ @saoudrizwan @arafatkatze @maxpaulus43 @dominiccooney
/README.md @saoudrizwan @juanpflores
+2 -2
View File
@@ -2,7 +2,7 @@ version: 2
updates:
# Main extension dependencies
- package-ecosystem: "npm"
directory: "/"
directory: "/apps/vscode"
schedule:
interval: "weekly"
# Group all updates into a single PR
@@ -20,7 +20,7 @@ updates:
# Webview UI dependencies
- package-ecosystem: "npm"
directory: "/webview-ui"
directory: "/apps/vscode/webview-ui"
schedule:
interval: "weekly"
groups:
+43 -7
View File
@@ -33,7 +33,7 @@ permissions:
defaults:
run:
working-directory: sdk
working-directory: .
jobs:
publish-main:
@@ -105,12 +105,12 @@ jobs:
PACKAGE_VERSION=$(node -p "require('./apps/cli/package.json').version")
if [ "$PACKAGE_VERSION" != "$VERSION" ]; then
echo "sdk/apps/cli/package.json version ${PACKAGE_VERSION} does not match ${TAG}"
echo "apps/cli/package.json version ${PACKAGE_VERSION} does not match ${TAG}"
exit 1
fi
if ! printf "%s\n" "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then
echo "sdk/apps/cli/package.json has invalid version: ${VERSION}"
echo "apps/cli/package.json has invalid version: ${VERSION}"
exit 1
fi
@@ -132,13 +132,31 @@ jobs:
- name: Build SDK packages
run: bun run build:sdk
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
- name: Run tests
run: bun run test
- name: Build platform binaries
run: bun script/build.ts --install-native-variants --skip-sdk-build
working-directory: sdk/apps/cli
working-directory: apps/cli
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
- name: Verify build output
env:
@@ -176,7 +194,7 @@ jobs:
env:
NPM_CONFIG_PROVENANCE: "true"
run: bun script/publish-npm.ts --tag latest
working-directory: sdk/apps/cli
working-directory: apps/cli
- name: Get Previous CLI Tag
id: prev_tag
@@ -313,6 +331,15 @@ jobs:
- name: Build SDK packages
if: steps.check_commits.outputs.skip != 'true'
run: bun run build:sdk
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
- name: Run tests
if: steps.check_commits.outputs.skip != 'true'
@@ -348,7 +375,16 @@ jobs:
- name: Build platform binaries
if: steps.check_commits.outputs.skip != 'true'
run: bun script/build.ts --install-native-variants --skip-sdk-build
working-directory: sdk/apps/cli
working-directory: apps/cli
env:
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
- name: Verify build output
if: steps.check_commits.outputs.skip != 'true'
@@ -388,7 +424,7 @@ jobs:
env:
NPM_CONFIG_PROVENANCE: "true"
run: bun script/publish-npm.ts --tag nightly
working-directory: sdk/apps/cli
working-directory: apps/cli
- name: Summary
if: steps.check_commits.outputs.skip != 'true'
@@ -15,9 +15,11 @@ jobs:
trigger-integration-test:
name: Run Tests
runs-on: ubuntu-latest
# Run on PR open/reopen, or when someone comments /test-jetbrains on a PR
# Auto-run only for trusted PR authors. Anyone else needs a maintainer
# to opt their PR in by commenting /test-jetbrains.
if: |
github.event_name == 'pull_request_target' ||
(github.event_name == 'pull_request_target' &&
contains(fromJSON('["MEMBER","OWNER","COLLABORATOR"]'), github.event.pull_request.author_association)) ||
(github.event_name == 'issue_comment' &&
github.event.issue.pull_request &&
contains(github.event.comment.body, '/test-jetbrains') &&
@@ -27,8 +29,8 @@ jobs:
id: app-token
uses: actions/create-github-app-token@v1
with:
app-id: 1998650
private-key: ${{ secrets.CLINE_JETBRAINS_WORKFLOW_KEY }}
app-id: ${{ vars.CLINE_JETBRAINS_APP_ID }}
private-key: ${{ secrets.CLINE_JETBRAINS_APP_KEY }}
owner: cline
repositories: intellij-plugin
@@ -1,66 +0,0 @@
# TODO: Fold this workflow's SDK login changes into ext-vscode-publish-nightly.yml
# and delete this file. Pinned to dpc/sdk-migration-simpler-login while Max is iterating.
# Owner: Max Paulus
name: ext-vscode-publish-nightly-sdk
on:
schedule:
- cron: '0 12 * * *' # 4 AM PST (UTC-8) = 12 UTC
workflow_dispatch:
permissions:
contents: read
packages: write
checks: write
pull-requests: write
env:
# Keep the publish source pinned to one reviewed branch instead of accepting arbitrary refs.
SDK_NIGHTLY_REF: dpc/sdk-migration-simpler-login
jobs:
publish:
name: Publish Cline New SDK Extension Nightly
if: github.repository == 'cline/cline' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: PublishNightly
steps:
- name: Checkout trusted SDK nightly branch
uses: actions/checkout@v4
with:
ref: ${{ env.SDK_NIGHTLY_REF }}
lfs: true
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@v4
with:
# Keep publish environment aligned with test workflow/tooling lockfile expectations.
# Newer LTS (Node 24 / npm 11) can make `npm list` fail with ELSPROBLEMS during vsce packaging.
node-version: 22
- name: Install root dependencies
run: npm ci --include=optional
- name: Install webview-ui dependencies
run: cd webview-ui && npm ci --include=optional
- name: Install Publishing Tools
run: npm install -g @vscode/vsce ovsx
- name: Publish SDK nightly extension
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OVSX_PAT: ${{ secrets.OVSX_PAT }}
TELEMETRY_SERVICE_API_KEY: ${{ secrets.TELEMETRY_SERVICE_API_KEY }}
ERROR_SERVICE_API_KEY: ${{ secrets.ERROR_SERVICE_API_KEY }}
CLINE_ENVIRONMENT: production
# OpenTelemetry production defaults (can be overridden at runtime)
OTEL_TELEMETRY_ENABLED: ${{ secrets.OTEL_TELEMETRY_ENABLED }}
OTEL_LOGS_EXPORTER: otlp
OTEL_METRICS_EXPORTER: otlp
OTEL_EXPORTER_OTLP_PROTOCOL: ${{ secrets.OTEL_EXPORTER_OTLP_PROTOCOL }}
OTEL_EXPORTER_OTLP_ENDPOINT: ${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }}
OTEL_EXPORTER_OTLP_HEADERS: ${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }}
run: npm run publish:marketplace:nightly
@@ -20,6 +20,7 @@ jobs:
if: github.repository == 'cline/cline' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dpc/sdk-migration-simpler-login')
permissions:
contents: read
pull-requests: read
uses: ./.github/workflows/ext-vscode-test.yml
publish:
@@ -30,6 +31,11 @@ jobs:
if: github.repository == 'cline/cline' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dpc/sdk-migration-simpler-login')
runs-on: ubuntu-latest
environment: PublishNightly
# Publish commands run from the VS Code extension package. Dependency install
# is done from the monorepo root because the repo is Bun workspace-managed.
defaults:
run:
working-directory: apps/vscode
steps:
- name: Checkout selected branch
@@ -40,6 +46,7 @@ jobs:
persist-credentials: false
- name: Show build source
working-directory: ${{ github.workspace }}
run: |
echo "Building ref: $GITHUB_REF"
echo "Building sha: $GITHUB_SHA"
@@ -48,12 +55,26 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
# Keep publish environment aligned with test workflow/tooling lockfile expectations.
# Keep publish environment aligned with test workflow/tooling expectations.
# Newer LTS (Node 24 / npm 11) can make `npm list` fail with ELSPROBLEMS during vsce packaging.
node-version: 22
cache: 'npm'
cache-dependency-path: apps/vscode/webview-ui/package-lock.json
- name: Install root dependencies
run: npm ci --include=optional
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.13
- name: Install workspace dependencies
working-directory: ${{ github.workspace }}
run: bun install --frozen-lockfile
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build SDK packages
working-directory: ${{ github.workspace }}
run: bun run build:sdk
- name: Install webview-ui dependencies
run: cd webview-ui && npm ci --include=optional
@@ -78,6 +99,7 @@ jobs:
run: npm run publish:marketplace:nightly
- name: Tag published commit
working-directory: ${{ github.workspace }}
env:
GH_TOKEN: ${{ github.token }}
run: |
@@ -36,6 +36,9 @@ jobs:
name: Publish Extension
runs-on: ubuntu-latest
environment: publish
defaults:
run:
working-directory: apps/vscode
steps:
- uses: actions/checkout@v4
@@ -47,6 +50,7 @@ jobs:
- name: Resolve Release Tag
id: resolve_tag
working-directory: ${{ github.workspace }}
env:
TAG: ${{ github.event.inputs.tag }}
AUTO_CREATE: ${{ github.event.inputs.auto_create_tag_from_main }}
@@ -171,6 +175,7 @@ jobs:
- name: Get Previous Tag
id: prev_tag
working-directory: ${{ github.workspace }}
run: |
CURRENT_TAG="${{ steps.resolve_tag.outputs.tag }}"
PREV_TAG=$(git describe --tags --abbrev=0 "$CURRENT_TAG^" 2>/dev/null || echo "")
@@ -178,6 +183,7 @@ jobs:
- name: Get Changelog Entry
id: changelog
working-directory: ${{ github.workspace }}
run: |
# Get content between first ## [ and second ## [
CONTENT=$(awk '/^## \[/{if(found) exit; found=1; next} found{print}' CHANGELOG.md)
@@ -189,7 +195,7 @@ jobs:
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ steps.resolve_tag.outputs.tag }}
files: "*.vsix"
files: "apps/vscode/*.vsix"
body: |
${{ steps.changelog.outputs.content }}
+28 -25
View File
@@ -36,24 +36,24 @@ jobs:
with:
filters: |
e2e:
- 'src/**'
- 'webview-ui/**'
- 'proto/**'
- 'tests/**'
- 'scripts/**'
- 'standalone/**'
- 'assets/**'
- 'walkthrough/**'
- 'package.json'
- 'package-lock.json'
- 'buf.yaml'
- 'tsconfig*.json'
- 'biome.jsonc'
- 'esbuild.mjs'
- '.mocharc.json'
- '.vscode-test.mjs'
- '.vscodeignore'
- 'playwright*.ts'
- 'apps/vscode/src/**'
- 'apps/vscode/webview-ui/**'
- 'apps/vscode/proto/**'
- 'apps/vscode/tests/**'
- 'apps/vscode/scripts/**'
- 'apps/vscode/standalone/**'
- 'apps/vscode/assets/**'
- 'apps/vscode/walkthrough/**'
- 'apps/vscode/package.json'
- 'apps/vscode/package-lock.json'
- 'apps/vscode/buf.yaml'
- 'apps/vscode/tsconfig*.json'
- 'apps/vscode/biome.jsonc'
- 'apps/vscode/esbuild.mjs'
- 'apps/vscode/.mocharc.json'
- 'apps/vscode/.vscode-test.mjs'
- 'apps/vscode/.vscodeignore'
- 'apps/vscode/playwright*.ts'
- '.github/workflows/ext-vscode-test-e2e.yml'
matrix_prep:
@@ -79,6 +79,9 @@ jobs:
permissions:
id-token: write
contents: read
defaults:
run:
working-directory: apps/vscode
steps:
- uses: actions/checkout@v4
- name: Setup Node.js environment
@@ -91,24 +94,24 @@ jobs:
uses: actions/cache@v4
id: root-cache
with:
path: node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
path: apps/vscode/node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('apps/vscode/package-lock.json') }}
# Cache webview-ui dependencies - only reuse if package-lock.json exactly matches
- name: Cache webview-ui dependencies
uses: actions/cache@v4
id: webview-cache
with:
path: webview-ui/node_modules
key: ${{ runner.os }}-npm-webview-${{ hashFiles('webview-ui/package-lock.json') }}
path: apps/vscode/webview-ui/node_modules
key: ${{ runner.os }}-npm-webview-${{ hashFiles('apps/vscode/webview-ui/package-lock.json') }}
# Cache VS Code installation
- name: Cache VS Code
uses: actions/cache@v4
id: vscode-cache
with:
path: .vscode-test
key: vscode-${{ runner.os }}-stable-${{ hashFiles('.vscode-test.mjs', 'package.json') }}
path: apps/vscode/.vscode-test
key: vscode-${{ runner.os }}-stable-${{ hashFiles('apps/vscode/.vscode-test.mjs', 'apps/vscode/package.json') }}
restore-keys: |
vscode-${{ runner.os }}-stable-
@@ -121,7 +124,7 @@ jobs:
~/.cache/ms-playwright
~/Library/Caches/ms-playwright
~/AppData/Local/ms-playwright
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
key: playwright-browsers-${{ runner.os }}-${{ hashFiles('apps/vscode/package-lock.json') }}
restore-keys: |
playwright-browsers-${{ runner.os }}-
+94 -55
View File
@@ -36,38 +36,40 @@ jobs:
with:
filters: |
vscode:
- 'src/**'
- 'webview-ui/**'
- 'proto/**'
- 'tests/**'
- 'scripts/**'
- 'standalone/**'
- 'assets/**'
- 'walkthrough/**'
- 'apps/vscode/src/**'
- 'apps/vscode/webview-ui/**'
- 'apps/vscode/proto/**'
- 'apps/vscode/tests/**'
- 'apps/vscode/scripts/**'
- 'apps/vscode/standalone/**'
- 'apps/vscode/assets/**'
- 'apps/vscode/walkthrough/**'
- 'apps/vscode/package.json'
- 'package.json'
- 'package-lock.json'
- 'buf.yaml'
- 'tsconfig*.json'
- 'biome.jsonc'
- 'esbuild.mjs'
- '.mocharc.json'
- '.nycrc*.json'
- '.vscode-test.mjs'
- 'test-setup.js'
- 'bun.lock'
- 'apps/vscode/buf.yaml'
- 'apps/vscode/tsconfig*.json'
- 'apps/vscode/biome.jsonc'
- 'apps/vscode/esbuild.mjs'
- 'apps/vscode/.mocharc.json'
- 'apps/vscode/.nycrc*.json'
- 'apps/vscode/.vscode-test.mjs'
- 'apps/vscode/test-setup.js'
- '.github/workflows/ext-vscode-test.yml'
testing_platform:
- 'src/**'
- 'proto/**'
- 'standalone/**'
- 'testing-platform/**'
- 'tests/specs/**'
- 'apps/vscode/src/**'
- 'apps/vscode/proto/**'
- 'apps/vscode/standalone/**'
- 'apps/vscode/testing-platform/**'
- 'apps/vscode/tests/specs/**'
- 'apps/vscode/package.json'
- 'package.json'
- 'package-lock.json'
- 'buf.yaml'
- 'tsconfig*.json'
- 'esbuild.mjs'
- '.vscodeignore'
- 'scripts/**'
- 'bun.lock'
- 'apps/vscode/buf.yaml'
- 'apps/vscode/tsconfig*.json'
- 'apps/vscode/esbuild.mjs'
- 'apps/vscode/.vscodeignore'
- 'apps/vscode/scripts/**'
- '.github/workflows/ext-vscode-test.yml'
quality-checks:
@@ -75,6 +77,9 @@ jobs:
if: needs.detect-changes.outputs.vscode == 'true' || needs.detect-changes.outputs.testing_platform == 'true'
runs-on: ubuntu-latest
name: Quality Checks
defaults:
run:
working-directory: apps/vscode
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -84,17 +89,25 @@ jobs:
with:
node-version: 22
cache: 'npm'
cache-dependency-path: |
package-lock.json
webview-ui/package-lock.json
cache-dependency-path: apps/vscode/webview-ui/package-lock.json
- name: Install root dependencies
run: npm ci
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.13
- name: Install workspace dependencies
working-directory: ${{ github.workspace }}
run: bun install --frozen-lockfile
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build SDK packages
working-directory: ${{ github.workspace }}
run: bun run build:sdk
- name: Install webview-ui dependencies
run: cd webview-ui && npm ci
run: cd webview-ui && npm ci --include=optional
- name: Run Quality Checks (Parallel)
run: npm run ci:check-all
@@ -113,6 +126,7 @@ jobs:
defaults:
run:
shell: bash
working-directory: apps/vscode
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -122,17 +136,25 @@ jobs:
with:
node-version: 22
cache: 'npm'
cache-dependency-path: |
package-lock.json
webview-ui/package-lock.json
cache-dependency-path: apps/vscode/webview-ui/package-lock.json
- name: Install root dependencies
run: npm ci
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.13
- name: Install workspace dependencies
working-directory: ${{ github.workspace }}
run: bun install --frozen-lockfile
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build SDK packages
working-directory: ${{ github.workspace }}
run: bun run build:sdk
- name: Install webview-ui dependencies
run: cd webview-ui && npm ci
run: cd webview-ui && npm ci --include=optional
- name: Set up NPM on Windows
if: runner.os == 'Windows'
@@ -151,6 +173,11 @@ jobs:
id: build_step
run: npm run ci:build
- name: Vitest Suites (SDK adapter + model catalog)
id: vitest_tests
if: ${{ !cancelled() && steps.build_step.outcome == 'success' }}
run: npm run test:vitest
- name: Unit Tests with coverage - Linux
id: unit_tests_linux
if: ${{ !cancelled() && steps.build_step.outcome == 'success' && runner.os == 'Linux' }}
@@ -201,13 +228,16 @@ jobs:
with:
name: pr-coverage-reports
path: |
coverage-unit/lcov.info
webview-ui/coverage/lcov.info
apps/vscode/coverage-unit/lcov.info
apps/vscode/webview-ui/coverage/lcov.info
test-platform-integration:
needs: [detect-changes, quality-checks]
if: needs.detect-changes.outputs.testing_platform == 'true'
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/vscode
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -218,17 +248,26 @@ jobs:
node-version: 22
cache: 'npm'
cache-dependency-path: |
package-lock.json
webview-ui/package-lock.json
testing-platform/package-lock.json
apps/vscode/webview-ui/package-lock.json
apps/vscode/testing-platform/package-lock.json
- name: Install root dependencies
run: npm ci
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.13
- name: Install workspace dependencies
working-directory: ${{ github.workspace }}
run: bun install --frozen-lockfile
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build SDK packages
working-directory: ${{ github.workspace }}
run: bun run build:sdk
- name: Install webview-ui dependencies
run: cd webview-ui && npm ci
run: cd webview-ui && npm ci --include=optional
- name: Download ripgrep binaries
run: npm run download-ripgrep
@@ -237,7 +276,7 @@ jobs:
run: npm run compile-standalone
- name: Install testing platform dependencies
run: cd testing-platform && npm ci
run: cd testing-platform && npm ci --include=optional
- name: Running testing platform integration spec tests
timeout-minutes: 7
@@ -247,7 +286,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: test-platform-integration-core-coverage
path: coverage/**/lcov.info
path: apps/vscode/coverage/**/lcov.info
# Keep the required "test" check as a tiny aggregate gate instead of the conditional
# VS Code matrix. GitHub treats conditionally skipped jobs as successful required
@@ -309,7 +348,7 @@ jobs:
uses: actions/download-artifact@v4
with:
name: pr-coverage-reports
path: .
path: apps/vscode
- name: Upload core unit tests coverage to Qlty
if: needs.detect-changes.outputs.vscode == 'true'
@@ -318,7 +357,7 @@ jobs:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
# we can merge multiple files if necessary
files: |
coverage-unit/lcov.info
apps/vscode/coverage-unit/lcov.info
tag: unit:core
- name: Upload webview-ui unit tests coverage to Qlty
@@ -328,7 +367,7 @@ jobs:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
# we can merge multiple files if necessary
files: |
webview-ui/coverage/lcov.info
apps/vscode/webview-ui/coverage/lcov.info
tag: unit:webview-ui
add-prefix: webview-ui/
@@ -339,12 +378,12 @@ jobs:
id: download-integration-coverage
with:
name: test-platform-integration-core-coverage
path: integration-core-coverage-reports
path: apps/vscode/integration-core-coverage-reports
- name: Upload core integration tests coverage to Qlty
if: needs.detect-changes.outputs.testing_platform == 'true' && steps.download-integration-coverage.outcome == 'success'
uses: qltysh/qlty-action/coverage@v2
with:
token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
files: integration-core-coverage-reports/**/lcov.info
files: apps/vscode/integration-core-coverage-reports/**/lcov.info
tag: integration:core
+9 -9
View File
@@ -26,7 +26,7 @@ on:
defaults:
run:
working-directory: sdk
working-directory: .
jobs:
test:
@@ -148,7 +148,7 @@ jobs:
env:
CHANNEL: ${{ steps.channel.outputs.channel }}
run: |
BASE_VERSION=$(node -p "require('./packages/llms/package.json').version")
BASE_VERSION=$(node -p "require('./sdk/packages/llms/package.json').version")
if [ "$CHANNEL" = "nightly" ]; then
TIMESTAMP=$(date +%s)
@@ -166,11 +166,11 @@ jobs:
if: steps.check_commits.outputs.skip != 'true'
env:
VERSION: ${{ steps.version.outputs.version }}
run: bun scripts/version.ts "$VERSION"
run: bun sdk/scripts/version.ts "$VERSION"
- name: Verify publishability
if: steps.check_commits.outputs.skip != 'true'
run: bun scripts/check-publish.ts
run: bun sdk/scripts/check-publish.ts
- name: Prepare package tarball directory
if: steps.check_commits.outputs.skip != 'true'
@@ -187,7 +187,7 @@ jobs:
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Publishing @cline/shared@${VERSION} with tag '${CHANNEL}'..."
cd packages/shared
cd sdk/packages/shared
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
@@ -199,7 +199,7 @@ jobs:
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Publishing @cline/llms@${VERSION} with tag '${CHANNEL}'..."
cd packages/llms
cd sdk/packages/llms
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
@@ -211,7 +211,7 @@ jobs:
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Publishing @cline/agents@${VERSION} with tag '${CHANNEL}'..."
cd packages/agents
cd sdk/packages/agents
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
@@ -223,7 +223,7 @@ jobs:
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Publishing @cline/core@${VERSION} with tag '${CHANNEL}'..."
cd packages/core
cd sdk/packages/core
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
@@ -235,7 +235,7 @@ jobs:
VERSION: ${{ steps.version.outputs.version }}
run: |
echo "Publishing @cline/sdk@${VERSION} with tag '${CHANNEL}'..."
cd packages/sdk
cd sdk/packages/sdk
TARBALL=$(bun pm pack --destination "$RUNNER_TEMP/sdk-npm-packs" --quiet)
npm publish "$RUNNER_TEMP/sdk-npm-packs/$(basename "$TARBALL")" --tag "$CHANNEL" --access public
+4 -4
View File
@@ -21,7 +21,7 @@ permissions:
defaults:
run:
working-directory: sdk
working-directory: .
jobs:
quality-checks:
@@ -96,12 +96,12 @@ jobs:
- name: Run SDK Tests (Windows)
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os == 'windows-latest' }}
run: bun -F './packages/**' test
run: bun -F './sdk/packages/**' test
- name: Smoke test SQLite under Node
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && matrix.os != 'windows-latest' }}
timeout-minutes: 10
run: bun scripts/ci-node-smoke.ts
run: bun sdk/scripts/ci-node-smoke.ts
- name: Run TUI e2e tests
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os == 'ubuntu-latest' && matrix.node-version == '24.x' }}
@@ -109,4 +109,4 @@ jobs:
- name: Verify packages are publishable
if: ${{ !cancelled() && steps.build_sdk_step.outcome == 'success' && steps.build_cli_step.outcome == 'success' && matrix.os == 'ubuntu-latest' && matrix.node-version == '24.x' }}
run: bun scripts/check-publish.ts
run: bun sdk/scripts/check-publish.ts
+29 -5
View File
@@ -13,12 +13,15 @@ tmp
pnpm-lock.yaml
.clineignore
.cline/enterprise
.cline/remote-config
**/.cline/remote-config
.venv
.actrc
CLAUDE.local.md
webview-ui/src/**/*.js
webview-ui/src/**/*.js.map
apps/vscode/webview-ui/src/**/*.js
apps/vscode/webview-ui/src/**/*.js.map
# Ignore coverage directories and files
coverage
@@ -35,9 +38,9 @@ coverage-unit
.worktrees
## Generated files ##
src/generated/
src/shared/proto/
webview-ui/src/services/grpc-client.ts
apps/vscode/src/generated/
apps/vscode/src/shared/proto/
apps/vscode/webview-ui/src/services/grpc-client.ts
*.tsbuildinfo
# E2E Tests
@@ -60,3 +63,24 @@ tests/**/cache
# Backup created by scripts/marketplace-readme.mjs while publishing.
# Should never be committed: only exists if a publish aborts mid-swap.
.README.github.bak
# Tauri generated code
apps/*/src-tauri/gen
apps/*/src-tauri/bin
apps/examples/*/src-tauri/gen
apps/examples/*/src-tauri/bin
# Tauri UI test snapshots
apps/*/src/tests/.tui-test
apps/*/src/tests/tui-traces
apps/vscode/webview-ui/src/**/*.js
apps/vscode/webview-ui/src/**/*.js.map
# SDK Session files / User data
.cline/data
.cline/tmp
*.db
*.db-shm
*.db-wal
.cline/**/managed.json
.cline/**/bundle.json
@@ -6,15 +6,18 @@
{
"id": "sdk-tool-handler-telemetry",
"rule": "Any new tool handler added to packages/agents/src or packages/core/src that performs a user-visible action (writes files, executes commands, modifies state, calls external APIs) must include a call to captureToolUsage() from packages/core/src/services/telemetry/core-events.ts, or emit a task.tool_used event via telemetry.capture(). Pure read-only helpers and getters are exempt. When in doubt, prefer instrumentation.",
"scope": ["packages/agents/src/**", "packages/core/src/**"],
"scope": [
"sdk/packages/agents/src/**",
"sdk/packages/core/src/**"
],
"severity": "high"
},
{
"id": "sdk-session-lifecycle-telemetry",
"rule": "New session start, end, or state-transition code paths in packages/core/src must call the appropriate typed helper from packages/core/src/services/telemetry/core-events.ts (captureTaskCreated, captureTaskCompleted, captureConversationTurnEvent, captureTokenUsage, etc.). Do not inline raw telemetry.capture() calls for session lifecycle events — always use the typed helper, which guarantees a consistent payload shape.",
"scope": [
"packages/core/src/cline-core/**",
"packages/core/src/runtime/**"
"sdk/packages/core/src/cline-core/**",
"sdk/packages/core/src/runtime/**"
],
"severity": "high"
},
@@ -22,8 +25,8 @@
"id": "sdk-no-raw-event-strings",
"rule": "All telemetry event name strings must be sourced from CORE_TELEMETRY_EVENTS in packages/core/src/services/telemetry/core-events.ts. If a PR introduces a string literal in a telemetry.capture(), telemetry.captureRequired(), or recordCounter()/recordHistogram()/recordGauge() call that does not reference CORE_TELEMETRY_EVENTS, flag it. New events must be added to CORE_TELEMETRY_EVENTS first, with a typed capture helper created alongside them.",
"scope": [
"packages/core/src/**",
"packages/agents/src/**",
"sdk/packages/core/src/**",
"sdk/packages/agents/src/**",
"apps/cli/src/**",
"apps/vscode/src/**"
],
@@ -32,13 +35,17 @@
{
"id": "sdk-auth-telemetry-completeness",
"rule": "Any new OAuth or authentication provider added under packages/core/src/auth must emit all four lifecycle events using the typed helpers from core-events.ts: captureAuthStarted (at flow entry), captureAuthSucceeded + identifyAccount (on token success), captureAuthFailed (on error), and captureAuthLoggedOut (on token invalidation or explicit logout). Flag PRs that introduce a new auth flow file without all four. Cross-reference packages/core/src/auth/cline.ts and packages/core/src/auth/codex.ts as canonical examples.",
"scope": ["packages/core/src/auth/**"],
"scope": [
"sdk/packages/core/src/auth/**"
],
"severity": "high"
},
{
"id": "sdk-telemetry-doc-update",
"rule": "Any PR that adds new event constants to CORE_TELEMETRY_EVENTS in packages/core/src/services/telemetry/core-events.ts, adds new capture* helper functions, or changes the payload shape of an existing event must update the Event Catalog section in DOC.md. Flag PRs that modify core-events.ts without a corresponding change to DOC.md.",
"scope": ["packages/core/src/services/telemetry/core-events.ts"],
"scope": [
"sdk/packages/core/src/services/telemetry/core-events.ts"
],
"severity": "medium"
}
]
@@ -1,19 +1,19 @@
{
"files": [
{
"path": "packages/core/src/services/telemetry/core-events.ts",
"path": "sdk/packages/core/src/services/telemetry/core-events.ts",
"description": "Single source of truth for all telemetry event names (CORE_TELEMETRY_EVENTS) and their typed capture helper functions. Every PR touching telemetry must be evaluated against this catalog. New events must be defined here first."
},
{
"path": "packages/shared/src/services/telemetry.ts",
"path": "sdk/packages/shared/src/services/telemetry.ts",
"description": "ITelemetryService interface definition. Defines the contract all telemetry implementations must satisfy (capture, captureRequired, recordCounter, recordHistogram, recordGauge, flush, dispose)."
},
{
"path": "packages/core/src/services/telemetry/TelemetryService.ts",
"path": "sdk/packages/core/src/services/telemetry/TelemetryService.ts",
"description": "Reference implementation of ITelemetryService used by all hosts. Multi-adapter fan-out service that forwards events to OpenTelemetry."
},
{
"path": "packages/core/src/services/telemetry/OpenTelemetryProvider.ts",
"path": "sdk/packages/core/src/services/telemetry/OpenTelemetryProvider.ts",
"description": "OpenTelemetry-backed provider that wires logs/metrics/traces exporters. Contains createConfiguredTelemetryService and createConfiguredTelemetryHandle, the canonical factories every host should use."
},
{
@@ -21,11 +21,11 @@
"description": "Public API and event documentation. The Event Catalog and 'Activation funnel' sections must be kept in sync with core-events.ts. Host integration rules (CLI dir ordering, hub daemon metadata forwarding) are documented here."
},
{
"path": "ARCHITECTURE.md",
"path": "sdk/ARCHITECTURE.md",
"description": "Architecture reference. Telemetry design decisions, completion semantics (submit_and_exit anchoring), and hub-daemon telemetry forwarding are documented here. Use as ground truth for design intent."
},
{
"path": "AGENTS.md",
"path": "sdk/AGENTS.md",
"description": "Package boundary rules. Telemetry runtime services live in @cline/core; @cline/agents must not own stateful telemetry. Use to evaluate whether a telemetry change is being made in the correct package."
}
]
@@ -10,9 +10,9 @@ The SDK uses OpenTelemetry (OTEL) as its sole telemetry transport. Events flow t
```
core-events.ts (event catalog + typed helpers)
ITelemetryService (packages/shared) ← interface contract
ITelemetryService (sdk/packages/shared) ← interface contract
TelemetryService (packages/core) ← multi-adapter fan-out
TelemetryService (sdk/packages/core) ← multi-adapter fan-out
OpenTelemetryAdapter → OpenTelemetryProvider ← OTLP transport
@@ -24,7 +24,7 @@ parallel-but-independent stacks; this `.greptile/` config covers only the SDK.
## The Single Source of Truth
`packages/core/src/services/telemetry/core-events.ts` is the single source of truth for all
`sdk/packages/core/src/services/telemetry/core-events.ts` is the single source of truth for all
event names. It exports:
- `CORE_TELEMETRY_EVENTS` — a frozen const object grouped by family
@@ -60,8 +60,8 @@ Emission ownership:
emitter in `prepareLocalRuntimeBootstrap`. Hosts must NOT re-emit these.
- `workspace.path_resolved`: emitted from default tool executors **only when**
`WorkspaceManager` exposes more than one root.
- `task.*`: emitted by core session lifecycle code in `packages/core/src/cline-core/` and
`packages/core/src/runtime/`. Hosts must not duplicate this emission.
- `task.*`: emitted by core session lifecycle code in `sdk/packages/core/src/cline-core/` and
`sdk/packages/core/src/runtime/`. Hosts must not duplicate this emission.
## `task.completed` Semantics
@@ -105,7 +105,7 @@ forwarding, hub-backed sessions silently drop their lifecycle telemetry.
## Auth Lifecycle Completeness
Every authentication provider in `packages/core/src/auth/` must emit all four auth lifecycle
Every authentication provider in `sdk/packages/core/src/auth/` must emit all four auth lifecycle
events using the typed helpers:
| Phase | Helper | Where it fires |
@@ -115,7 +115,7 @@ events using the typed helpers:
| Token error | `captureAuthFailed(provider, errorMessage)` | In the catch block |
| Token invalidation | `captureAuthLoggedOut(provider, reason)` | On invalid_grant or explicit logout |
Cross-reference `packages/core/src/auth/cline.ts` and `packages/core/src/auth/codex.ts` as
Cross-reference `sdk/packages/core/src/auth/cline.ts` and `sdk/packages/core/src/auth/codex.ts` as
canonical examples of all four phases.
## Single Telemetry Service Per Host
+11 -1
View File
@@ -1 +1,11 @@
lint-staged
if ! command -v gitleaks >/dev/null 2>&1; then
echo "gitleaks is required for the pre-commit secret scan."
echo "Install it with: brew install gitleaks"
echo "Other install options: https://github.com/gitleaks/gitleaks#installing"
exit 1
fi
gitleaks git --pre-commit --redact --staged --verbose || exit 1
cd apps/vscode && lint-staged
-16
View File
@@ -1,16 +0,0 @@
{
"extension": [
"ts"
],
"spec": [
"src/**/__tests__/*.ts",
"src/test/services/**/*.test.ts"
],
"require": [
"ts-node/register",
"source-map-support/register",
"./src/test/requires.ts"
],
"recursive": true,
"exit": true
}
+31 -31
View File
@@ -10,23 +10,23 @@
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
"--disable-workspace-trust",
"--disable-extension",
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
"--disable-extension",
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
"${workspaceFolder}",
"${workspaceFolder}/apps/vscode",
"--disable-extensions"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
"${workspaceFolder}/apps/vscode/dist/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}",
"envFile": "${workspaceFolder}/.env",
"envFile": "${workspaceFolder}/apps/vscode/.env",
"env": {
"IS_DEV": "true",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
"CLINE_ENVIRONMENT": "production"
}
},
@@ -35,22 +35,22 @@
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
"--disable-workspace-trust",
"--disable-extension",
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
"--disable-extension",
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
"${workspaceFolder}"
"${workspaceFolder}/apps/vscode"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
"${workspaceFolder}/apps/vscode/dist/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}",
"envFile": "${workspaceFolder}/.env",
"envFile": "${workspaceFolder}/apps/vscode/.env",
"env": {
"IS_DEV": "true",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
"CLINE_ENVIRONMENT": "staging"
}
},
@@ -59,22 +59,22 @@
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
"--disable-workspace-trust",
"--disable-extension",
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
"--disable-extension",
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
"${workspaceFolder}"
"${workspaceFolder}/apps/vscode"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
"${workspaceFolder}/apps/vscode/dist/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}",
"envFile": "${workspaceFolder}/.env",
"envFile": "${workspaceFolder}/apps/vscode/.env",
"env": {
"IS_DEV": "true",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
"CLINE_ENVIRONMENT": "local"
}
},
@@ -84,27 +84,27 @@
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--user-data-dir=${workspaceFolder}/dist/tmp/user",
"--user-data-dir=${workspaceFolder}/apps/vscode/dist/tmp/user",
"--profile-temp",
"--sync=off",
"--disable-extension",
"saoudrizwan.claude-dev", // Avoid conflicts with installed Cline
"--disable-extension",
"saoudrizwan.cline-nightly", // Avoid conflicts with installed Cline Nightly
"--extensionDevelopmentPath=${workspaceFolder}",
"${workspaceFolder}"
"--extensionDevelopmentPath=${workspaceFolder}/apps/vscode",
"${workspaceFolder}/apps/vscode"
],
"outFiles": [
"${workspaceFolder}/dist/**/*.js"
"${workspaceFolder}/apps/vscode/dist/**/*.js"
],
"preLaunchTask": "clean-tmp-user",
"internalConsoleOptions": "openOnSessionStart",
"postDebugTask": "stop",
"envFile": "${workspaceFolder}/.env",
"envFile": "${workspaceFolder}/apps/vscode/.env",
"env": {
"IS_DEV": "true",
"TEMP_PROFILE": "true",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}/apps/vscode",
"CLINE_ENVIRONMENT": "production"
}
},
@@ -117,13 +117,13 @@
],
"sourceMaps": true,
"resolveSourceMapLocations": [
"${workspaceFolder}/**",
"${workspaceFolder}/apps/vscode/**",
"!**/node_modules/**"
],
"cwd": "${workspaceFolder}",
"cwd": "${workspaceFolder}/apps/vscode",
"outFiles": [
"${workspaceFolder}/dist/**/*.js",
"${workspaceFolder}/dist-standalone/**/*.js"
"${workspaceFolder}/apps/vscode/dist/**/*.js",
"${workspaceFolder}/apps/vscode/dist-standalone/**/*.js"
],
"preLaunchTask": "compile-standalone",
"runtimeExecutable": "npx",
@@ -131,11 +131,11 @@
"tsx"
],
"program": "scripts/test-standalone-core-api-server.ts",
"envFile": "${workspaceFolder}/.env",
"envFile": "${workspaceFolder}/apps/vscode/.env",
"env": {
"PROTOBUS_PORT": "26040",
"HOSTBRIDGE_PORT": "26041",
"WORKSPACE_DIR": "${workspaceFolder}",
"WORKSPACE_DIR": "${workspaceFolder}/apps/vscode",
"E2E_TEST": "true",
"CLINE_ENVIRONMENT": "local"
},
@@ -151,10 +151,10 @@
],
"sourceMaps": true,
"resolveSourceMapLocations": [
"${workspaceFolder}/**",
"${workspaceFolder}/apps/vscode/**",
"!**/node_modules/**"
],
"cwd": "${workspaceFolder}",
"cwd": "${workspaceFolder}/apps/vscode",
"runtimeExecutable": "npx",
"runtimeArgs": [
"mocha"
@@ -169,7 +169,7 @@
"--exit",
"${file}"
],
"envFile": "${workspaceFolder}/.env",
"envFile": "${workspaceFolder}/apps/vscode/.env",
"env": {
"TS_NODE_PROJECT": "./tsconfig.unit-test.json",
"NODE_ENV": "test",
@@ -188,7 +188,7 @@
"run",
"storybook"
],
"cwd": "${workspaceFolder}/webview-ui",
"cwd": "${workspaceFolder}/apps/vscode/webview-ui",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"serverReadyAction": {
+1 -1
View File
@@ -17,7 +17,7 @@
// Protobuf settings
"protoc": {
"options": [
"--proto_path=proto"
"--proto_path=apps/vscode/proto"
]
},
// Enable Lint and format using Biome
+40 -23
View File
@@ -5,24 +5,28 @@
"tasks": [
{
"label": "compile-standalone",
"type": "npm",
"script": "compile-standalone",
"type": "shell",
"command": "npm run compile-standalone",
"group": "build",
"problemMatcher": [],
"presentation": {
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode"
}
},
{
"label": "npm: protos",
"type": "npm",
"script": "protos",
"type": "shell",
"command": "npm run protos",
"problemMatcher": [],
"isBackground": false,
"presentation": {
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true"
}
@@ -60,8 +64,8 @@
"group": "build"
},
{
"type": "npm",
"script": "build:webview",
"type": "shell",
"command": "npm run build:webview",
"group": "build",
"problemMatcher": [],
"isBackground": true,
@@ -74,14 +78,15 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true"
}
}
},
{
"type": "npm",
"script": "build:webview:test",
"type": "shell",
"command": "npm run build:webview:test",
"group": "build",
"problemMatcher": [],
"isBackground": true,
@@ -94,6 +99,7 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true",
"IS_TEST": "true"
@@ -101,8 +107,8 @@
}
},
{
"type": "npm",
"script": "dev:webview",
"type": "shell",
"command": "npm run dev:webview",
"group": "build",
"problemMatcher": [
{
@@ -131,14 +137,15 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true"
}
}
},
{
"type": "npm",
"script": "watch:esbuild",
"type": "shell",
"command": "npm run watch:esbuild",
"group": "build",
"problemMatcher": {
"pattern": [
@@ -169,14 +176,15 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true"
}
}
},
{
"type": "npm",
"script": "watch:esbuild:test",
"type": "shell",
"command": "npm run watch:esbuild:test",
"group": "build",
"problemMatcher": {
"pattern": [
@@ -207,6 +215,7 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true",
"IS_TEST": "true"
@@ -214,8 +223,8 @@
}
},
{
"type": "npm",
"script": "watch:tsc",
"type": "shell",
"command": "npm run watch:tsc",
"group": "build",
"problemMatcher": "$tsc-watch",
"isBackground": true,
@@ -226,11 +235,15 @@
"presentation": {
"group": "watch",
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode"
}
},
{
"type": "npm",
"script": "watch-tests",
"type": "shell",
"command": "npm run watch-tests",
"label": "npm: watch-tests",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"dependsOn": [
@@ -240,7 +253,10 @@
"reveal": "always",
"group": "watchers"
},
"group": "build"
"group": "build",
"options": {
"cwd": "${workspaceFolder}/apps/vscode"
}
},
{
"label": "tasks: watch-tests",
@@ -262,11 +278,11 @@
"dependsOn": [
"watch"
],
"command": "rm -rf ${workspaceFolder}/dist/tmp/user && mkdir -p ${workspaceFolder}/dist/tmp/user"
"command": "rm -rf ${workspaceFolder}/apps/vscode/dist/tmp/user && mkdir -p ${workspaceFolder}/apps/vscode/dist/tmp/user"
},
{
"type": "npm",
"script": "storybook",
"type": "shell",
"command": "npm run storybook",
"group": "build",
"problemMatcher": [],
"isBackground": false,
@@ -279,6 +295,7 @@
"reveal": "always"
},
"options": {
"cwd": "${workspaceFolder}/apps/vscode",
"env": {
"IS_DEV": "true"
}
@@ -292,7 +309,7 @@
"$tsc"
],
"options": {
"cwd": "${workspaceFolder}/sdk"
"cwd": "${workspaceFolder}"
}
}
],
+56
View File
@@ -1,5 +1,61 @@
# Changelog
## [3.87.0]
### Added
- Add MiniMax M3 model support.
### Fixed
- Update VS Code extension dependencies to resolve security issues in `@xmldom/xmldom`, `basic-ftp`, `axios`, `undici`, and other direct/transitive packages.
## [3.86.2]
### Fixed
- Fix `@` file mentions and workspace file search on VS Code 1.122+ by resolving the new bundled `@vscode/ripgrep-universal` per-platform binary layout before falling back to legacy ripgrep paths.
## [3.86.1]
### Fixed
- Fix `@` file mentions failing to find files in some environments (notably VS Code Remote SSH, and after certain VS Code updates) by keeping the file-search fallback alive when the workspace index or bundled ripgrep binary is unavailable.
## [3.86.0]
### Added
- Add Claude Opus 4.8 provider support, including 1M-context variants where available.
- Add Moonshot Kimi K2.6 model support.
### Fixed
- Show prompt-cache support for Qwen 3.7 Max in the Cline provider.
- Fix the VS Code nightly publish workflow startup permissions.
### Changed
- Move the VS Code extension project into `apps/vscode`.
## [3.85.0]
### Added
- Add GPT-5.5 support to SAP AI Core.
- Add DeepSeek V4 Flash and Pro models.
- Add Gemini 3.5 Flash to Gemini and Vertex providers.
- Add `/lg-task` URI webhook integration for LG dashboard flows.
### Fixed
- Fix Vertex AI global endpoint handling for Claude models.
- Route Poolside Laguna models through next-gen prompts and native tool calling.
### Changed
- Update `diff` and `protobufjs` dependencies.
## [3.84.0]
### Added
-1
View File
@@ -1,2 +1 @@
@.clinerules/general.md
@.clinerules/network.md
+3 -2
View File
@@ -45,7 +45,7 @@ We also welcome contributions to our [documentation](https://github.com/cline/cl
3. Install [bun](https://bun.com)
4. Install the necessary dependencies for the extension and webview-gui:
```bash
npm run install:all
cd apps/vscode && npm run install:all && cd ../..
cd sdk && bun run build && cd ..
```
5. Generate Protocol Buffer files (required before first build):
@@ -61,7 +61,7 @@ We also welcome contributions to our [documentation](https://github.com/cline/cl
2. Push your branch and create a PR on GitHub. Our CI will:
- Run tests and checks
3. Testing
- Run `npm run test` to run tests locally.
- Run `cd apps/vscode && npm run test` to run tests locally.
- Before submitting PR, run `npm run format:fix` to format your code
### Extension
@@ -73,6 +73,7 @@ We also welcome contributions to our [documentation](https://github.com/cline/cl
- If you dismissed the prompts, you can install them manually from the Extensions panel
2. **Local Development**
- cd into the vscode extension, `cd apps/vscode`
- Run `npm run install:all` to install dependencies
- Run `npm run protos` to generate Protocol Buffer files (required before first build)
- Run `npm run test` to run tests locally
+3 -3
View File
@@ -51,7 +51,7 @@ for CI/CD and scripting.
npm i -g cline
```
<a href="./sdk/apps/cli/README.md">Learn more</a>
<a href="./apps/cli/README.md">Learn more</a>
<br><br>
</td>
@@ -129,7 +129,7 @@ npm install @cline/sdk
| Product | Description | Location | CHANGELOG |
|---------|------------|--------------|--------------|
| **SDK** | Node.js programmatic agent API and extension exports. | [`sdk/`](https://github.com/cline/cline/tree/main/sdk) | [CHANGELOG.md](https://github.com/cline/cline/blob/main/sdk/CHANGELOG.md) |
| **CLI** | Terminal UI, headless mode, shell commands, and CLI-specific flows. | [`sdk/apps/cli/`](https://github.com/cline/cline/tree/main/sdk/apps/cli) | [CHANGELOG.md](https://github.com/cline/cline/blob/main/sdk/apps/cli/CHANGELOG.md) |
| **CLI** | Terminal UI, headless mode, shell commands, and CLI-specific flows. | [`apps/cli/`](https://github.com/cline/cline/tree/main/apps/cli) | [CHANGELOG.md](https://github.com/cline/cline/blob/main/apps/cli/CHANGELOG.md) |
| **VS Code Extension** | The Marketplace extension and extension host integration. | [`/`](https://github.com/cline/cline/tree/main) (WIP migrating) | [CHANGELOG.md](https://github.com/cline/cline/blob/main/CHANGELOG.md) |
| **JetBrains Plugin** | JetBrains-hosted client that talks to the shared agent core. | Currently we are not open-sourcing JetBrains plugins | - |
| **Kanban** | Web-based multi-agent task board. | [`cline/kanban`](https://github.com/cline/kanban) | [CHANGELOG.md](https://github.com/cline/kanban/blob/main/CHANGELOG.md) |
@@ -212,7 +212,7 @@ cline schedule create "PR summary" \
Chat with your agent from any messaging platform: Telegram, Slack, Discord, Google Chat, WhatsApp, and Linear. Each conversation thread maps to an agent session with full context. Set up access control to restrict who can interact with your agent.
```bash
cline connect telegram -m my_bot -k $BOT_TOKEN
cline connect telegram -k $BOT_TOKEN
cline connect slack --token $SLACK_TOKEN --signing-secret $SECRET --base-url $URL
```
+124
View File
@@ -0,0 +1,124 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.5/schema.json",
"root": false,
"extends": ["../sdk/biome.json"],
"linter": {
"rules": {
"a11y": {
"noStaticElementInteractions": "warn"
}
}
},
"overrides": [
{
"includes": ["vscode/**"],
"formatter": {
"enabled": true,
"indentStyle": "tab",
"indentWidth": 4,
"lineWidth": 130,
"lineEnding": "lf",
"formatWithErrors": true
},
"javascript": {
"formatter": {
"semicolons": "asNeeded",
"arrowParentheses": "always",
"bracketSameLine": true,
"bracketSpacing": true,
"jsxQuoteStyle": "double",
"quoteProperties": "asNeeded",
"trailingCommas": "all"
}
},
"json": {
"formatter": {
"trailingCommas": "none",
"expand": "always"
}
},
"linter": {
"enabled": true,
"domains": {
"react": "recommended"
},
"rules": {
"recommended": true,
"correctness": {
"useExhaustiveDependencies": "info",
"noUndeclaredVariables": "off",
"noEmptyPattern": "info",
"useJsxKeyInIterable": "off",
"noInnerDeclarations": "off",
"useHookAtTopLevel": "info",
"useYield": "info",
"noConstructorReturn": "off",
"noInvalidPositionAtImportRule": "off",
"noSwitchDeclarations": "off",
"noUnusedImports": "error"
},
"a11y": "info",
"style": {
"useNodejsImportProtocol": "off",
"useImportType": "off",
"useBlockStatements": "off",
"useNamingConvention": "off",
"useThrowOnlyError": "info",
"useConsistentArrayType": "off",
"noParameterAssign": "off",
"useAsConstAssertion": "off",
"useDefaultParameterLast": "off",
"noNonNullAssertion": "info",
"useEnumInitializers": "off",
"useSelfClosingElements": "info",
"useSingleVarDeclarator": "off",
"useNumberNamespace": "info",
"noInferrableTypes": "info",
"useTemplate": "info",
"noUselessElse": "info"
},
"suspicious": {
"noDoubleEquals": "warn",
"noImplicitAnyLet": "info",
"noThenProperty": "off",
"noAsyncPromiseExecutor": "info",
"noImportAssign": "off",
"noExplicitAny": "info",
"noControlCharactersInRegex": "warn",
"noShadowRestrictedNames": "off",
"noArrayIndexKey": "info",
"noAssignInExpressions": "info",
"useIterableCallbackReturn": "info"
},
"complexity": {
"noUselessConstructor": "info",
"useOptionalChain": "info",
"noBannedTypes": "warn",
"useLiteralKeys": "info",
"noUselessCatch": "info",
"noUselessSwitchCase": "info",
"noStaticOnlyClass": "info"
},
"security": {
"noDangerouslySetInnerHtml": "info"
}
}
},
"css": {
"parser": {
"tailwindDirectives": true
}
},
"assist": {
"enabled": true,
"actions": {
"source": {
"organizeImports": "on",
"useSortedAttributes": "on"
}
}
},
"plugins": ["vscode/src/dev/grit/process-env.grit"]
}
]
}
@@ -92,6 +92,8 @@ bun run test
bun --cwd apps/cli run build:platforms
```
Known local-only test failure: `src/commands/distribution-package.test.ts > rejects direct source package packing by default` will fail on machines that have `ignore-scripts=true` in `~/.npmrc` (set by the npm supply-chain hardening guide). Bun reads npm's `ignore-scripts` from `~/.npmrc`, so `bun pm pack --dry-run` skips the source-publish `prepack` guard and exits 0, which the test reads as a failure. CI does not set `ignore-scripts`, so the test passes there. Confirm by running `bun pm pack --dry-run` directly: with `~/.npmrc` in place it exits 0 with no guard output; with `~/.npmrc` moved aside it exits 1 and prints the guard message. This is not a release blocker by itself, but it does mean the local-publish path (`bun release cli`) will also bypass the source-publish guard on this machine; prefer the GitHub Actions publish path on machines with `ignore-scripts=true` set globally, or temporarily unset it (`npm config delete ignore-scripts` or `mv ~/.npmrc ~/.npmrc.bak`) for the duration of a local publish.
7. Commit release changes.
Only after the user approves the notes and version:
@@ -1,5 +1,78 @@
# Cline CLI Changelog
## 3.0.15
- Add Cline Hub, a web app for monitoring connected clients, viewing and driving sessions, streaming assistant output, and restarting the local hub, with local, LAN, and tunnel usage gated by a room secret.
- Support global AGENTS rules so agent rules can be applied across all sessions, not just per-project.
- Let plugins contribute static or dynamic rule content when installed in the sandbox.
- Bind Discord sessions to individual message authors so different Discord users no longer share chat state in a thread.
- Support participant mute targets in Discord: resolve `/mute` and `/unmute` from user mentions or raw user IDs to mute a specific participant in a thread.
- Make OAuth URLs clickable in the TUI.
- Refresh the bundled model catalog, adding Claude Opus 4.8, Moonshot Kimi K2.6, and Qwen3.7 Max (with cache support).
- Discover SDK skill directories that are symlinked, including handling circular symlinks.
- Steer active connector sessions across turn keys by matching on session ID, so replies continue the existing session instead of starting a duplicate.
- Stop the Discord connector after repeated identical errors (per thread, within a time window) to prevent error messages from flooding a channel.
- Fix Discord connector registration and reply fallback handling.
- Fix SAP AI Core to use the AI SDK community provider.
- Log ACP output as diagnostics instead of errors so normal output no longer appears as errors.
## 3.0.14
- Fix OTEL telemetry variable bundling so telemetry is correctly enabled in compiled CLI builds: guard against environments where `process.env` is undefined and remove optional chaining so bundlers can inline the values at build time.
## 3.0.13
- Show a loading dialog while resuming a session from history so the TUI no longer appears frozen during the load.
- Speed up the `/clear` command by deferring new session creation until you send the next prompt, so clearing no longer blocks on spinning up an empty session.
## 3.0.12
- Show a loading dialog while the config screen switches provider or model so the transition no longer looks frozen.
- Render the ask question tool prompt inline with the conversation so the question and suggested answers stay attached to the assistant turn that asked them, instead of appearing in a separate modal.
- Allow manual `cline update` runs to install the latest published version immediately, bypassing the release age gate that delays automatic updates.
- Refresh the bundled SDK to 0.0.42, updating the model catalog.
## 3.0.11
- Fix a regression in the ChatGPT OAuth provider where requests failed with `max_output_tokens not supported`, by restoring the full output token budget instead of applying an implicit cap.
- Hide the `Space toggle` hint in the config footer when the highlighted row is not toggleable (rules, agents, hooks).
- Authenticate Vertex Gemini through Google auth when `gcp.projectId` is configured, and surface the full Vertex model list instead of only Claude models.
- Include tool names in tool result content blocks so message logs and session history consistently track which tool produced each result.
## 3.0.10
- Install plugins from `file://` URLs in addition to npm and git sources.
- Show Ollama API key note in TUI settings so users know when to provide an API key.
- Keep interactive sessions alive when idle or awaiting approval instead of treating them as ended, and stop reading message files for every session when `hydrate: false`.
- Add Poolside as a provider.
- Add Gemini 3.5 Flash to the Gemini provider model list.
- Auto-detect Telegram bot username from the bot token so the Telegram connector no longer requires it to be configured separately.
- Notify connectors when a scheduled execution fails, not just when it succeeds.
- Bake OTEL telemetry variables into the CLI at build time so telemetry works in nightly and production builds.
- Preserve model output token limits from the SDK model catalog so context window math matches the upstream provider.
- Soften the visual treatment of rejected tool calls in the TUI.
- Hide the skills tool from the system prompt when skills are disabled, and refresh slash commands after toggling a skill.
- Restore AWS Bedrock profile-based auth during legacy config migration so profiles set via `awsAuthentication: "profile"` are preserved without `awsUseProfile`.
- Cache global settings reads keyed by file mtime so repeated reads skip the JSON parse and zod validation on the hot path.
## 3.0.9
- Speed up CLI startup with plugins by loading sandboxed plugins concurrently and caching plugin tool descriptors per plugin, provider, and model.
- Speed up plugin and tool config toggles by updating the TUI optimistically and persisting changes without reloading the full config or reimporting plugins.
- Restore fuzzy ranking for the @-mention file picker so the most relevant files appear first.
- Keep the interactive CLI session alive after cancelling a task instead of tearing the session down.
- Accept dash-prefixed prompts when passed after `--`, so prompts starting with `-` are no longer parsed as flags.
- Recover from hub abort cleanup failures so a cancel that hits an error no longer crashes the runtime host.
- Route GLM thinking through provider metadata so thinking-enabled GLM models behave correctly through the gateway.
## 3.0.8
- Use Telegram numeric participant ids so renamed users stay linked to the same participant in the Telegram connector.
- Keep failed plugins visible in the config UI with their load/setup phase and error details so broken plugin definitions are easier to diagnose.
- Move the Create Session Fork shortcut from Opt+F to Opt+R so terminal word-right navigation works again.
- Fix AWS Bedrock region and profile detection in the CLI onboarding, and surface bearer-token and additional Bedrock config fields in the provider config screens.
- Fix inflated token usage counts caused by AgentRuntime.execute() not resetting usage between calls, which the local runtime host was then double-counting on top of the session baseline.
## 3.0.7
- Skip the ChatGPT OAuth model refresh on session startup so the CLI launches without the extra network round-trip.
@@ -416,7 +416,7 @@ Then attach VS Code or Chrome DevTools to `ws://127.0.0.1:6499`.
## Publishing
The CLI is published as the `cline` wrapper package on npm with platform-specific binaries under `@cline/cli-*`. The release flow lives in the `publish-cli` skill (`sdk/apps/cli/.cline/skills/publish-cli/SKILL.md`).
The CLI is published as the `cline` wrapper package on npm with platform-specific binaries under `@cline/cli-*`. The release flow lives in the `publish-cli` skill (`apps/cli/.cline/skills/publish-cli/SKILL.md`).
From the `apps/cli` workspace:
@@ -169,7 +169,7 @@ Bridge a chat surface into RPC-backed Cline sessions. Each conversation thread m
```sh
# Telegram (polling mode)
cline connect telegram -m my_bot -k 123456:ABCDEF...
cline connect telegram -k 123456:ABCDEF...
# Slack (webhook mode)
cline connect slack --bot-token $SLACK_BOT_TOKEN --signing-secret $SLACK_SIGNING_SECRET --base-url https://your-domain.com
+60 -3
View File
@@ -1,12 +1,65 @@
import { copyFileSync, mkdirSync } from "node:fs";
import {
copyFileSync,
cpSync,
existsSync,
mkdirSync,
readdirSync,
statSync,
} from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { $ } from "bun";
function defineProcessEnv(name: string): string {
return JSON.stringify(process.env[name] ?? "");
}
const sourcemap = Bun.env.CLINE_SOURCEMAPS === "1" ? "linked" : "none";
const rootDir = dirname(fileURLToPath(import.meta.url));
const repoRoot = join(rootDir, "../../");
const hubWebviewSourcePath = join(repoRoot, "apps/cline-hub/src/webview");
const hubWebviewDistPath = join(repoRoot, "apps/cline-hub/dist/webview");
const hubWebviewIndexPath = join(hubWebviewDistPath, "index.html");
const cliHubWebviewDistPath = join(rootDir, "dist/cline-hub/webview");
function newestFileMtimeMs(dir: string): number {
let newest = 0;
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (
entry.name === "node_modules" ||
entry.name === "dist" ||
entry.name === ".turbo"
) {
continue;
}
const path = join(dir, entry.name);
if (entry.isDirectory()) {
newest = Math.max(newest, newestFileMtimeMs(path));
} else if (entry.isFile()) {
newest = Math.max(newest, statSync(path).mtimeMs);
}
}
return newest;
}
function shouldBuildHubWebview(): boolean {
if (!existsSync(hubWebviewIndexPath)) {
return true;
}
try {
return (
newestFileMtimeMs(hubWebviewSourcePath) >
statSync(hubWebviewIndexPath).mtimeMs
);
} catch {
return true;
}
}
if (shouldBuildHubWebview()) {
console.log("Building Cline Hub webview...");
await $`bun -F @cline/cline-hub build:webview`.cwd(repoRoot);
}
const result = await Bun.build({
entrypoints: ["./src/index.ts"],
@@ -63,10 +116,9 @@ if (result.logs.length > 0) {
}
}
const rootDir = dirname(fileURLToPath(import.meta.url));
const coreBootstrapPath = join(
rootDir,
"../../packages/core/dist/extensions/plugin-sandbox-bootstrap.js",
"../../sdk/packages/core/dist/extensions/plugin-sandbox-bootstrap.js",
);
const cliBootstrapPath = join(
rootDir,
@@ -74,3 +126,8 @@ const cliBootstrapPath = join(
);
mkdirSync(dirname(cliBootstrapPath), { recursive: true });
copyFileSync(coreBootstrapPath, cliBootstrapPath);
if (existsSync(hubWebviewDistPath)) {
mkdirSync(dirname(cliHubWebviewDistPath), { recursive: true });
cpSync(hubWebviewDistPath, cliHubWebviewDistPath, { recursive: true });
}
@@ -1,7 +1,7 @@
{
"name": "@cline/cli",
"displayName": "cline",
"version": "3.0.7",
"version": "3.0.15",
"description": "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more",
"type": "module",
"publishConfig": {
@@ -10,7 +10,7 @@
"repository": {
"type": "git",
"url": "git+https://github.com/cline/cline.git",
"directory": "sdk/apps/cli"
"directory": "apps/cli"
},
"keywords": [
"cline",
@@ -68,33 +68,38 @@
},
"dependencies": {
"@agentclientprotocol/sdk": "^0.16.1",
"@clack/prompts": "^1.2.0",
"@chat-adapter/discord": "^4.23.0",
"@chat-adapter/gchat": "^4.23.0",
"@chat-adapter/linear": "^4.23.0",
"@chat-adapter/slack": "^4.23.0",
"@chat-adapter/telegram": "^4.23.0",
"@chat-adapter/whatsapp": "^4.23.0",
"@clack/prompts": "^1.2.0",
"@cline/cline-hub": "workspace:*",
"@gramio/format": "^0.7.0",
"chat": "^4.23.0",
"commander": "^14.0.3",
"@opentui-ui/dialog": "^0.1.2",
"@opentui/core": "0.1.102",
"@opentui/react": "0.1.102",
"@opentui-ui/dialog": "^0.1.2",
"chat": "^4.23.0",
"commander": "^14.0.3",
"fzf": "^0.5.2",
"marked": "^15.0.12",
"open": "^10.2.0",
"opentui-spinner": "^0.0.6",
"pino": "^10.3.1",
"react": "19.2.4",
"react-reconciler": "0.32.0",
"react-devtools-core": "^7.0.1",
"react-reconciler": "0.32.0",
"yaml": "^2.8.2",
"nanoid": "^5.1.7",
"zod": "^4.1.11"
},
"devDependencies": {
"@cline/core": "workspace:*",
"@cline/shared": "workspace:*",
"@microsoft/tui-test": "^0.0.2",
"@types/react": "19.2.14"
"@types/react": "19.2.14",
"vitest": "^4.0.18",
"@types/bun": "^1.3.10"
}
}
@@ -1,6 +1,14 @@
#!/usr/bin/env bun
import { existsSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
import {
cpSync,
existsSync,
mkdirSync,
readdirSync,
readFileSync,
realpathSync,
statSync,
} from "node:fs";
import { join, relative, resolve } from "node:path";
import { $ } from "bun";
import {
@@ -13,6 +21,30 @@ const cliDir = resolve(import.meta.dir, "..");
const rootDir = resolve(cliDir, "../..");
process.chdir(cliDir);
// Telemetry / OTEL environment variables that should be baked into the
// compiled binary at build time. Mirrors the list of secrets injected by the
// `cli-publish` GitHub Actions workflow. These are inlined via Bun's `define`
// so the CLI ships with the production telemetry configuration without
// requiring the end user to set any env vars.
const BUILD_TIME_INLINED_ENV_VARS = [
"TELEMETRY_SERVICE_API_KEY",
"ERROR_SERVICE_API_KEY",
"OTEL_TELEMETRY_ENABLED",
"OTEL_LOGS_EXPORTER",
"OTEL_METRICS_EXPORTER",
"OTEL_EXPORTER_OTLP_PROTOCOL",
"OTEL_EXPORTER_OTLP_ENDPOINT",
"OTEL_EXPORTER_OTLP_HEADERS",
] as const;
function buildInlinedEnvDefines(): Record<string, string> {
const defines: Record<string, string> = {};
for (const name of BUILD_TIME_INLINED_ENV_VARS) {
defines[`process.env.${name}`] = JSON.stringify(process.env[name] ?? "");
}
return defines;
}
const pkg = JSON.parse(readFileSync(join(cliDir, "package.json"), "utf-8"));
const version: string = pkg.version;
const repository: unknown = pkg.repository;
@@ -71,6 +103,48 @@ if (!buildOptions.skipSdkBuild) {
await $`bun -F @cline/cli build`.cwd(rootDir);
}
const hubWebviewSource = join(cliDir, "../cline-hub/src/webview");
const hubWebviewDist = join(cliDir, "../cline-hub/dist/webview");
const hubWebviewIndex = join(hubWebviewDist, "index.html");
function newestFileMtimeMs(dir: string): number {
let newest = 0;
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (
entry.name === "node_modules" ||
entry.name === "dist" ||
entry.name === ".turbo"
) {
continue;
}
const path = join(dir, entry.name);
if (entry.isDirectory()) {
newest = Math.max(newest, newestFileMtimeMs(path));
} else if (entry.isFile()) {
newest = Math.max(newest, statSync(path).mtimeMs);
}
}
return newest;
}
function shouldBuildHubWebview(): boolean {
if (!existsSync(hubWebviewIndex)) {
return true;
}
try {
return (
newestFileMtimeMs(hubWebviewSource) > statSync(hubWebviewIndex).mtimeMs
);
} catch {
return true;
}
}
if (shouldBuildHubWebview()) {
console.log("Building Cline Hub webview...");
await $`bun -F @cline/cline-hub build:webview`.cwd(rootDir);
}
const binaries: Record<string, string> = {};
function findOpenTuiParserWorker(): string {
@@ -128,6 +202,9 @@ async function buildCompiledBinary(input: {
external: ["@anthropic-ai/vertex-sdk"],
define: {
OTUI_TREE_SITTER_WORKER_PATH: bunfsRoot + parserWorkerPath,
// Inline telemetry/OTEL env vars at build time so the compiled
// binary ships with production telemetry configuration baked in.
...buildInlinedEnvDefines(),
},
throw: false,
});
@@ -182,7 +259,7 @@ for (const item of targets) {
// Copy plugin sandbox bootstrap if it exists
const bootstrapSrc = join(
rootDir,
"packages/core/dist/extensions/plugin-sandbox-bootstrap.js",
"sdk/packages/core/dist/extensions/plugin-sandbox-bootstrap.js",
);
if (existsSync(bootstrapSrc)) {
const bootstrapDir = join(cliDir, `dist/${dirName}/extensions`);
@@ -191,6 +268,14 @@ for (const item of targets) {
await Bun.write(join(bootstrapDir, "plugin-sandbox-bootstrap.js"), content);
}
if (existsSync(hubWebviewDist)) {
const hubWebviewDest = join(cliDir, `dist/${dirName}/cline-hub/webview`);
mkdirSync(join(cliDir, `dist/${dirName}/cline-hub`), {
recursive: true,
});
cpSync(hubWebviewDist, hubWebviewDest, { recursive: true });
}
// Generate platform package.json
await Bun.write(
join(cliDir, `dist/${dirName}/package.json`),
@@ -90,7 +90,7 @@ function buildHostSdkDependencies(): Record<string, string> {
for (const pkg of hostSdkPackages) {
dependencies[pkg.name] = readPackageVersion(
pkg.name,
join(cliDir, "../../packages", pkg.directory, "package.json"),
join(cliDir, "../../sdk/packages", pkg.directory, "package.json"),
);
}
return dependencies;
@@ -6,7 +6,7 @@ import {
saveOAuthProviderSettings,
toProviderApiKey,
} from "../commands/auth";
import { writeErr } from "../utils/output";
import { writeDiagnostic } from "../utils/output";
/**
* Supported ACP OAuth provider IDs.
@@ -73,10 +73,10 @@ async function performOAuthLogin(
),
);
},
onOutput: (message) => writeErr(`[acp/auth] ${message}`),
onOutput: (message) => writeDiagnostic(`[acp/auth] ${message}`),
openUrl: (url) => open(url, { wait: false }).then(() => undefined),
onOpenUrlError: ({ url }) => {
writeErr(
writeDiagnostic(
`[acp/auth] Could not open browser automatically. Open this URL manually:\n${url}`,
);
},
@@ -116,12 +116,12 @@ export async function authenticateAcpProvider(
// Check for already-stored credentials.
const existingKey = getPersistedProviderApiKey(methodId, existing);
if (existingKey) {
writeErr(`[acp/auth] Using existing credentials for ${methodId}`);
writeDiagnostic(`[acp/auth] Using existing credentials for ${methodId}`);
return { providerId: methodId, apiKey: existingKey };
}
// Perform a fresh OAuth login.
writeErr(`[acp/auth] Starting OAuth login for ${methodId}`);
writeDiagnostic(`[acp/auth] Starting OAuth login for ${methodId}`);
const credentials = await performOAuthLogin(methodId, existing);
saveOAuthProviderSettings(
@@ -132,6 +132,6 @@ export async function authenticateAcpProvider(
);
const apiKey = toProviderApiKey(methodId, credentials);
writeErr(`[acp/auth] Successfully authenticated with ${methodId}`);
writeDiagnostic(`[acp/auth] Successfully authenticated with ${methodId}`);
return { providerId: methodId, apiKey };
}
+36
View File
@@ -0,0 +1,36 @@
import { afterEach, describe, expect, it, vi } from "vitest";
describe("runAcpMode", () => {
afterEach(() => {
vi.doUnmock("@agentclientprotocol/sdk");
vi.doUnmock("./acpAgent");
vi.restoreAllMocks();
});
it("writes the startup diagnostic without labeling it as an error", async () => {
const stderrWrite = vi
.spyOn(process.stderr, "write")
.mockImplementation(() => true);
vi.doMock("@agentclientprotocol/sdk", () => ({
ndJsonStream: vi.fn(() => ({})),
AgentSideConnection: class {
closed = Promise.resolve();
},
}));
vi.doMock("./acpAgent", () => ({
AcpAgent: class {},
}));
const { runAcpMode } = await import("./index");
await runAcpMode();
expect(stderrWrite).toHaveBeenCalledWith(
"[acp] starting ACP mode over stdio…\n",
);
expect(stderrWrite).not.toHaveBeenCalledWith(
expect.stringContaining("error:"),
);
});
});
@@ -1,5 +1,5 @@
import { Readable, Writable } from "node:stream";
import { writeErr } from "../utils/output";
import { writeDiagnostic } from "../utils/output";
export async function runAcpMode(): Promise<void> {
const { AgentSideConnection, ndJsonStream } = await import(
@@ -7,7 +7,7 @@ export async function runAcpMode(): Promise<void> {
);
const { AcpAgent } = await import("./acpAgent");
writeErr("[acp] starting ACP mode over stdio…");
writeDiagnostic("[acp] starting ACP mode over stdio…");
const stream = ndJsonStream(
Writable.toWeb(process.stdout) as WritableStream<Uint8Array>,
@@ -30,6 +30,18 @@ function resolveCliAgentConfigSearchPaths(cwd: string): string[] {
return [join(cwd, ".cline", "agents"), join(clineDir, "agents")];
}
function createConfigUserInstructionService(cwd: string) {
return createUserInstructionConfigService({
skills: {
workspacePath: cwd,
includePluginSkills: true,
cwd,
},
rules: { workspacePath: cwd },
workflows: { workspacePath: cwd },
});
}
async function runWorkflowsConfigCommand(
cwd: string,
outputMode: CliOutputMode,
@@ -39,11 +51,7 @@ async function runWorkflowsConfigCommand(
string,
{ id: string; name: string; instructions: string; path: string }
>();
const service = createUserInstructionConfigService({
skills: { workspacePath: cwd },
rules: { workspacePath: cwd },
workflows: { workspacePath: cwd },
});
const service = createConfigUserInstructionService(cwd);
try {
await service.start();
for (const record of service.listRecords<WorkflowConfig>("workflow")) {
@@ -90,11 +98,7 @@ async function runRulesConfigCommand(
string,
{ name: string; instructions: string; path: string }
>();
const service = createUserInstructionConfigService({
skills: { workspacePath: cwd },
rules: { workspacePath: cwd },
workflows: { workspacePath: cwd },
});
const service = createConfigUserInstructionService(cwd);
try {
await service.start();
for (const record of service.listRecords<RuleConfig>("rule")) {
@@ -142,11 +146,7 @@ async function runSkillsConfigCommand(
path: string;
}
>();
const service = createUserInstructionConfigService({
skills: { workspacePath: cwd },
rules: { workspacePath: cwd },
workflows: { workspacePath: cwd },
});
const service = createConfigUserInstructionService(cwd);
try {
await service.start();
for (const record of service.listRecords<SkillConfig>("skill")) {
@@ -420,11 +420,7 @@ async function runToolsConfigCommand(
async function loadInteractiveConfigDataForCommand(
cwd: string,
): Promise<Awaited<ReturnType<typeof loadInteractiveConfigData>>> {
const userInstructionService = createUserInstructionConfigService({
skills: { workspacePath: cwd },
rules: { workspacePath: cwd },
workflows: { workspacePath: cwd },
});
const userInstructionService = createConfigUserInstructionService(cwd);
try {
await userInstructionService.start();
return await loadInteractiveConfigData({
+184
View File
@@ -0,0 +1,184 @@
import { mkdirSync, mkdtempSync } from "node:fs";
import { arch, platform, tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { runDashboardCommand, waitForProcessShutdown } from "./dashboard";
const ENV_KEYS = [
"WORKSPACE_ROOT",
"HOST",
"CLINE_HUB_DASHBOARD_PORT",
"PUBLIC_URL",
"ROOM_SECRET",
"CLINE_HUB_WEBVIEW_DIST_DIR",
"CLINE_WRAPPER_PATH",
] as const;
const originalEnv = Object.fromEntries(
ENV_KEYS.map((key) => [key, process.env[key]]),
);
afterEach(() => {
for (const key of ENV_KEYS) {
const value = originalEnv[key];
if (value === undefined) {
delete process.env[key];
} else {
process.env[key] = value;
}
}
});
describe("runDashboardCommand", () => {
it("starts the dashboard server, opens the invite URL, and waits for shutdown", async () => {
const output: string[] = [];
const errors: string[] = [];
const opened: string[] = [];
const stop = vi.fn();
let observedEnv:
| {
workspaceRoot: string | undefined;
host: string | undefined;
port: string | undefined;
publicUrl: string | undefined;
roomSecret: string | undefined;
webviewDistDir: string | undefined;
}
| undefined;
const webviewDistDir = mkdtempSync(join(tmpdir(), "cline-webview-dist-"));
mkdirSync(webviewDistDir, { recursive: true });
process.env.CLINE_HUB_WEBVIEW_DIST_DIR = webviewDistDir;
const exitCode = await runDashboardCommand({
cwd: "sdk",
host: "127.0.0.1",
port: "9090",
publicUrl: "http://127.0.0.1:9090",
roomSecret: "secret",
io: {
writeln: (text) => output.push(text ?? ""),
writeErr: (text) => errors.push(text),
},
startServer: async () => {
observedEnv = {
workspaceRoot: process.env.WORKSPACE_ROOT,
host: process.env.HOST,
port: process.env.CLINE_HUB_DASHBOARD_PORT,
publicUrl: process.env.PUBLIC_URL,
roomSecret: process.env.ROOM_SECRET,
webviewDistDir: process.env.CLINE_HUB_WEBVIEW_DIST_DIR,
};
return {
listenUrl: "http://127.0.0.1:9090/",
publicUrl: "http://127.0.0.1:9090",
inviteUrl: "http://127.0.0.1:9090/?roomSecret=secret",
hubUrl: "ws://127.0.0.1:25463/hub",
stop,
};
},
openUrl: async (url) => {
opened.push(url);
},
waitForShutdown: async (server) => {
await server.stop();
},
});
expect(exitCode).toBe(0);
expect(observedEnv).toEqual({
workspaceRoot: resolve("sdk"),
host: "127.0.0.1",
port: "9090",
publicUrl: "http://127.0.0.1:9090",
roomSecret: "secret",
webviewDistDir,
});
expect(opened).toEqual(["http://127.0.0.1:9090/?roomSecret=secret"]);
expect(stop).toHaveBeenCalledTimes(1);
expect(output.join("\n")).toContain("Cline dashboard listening at");
expect(output.join("\n")).toContain("ws://127.0.0.1:25463/hub");
expect(errors).toEqual([]);
expect(process.env.WORKSPACE_ROOT).toBe(originalEnv.WORKSPACE_ROOT);
expect(process.env.CLINE_HUB_WEBVIEW_DIST_DIR).toBe(webviewDistDir);
});
it("honors --no-open behavior", async () => {
const openUrl = vi.fn();
const exitCode = await runDashboardCommand({
openBrowser: false,
io: {
writeln: () => {},
writeErr: () => {},
},
startServer: async () => ({
listenUrl: "http://127.0.0.1:8787/",
publicUrl: "http://127.0.0.1:8787",
inviteUrl: "http://127.0.0.1:8787",
stop: vi.fn(),
}),
openUrl,
waitForShutdown: async () => {},
});
expect(exitCode).toBe(0);
expect(openUrl).not.toHaveBeenCalled();
});
it("finds webview assets from the published wrapper package layout", async () => {
const root = mkdtempSync(join(tmpdir(), "cline-wrapper-layout-"));
const wrapperPath = join(root, "node_modules", "cline", "bin", "cline");
const platformName = platform() === "win32" ? "windows" : platform();
const webviewDistDir = join(
root,
"node_modules",
"cline",
"node_modules",
"@cline",
`cli-${platformName}-${arch()}`,
"cline-hub",
"webview",
);
mkdirSync(join(wrapperPath, ".."), { recursive: true });
mkdirSync(webviewDistDir, { recursive: true });
process.env.CLINE_WRAPPER_PATH = wrapperPath;
delete process.env.CLINE_HUB_WEBVIEW_DIST_DIR;
let observedWebviewDistDir: string | undefined;
const exitCode = await runDashboardCommand({
openBrowser: false,
io: {
writeln: () => {},
writeErr: () => {},
},
startServer: async () => {
observedWebviewDistDir = process.env.CLINE_HUB_WEBVIEW_DIST_DIR;
return {
listenUrl: "http://127.0.0.1:8787/",
publicUrl: "http://127.0.0.1:8787",
inviteUrl: "http://127.0.0.1:8787",
stop: vi.fn(),
};
},
waitForShutdown: async () => {},
});
expect(exitCode).toBe(0);
expect(observedWebviewDistDir).toBe(webviewDistDir);
});
it("settles shutdown when server stop rejects", async () => {
const shutdown = waitForProcessShutdown({
listenUrl: "http://127.0.0.1:8787/",
publicUrl: "http://127.0.0.1:8787",
inviteUrl: "http://127.0.0.1:8787",
stop: vi.fn(async () => {
throw new Error("stop failed");
}),
});
process.emit("SIGINT", "SIGINT");
await expect(shutdown).rejects.toThrow("stop failed");
});
});
+195
View File
@@ -0,0 +1,195 @@
import { existsSync } from "node:fs";
import { arch, platform } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import open from "open";
import { c } from "../utils/output";
export interface DashboardServerHandle {
listenUrl: string;
publicUrl: string;
inviteUrl: string;
hubUrl?: string;
stop: () => void | Promise<void>;
}
interface DashboardCommandIo {
writeln: (text?: string) => void;
writeErr: (text: string) => void;
}
export interface RunDashboardCommandOptions {
cwd?: string;
host?: string;
port?: string;
publicUrl?: string;
roomSecret?: string;
openBrowser?: boolean;
io: DashboardCommandIo;
startServer?: () => Promise<DashboardServerHandle>;
openUrl?: (url: string) => Promise<void>;
waitForShutdown?: (server: DashboardServerHandle) => Promise<void>;
}
const DASHBOARD_PORT_ENV = "CLINE_HUB_DASHBOARD_PORT";
const WEBVIEW_DIST_ENV = "CLINE_HUB_WEBVIEW_DIST_DIR";
function setEnvValue(name: string, value: string | undefined): () => void {
const previous = process.env[name];
if (value === undefined) {
return () => {};
}
process.env[name] = value;
return () => {
if (previous === undefined) {
delete process.env[name];
} else {
process.env[name] = previous;
}
};
}
async function withDashboardEnvironment<T>(
options: RunDashboardCommandOptions,
fn: () => Promise<T>,
): Promise<T> {
const restore = [
setEnvValue(
"WORKSPACE_ROOT",
options.cwd ? resolve(options.cwd) : undefined,
),
setEnvValue("HOST", options.host),
setEnvValue(DASHBOARD_PORT_ENV, options.port),
setEnvValue("PUBLIC_URL", options.publicUrl),
setEnvValue("ROOM_SECRET", options.roomSecret),
setEnvValue(WEBVIEW_DIST_ENV, resolveDefaultWebviewDistDir()),
];
try {
return await fn();
} finally {
for (let i = restore.length - 1; i >= 0; i--) {
restore[i]?.();
}
}
}
function resolveDefaultWebviewDistDir(): string | undefined {
if (process.env[WEBVIEW_DIST_ENV]?.trim()) {
return undefined;
}
const moduleDir = dirname(fileURLToPath(import.meta.url));
const candidates = [
...resolveInstalledPlatformPackageWebviewCandidates(),
// Source checkout: apps/cli/src/commands/dashboard.ts
join(moduleDir, "../../../cline-hub/dist/webview"),
// Node bundle: apps/cli/dist/index.js
join(moduleDir, "cline-hub/webview"),
// Compiled platform package: apps/cli/dist/<platform>/bin/cline
join(dirname(process.execPath), "../cline-hub/webview"),
];
return candidates.find((candidate) => existsSync(candidate));
}
function resolveInstalledPlatformPackageWebviewCandidates(): string[] {
const packageName = resolvePlatformPackageName();
const starts = [
process.env.CLINE_WRAPPER_PATH
? dirname(process.env.CLINE_WRAPPER_PATH)
: undefined,
dirname(process.execPath),
].filter((value): value is string => !!value?.trim());
const candidates: string[] = [];
for (const start of starts) {
let current = start;
for (;;) {
candidates.push(
join(current, "node_modules", packageName, "cline-hub/webview"),
);
const parent = dirname(current);
if (parent === current) break;
current = parent;
}
}
return candidates;
}
function resolvePlatformPackageName(): string {
const platformName = platform() === "win32" ? "windows" : platform();
return `@cline/cli-${platformName}-${arch()}`;
}
async function startDefaultDashboardServer(): Promise<DashboardServerHandle> {
const { startClineHubDashboardServer } = await import("@cline/cline-hub");
return await startClineHubDashboardServer();
}
async function openDefaultUrl(url: string): Promise<void> {
await open(url, { wait: false });
}
export function waitForProcessShutdown(
server: DashboardServerHandle,
): Promise<void> {
return new Promise<void>((resolveShutdown, rejectShutdown) => {
let settled = false;
const cleanup = () => {
process.off("SIGINT", handleSignal);
process.off("SIGTERM", handleSignal);
};
const stop = async () => {
if (settled) return;
settled = true;
cleanup();
try {
await server.stop();
resolveShutdown();
} catch (error) {
rejectShutdown(error);
}
};
function handleSignal() {
void stop();
}
process.on("SIGINT", handleSignal);
process.on("SIGTERM", handleSignal);
});
}
export async function runDashboardCommand(
options: RunDashboardCommandOptions,
): Promise<number> {
try {
const server = await withDashboardEnvironment(options, () =>
(options.startServer ?? startDefaultDashboardServer)(),
);
const dashboardUrl =
server.inviteUrl || server.publicUrl || server.listenUrl;
options.io.writeln(
`${c.green}Cline dashboard listening at${c.reset} ${dashboardUrl}`,
);
if (server.hubUrl) {
options.io.writeln(`${c.dim}Hub endpoint: ${server.hubUrl}${c.reset}`);
}
if (options.openBrowser !== false) {
try {
await (options.openUrl ?? openDefaultUrl)(dashboardUrl);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
options.io.writeErr(`Failed to open browser: ${message}`);
}
}
await (options.waitForShutdown ?? waitForProcessShutdown)(server);
return 0;
} catch (error) {
options.io.writeErr(error instanceof Error ? error.message : String(error));
return 1;
}
}
@@ -115,8 +115,8 @@ describe("runDoctorCommand", () => {
return {
status: 0,
stdout: [
"50174 /Users/example/.bun/bin/bun /Users/example/dev/sdk/apps/cli/src/index.ts hub start --cwd /workspace",
"50190 /Users/example/.bun/bin/bun /Users/example/dev/sdk/apps/cli/src/index.ts hey",
"50174 /Users/example/.bun/bin/bun /Users/example/dev/apps/cli/src/index.ts hub start --cwd /workspace",
"50190 /Users/example/.bun/bin/bun /Users/example/dev/apps/cli/src/index.ts hey",
].join("\n"),
};
}
@@ -266,7 +266,7 @@ describe("runDoctorCommand", () => {
return {
status: 0,
stdout:
"60123 /Users/example/dev/sdk/apps/examples/desktop-app/src-tauri/bin/code-sidecar\n",
"60123 /Users/example/dev/apps/examples/desktop-app/src-tauri/bin/code-sidecar\n",
};
}
return { status: 1, stdout: "" };
@@ -1,5 +1,5 @@
import { spawnSync } from "node:child_process";
import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
import { existsSync, readFileSync, rmSync } from "node:fs";
import { dirname, join } from "node:path";
import {
clearHubDiscovery,
@@ -14,6 +14,10 @@ import { formatUptime } from "@cline/shared";
import { Command } from "commander";
import open from "open";
import { isProcessRunning } from "../connectors/common";
import {
type ActiveConnectorRecord,
listActiveConnectors,
} from "../connectors/status";
import { getCliBuildInfo } from "../utils/common";
import { c, writeln } from "../utils/output";
import { stopAllConnectors } from "./connect";
@@ -34,19 +38,6 @@ type StartupArtifact = {
stale: boolean;
};
type ActiveConnectorRecord = {
type: string;
pid: number;
hubUrl: string;
startedAt?: string;
applicationId?: string;
botUsername?: string;
userName?: string;
phoneNumberId?: string;
port?: number;
baseUrl?: string;
};
type SpawnedProcessRecord = {
timestamp?: string;
pid?: number;
@@ -287,142 +278,6 @@ async function clearHubStartupArtifacts(
};
}
function listConnectorStatePaths(
type: ActiveConnectorRecord["type"],
): string[] {
const dir = join(resolveClineDataDir(), "connectors", type);
if (!existsSync(dir)) {
return [];
}
return readdirSync(dir)
.filter((name) => name.endsWith(".json") && !name.endsWith(".threads.json"))
.map((name) => join(dir, name));
}
function readJsonRecord(path: string): Record<string, unknown> | undefined {
if (!existsSync(path)) {
return undefined;
}
try {
const raw = readFileSync(path, "utf8");
const parsed = JSON.parse(raw) as unknown;
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
return parsed as Record<string, unknown>;
}
} catch {
// Ignore malformed connector state.
}
return undefined;
}
type ConnectorFieldKey = keyof Omit<
ActiveConnectorRecord,
"type" | "pid" | "hubUrl"
>;
const connectorFieldExtractors: Record<
ConnectorFieldKey,
(p: Record<string, unknown>) => string | number | undefined
> = {
startedAt: (p) => (typeof p.startedAt === "string" ? p.startedAt : undefined),
port: (p) => (typeof p.port === "number" ? p.port : undefined),
baseUrl: (p) => (typeof p.baseUrl === "string" ? p.baseUrl : undefined),
userName: (p) => (typeof p.userName === "string" ? p.userName : undefined),
botUsername: (p) =>
typeof p.botUsername === "string" ? p.botUsername : undefined,
applicationId: (p) =>
typeof p.applicationId === "string" ? p.applicationId : undefined,
phoneNumberId: (p) =>
typeof p.phoneNumberId === "string" ? p.phoneNumberId : undefined,
};
const connectorConfigs: Record<
string,
{ required: ConnectorFieldKey[]; optional: ConnectorFieldKey[] }
> = {
discord: {
required: ["userName", "applicationId"],
optional: ["startedAt", "port", "baseUrl"],
},
telegram: { required: ["botUsername"], optional: ["startedAt"] },
gchat: { required: ["userName"], optional: ["startedAt", "port", "baseUrl"] },
linear: {
required: ["userName"],
optional: ["startedAt", "port", "baseUrl"],
},
whatsapp: {
required: ["userName"],
optional: ["startedAt", "phoneNumberId", "port", "baseUrl"],
},
};
function readActiveConnectorRecord(
type: ActiveConnectorRecord["type"],
statePath: string,
): ActiveConnectorRecord | undefined {
const parsed = readJsonRecord(statePath);
if (!parsed) {
return undefined;
}
const pid = typeof parsed.pid === "number" ? parsed.pid : undefined;
const hubUrl =
typeof parsed.hubUrl === "string"
? parsed.hubUrl
: typeof parsed.rpcAddress === "string"
? parsed.rpcAddress
: undefined;
if (!pid || !hubUrl || !isProcessRunning(pid)) {
return undefined;
}
const config = connectorConfigs[type];
if (!config) {
return undefined;
}
const fields: Partial<
Omit<ActiveConnectorRecord, "type" | "pid" | "hubUrl">
> = {};
for (const key of config.required) {
const value = connectorFieldExtractors[key](parsed);
if (!value || (typeof value === "string" && !value.trim())) {
return undefined;
}
(fields as Record<string, unknown>)[key] = value;
}
for (const key of config.optional) {
const value = connectorFieldExtractors[key](parsed);
if (value !== undefined) {
(fields as Record<string, unknown>)[key] = value;
}
}
return { type, pid, hubUrl, ...fields } as ActiveConnectorRecord;
}
function listActiveConnectors(): ActiveConnectorRecord[] {
const connectorTypes: ActiveConnectorRecord["type"][] = [
"telegram",
"gchat",
"linear",
"whatsapp",
];
const records: ActiveConnectorRecord[] = [];
for (const type of connectorTypes) {
for (const statePath of listConnectorStatePaths(type)) {
const record = readActiveConnectorRecord(type, statePath);
if (record) {
records.push(record);
}
}
}
return records.sort((left, right) => {
if (left.type !== right.type) {
return left.type.localeCompare(right.type);
}
const leftName = left.botUsername ?? left.userName ?? "";
const rightName = right.botUsername ?? right.userName ?? "";
return leftName.localeCompare(rightName);
});
}
function formatHubUptimeFromStartedAt(
startedAt: string | undefined,
): string | undefined {

Some files were not shown because too many files have changed in this diff Show More